From 4c30f8bd6de5487603ed645cc4dd2a2983f4f0ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20C=C5=93ur?= Date: Wed, 10 Apr 2019 20:15:11 +0800 Subject: [PATCH 001/745] appendingPathComponent(:isDirectory:) should account for isDirectory --- stdlib/public/Darwin/Foundation/URL.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/public/Darwin/Foundation/URL.swift b/stdlib/public/Darwin/Foundation/URL.swift index 55ffeb8f9402b..ab9e68bc49c01 100644 --- a/stdlib/public/Darwin/Foundation/URL.swift +++ b/stdlib/public/Darwin/Foundation/URL.swift @@ -827,7 +827,8 @@ public struct URL : ReferenceConvertible, Equatable { } else { // Now we need to do something more expensive if var c = URLComponents(url: self, resolvingAgainstBaseURL: true) { - c.path = (c.path as NSString).appendingPathComponent(pathComponent) + let path = (c.path as NSString).appendingPathComponent(pathComponent) + c.path = isDirectory ? path + "/" : path if let result = c.url { return result From 6b6b640e133ca547139c6586eb0dcbf7933b46ec Mon Sep 17 00:00:00 2001 From: Owen Voorhees Date: Sun, 6 Sep 2020 15:33:13 -0700 Subject: [PATCH 002/745] [Driver] Don't test for null source locations in driver diagnostics swift-driver doesn't emit these, and there's no reason to check that they're present when using the integrated driver. --- test/Frontend/unknown-arguments.swift | 12 ++++++------ test/Misc/serialized-diagnostics-batch-mode.swift | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/Frontend/unknown-arguments.swift b/test/Frontend/unknown-arguments.swift index c857177de76d1..a9007a6e540fb 100644 --- a/test/Frontend/unknown-arguments.swift +++ b/test/Frontend/unknown-arguments.swift @@ -1,14 +1,14 @@ // RUN: not %swift -fake-argument -abcdef -c %s -o %t.o 2>&1 | %FileCheck %s -// CHECK: :0: error: unknown argument: '-fake-argument' -// CHECK-NEXT: :0: error: unknown argument: '-abcdef' +// CHECK: error: unknown argument: '-fake-argument' +// CHECK-NEXT: error: unknown argument: '-abcdef' // RUN: not %swiftc_driver -c %s -o %t.o -Xfrontend -fake-frontend-arg -Xfrontend fakevalue 2>&1 | %FileCheck -check-prefix=XFRONTEND %s -// XFRONTEND: :0: error: unknown argument: '-fake-frontend-arg' +// XFRONTEND: error: unknown argument: '-fake-frontend-arg' // RUN: not %swiftc_driver -D Correct -DAlsoCorrect -D@#%! -D Swift=Cool -D-D -c %s -o %t.o 2>&1 | %FileCheck -check-prefix=INVALID-COND %s -// INVALID-COND: :0: error: conditional compilation flags must be valid Swift identifiers (rather than '@#%!') -// INVALID-COND-NEXT: :0: warning: conditional compilation flags do not have values in Swift; they are either present or absent (rather than 'Swift=Cool') -// INVALID-COND-NEXT: :0: error: invalid argument '-D-D'; did you provide a redundant '-D' in your build settings? +// INVALID-COND: error: conditional compilation flags must be valid Swift identifiers (rather than '@#%!') +// INVALID-COND-NEXT: warning: conditional compilation flags do not have values in Swift; they are either present or absent (rather than 'Swift=Cool') +// INVALID-COND-NEXT: error: invalid argument '-D-D'; did you provide a redundant '-D' in your build settings? diff --git a/test/Misc/serialized-diagnostics-batch-mode.swift b/test/Misc/serialized-diagnostics-batch-mode.swift index 20c904546c5cb..1a6e82cca940a 100644 --- a/test/Misc/serialized-diagnostics-batch-mode.swift +++ b/test/Misc/serialized-diagnostics-batch-mode.swift @@ -2,7 +2,7 @@ // The `-serialize-diagnostics-path` flag is not allowed for batch mode invoked by swiftc // RUN: not %target-swiftc_driver -serialize-diagnostics-path %t.notexpected.dia %s %S/Inputs/serialized-diagnostics-batch-mode-helper.swift -c -o %t.o 2>&1 | %FileCheck %s -// CHECK: :0: error: option '-serialize-diagnostics-path' is not supported by 'swiftc'; did you mean to use 'swift'? +// CHECK: error: option '-serialize-diagnostics-path' is not supported by 'swiftc'; did you mean to use 'swift'? // RUN: not ls %t.notexpected.dia > /dev/null // RUN: not ls %t.o > /dev/null From 26cda274ca78b1df246521ba1eeea33285b57d5f Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Fri, 11 Sep 2020 09:56:58 -0400 Subject: [PATCH 003/745] [stdlib] Simplify 'BinaryFloatingPoint.init?(exactly: T)' --- stdlib/public/core/FloatingPoint.swift | 27 +++++++++++++++-- test/stdlib/FloatingPoint.swift.gyb | 41 ++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/stdlib/public/core/FloatingPoint.swift b/stdlib/public/core/FloatingPoint.swift index 80b3249a70f55..ba8b4877844a7 100644 --- a/stdlib/public/core/FloatingPoint.swift +++ b/stdlib/public/core/FloatingPoint.swift @@ -1948,9 +1948,30 @@ extension BinaryFloatingPoint { /// - Parameter value: A floating-point value to be converted. @inlinable public init?(exactly value: Source) { - let (value_, exact) = Self._convert(from: value) - guard exact else { return nil } - self = value_ + // We define exactness by equality after roundtripping; since NaN is never + // equal to itself, it can never be converted exactly. + if value.isNaN { return nil } + + if (Source.exponentBitCount > Self.exponentBitCount + || Source.significandBitCount > Self.significandBitCount) + && value.isFinite && !value.isZero { + let exponent = value.exponent + if exponent < Self.leastNormalMagnitude.exponent { + if exponent < Self.leastNonzeroMagnitude.exponent { return nil } + if value.significandWidth > + Int(Self.Exponent(exponent) - Self.leastNonzeroMagnitude.exponent) { + return nil + } + } else { + if exponent > Self.greatestFiniteMagnitude.exponent { return nil } + if value.significandWidth > + Self.greatestFiniteMagnitude.significandWidth { + return nil + } + } + } + + self = Self(value) } @inlinable diff --git a/test/stdlib/FloatingPoint.swift.gyb b/test/stdlib/FloatingPoint.swift.gyb index 04a9e623a65dc..f114d3b6a21be 100644 --- a/test/stdlib/FloatingPoint.swift.gyb +++ b/test/stdlib/FloatingPoint.swift.gyb @@ -116,6 +116,47 @@ FloatingPoint.test("BinaryFloatingPoint/genericIntegerConversion") { } FloatingPoint.test("BinaryFloatingPoint/genericFloatingPointConversion") { + func convert< + T: BinaryFloatingPoint, U: BinaryFloatingPoint + >(exactly value: T, to: U.Type) -> U { U(exactly: value) } + + expectEqual(convert(exactly: 0 as Float, to: Double.self), 0.0) + expectEqual(convert(exactly: -0.0 as Float, to: Double.self), -0.0) + expectEqual( + convert(exactly: -0.0 as Float, to: Double.self).sign, + FloatingPointSign.minus) + expectEqual(convert(exactly: 0 as Double, to: Float.self), 0.0 as Float) + expectEqual(convert(exactly: -0.0 as Double, to: Float.self), -0.0 as Float) + expectEqual( + convert(exactly: -0.0 as Double, to: Float.self).sign, + FloatingPointSign.minus) + expectEqual(convert(exactly: 1 as Float, to: Double.self), 1.0) + expectEqual(convert(exactly: -1 as Float, to: Double.self), -1.0) + expectEqual(convert(exactly: 1 as Double, to: Float.self), 1.0 as Float) + expectEqual(convert(exactly: -1 as Double, to: Float.self), -1.0 as Float) + expectEqual( + convert(exactly: Float.infinity, to: Double.self), Double.infinity) + expectEqual( + convert(exactly: -Float.infinity, to: Double.self), -Double.infinity) + expectEqual( + convert(exactly: Double.infinity, to: Float.self), Float.infinity) + expectEqual( + convert(exactly: -Double.infinity, to: Float.self), -Float.infinity) + expectEqual(convert(exactly: Float.nan, to: Double.self), nil) + expectEqual(convert(exactly: Double.nan, to: Float.self), nil) + expectEqual(convert(exactly: Float.nan, to: Float.self), nil) + expectEqual(convert(exactly: Double.nan, to: Double.self), nil) + expectEqual( + convert(exactly: Double.leastNonzeroMagnitude, to: Float.self), nil) + expectEqual( + convert(exactly: Float.leastNonzeroMagnitude, to: Double.self), + Double(Float.leastNonzeroMagnitude)) + expectEqual( + convert(exactly: Double.greatestFiniteMagnitude, to: Float.self), nil) + expectEqual( + convert(exactly: Float.greatestFiniteMagnitude, to: Double.self), + Double(Float.greatestFiniteMagnitude)) + expectTrue(Double._convert(from: 0 as Float) == (value: 0, exact: true)) expectTrue(Double._convert(from: -0.0 as Float) == (value: -0.0, exact: true)) expectTrue(Double._convert(from: 1 as Float) == (value: 1, exact: true)) From 023066c1966c63ab3b7a4baee31ac24d29b7de36 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 14 Sep 2020 12:53:28 -0700 Subject: [PATCH 004/745] SILGen: Relax assertion that incorrectly tripped on lowered opaque capture types. When lowering the SILConstantInfo for a closure implementation type with captures, we are more conservative about substituting opaque types in the capture, since the underlying types may only be available in the local context. This means the SILConstantInfo type may not in fact be exactly what you get from `SILFunction::getFunctionTypeInContext` referencing the implementation function from its originating context, since those opaque types will be substituted in the local context. Fixes SR-13480 | rdar://problem/68159831. --- lib/SILGen/SILGenThunk.cpp | 10 ++++++++-- .../serialized-capture-opaque-type-subst.swift | 12 ++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 test/SILGen/serialized-capture-opaque-type-subst.swift diff --git a/lib/SILGen/SILGenThunk.cpp b/lib/SILGen/SILGenThunk.cpp index e2c65ef1ee7be..d51ec1dfad687 100644 --- a/lib/SILGen/SILGenThunk.cpp +++ b/lib/SILGen/SILGenThunk.cpp @@ -124,8 +124,14 @@ SILGenFunction::emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant, } auto f = SGM.getFunction(constant, NotForDefinition); - assert(f->getLoweredFunctionTypeInContext(B.getTypeExpansionContext()) == - constantInfo.SILFnType); +#ifndef NDEBUG + auto constantFnTypeInContext = + SGM.Types.getLoweredType(constantInfo.SILFnType, + B.getTypeExpansionContext()) + .castTo(); + assert(f->getLoweredFunctionTypeInContext(B.getTypeExpansionContext()) + == constantFnTypeInContext); +#endif if (callPreviousDynamicReplaceableImpl) return B.createPreviousDynamicFunctionRef(loc, f); else diff --git a/test/SILGen/serialized-capture-opaque-type-subst.swift b/test/SILGen/serialized-capture-opaque-type-subst.swift new file mode 100644 index 0000000000000..e355155ff46fb --- /dev/null +++ b/test/SILGen/serialized-capture-opaque-type-subst.swift @@ -0,0 +1,12 @@ +// RUN: %target-swift-frontend -disable-availability-checking -emit-silgen -verify %s + +public func foo() -> some Any { return 1 } + +public struct XY { public init(x: X, y: Y) { fatalError() } } + +@inlinable +public func bar() -> () -> Any { + let xy = XY(x: 1, y: foo()) + + return { xy } +} From 103a8218380c9d72160f9ca3aa7d1e0ba4d8f1bc Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Sun, 20 Sep 2020 21:59:15 +0300 Subject: [PATCH 005/745] Sema: Allow non-final classes to satisfy properties and subscripts with covariant Self --- include/swift/AST/Decl.h | 6 ++- include/swift/AST/DiagnosticsSema.def | 15 +++--- lib/AST/Decl.cpp | 11 +++-- lib/Sema/TypeCheckProtocol.cpp | 48 +++++++++--------- test/decl/protocol/conforms/inherited.swift | 10 ++-- test/decl/protocol/conforms/self.swift | 16 +++++- test/decl/protocol/req/dynamic_self.swift | 54 ++++++++++++++++----- 7 files changed, 101 insertions(+), 59 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index f822dcbefbb81..f7e5093168de5 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -4123,12 +4123,14 @@ struct SelfReferenceKind { return SelfReferenceKind(false, false, false, false); } - /// The type refers to 'Self', but only as the result type of a method. + /// The type refers to 'Self', but only as the type of a property or + /// the result type of a method/subscript. static SelfReferenceKind Result() { return SelfReferenceKind(true, false, false, false); } - /// The type refers to 'Self', but only as the parameter type of a method. + /// The type refers to 'Self', but only as the parameter type + /// of a method/subscript. static SelfReferenceKind Parameter() { return SelfReferenceKind(false, true, false, false); } diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index d9a3489de1ab9..a8c0c8e705ddc 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1992,14 +1992,15 @@ NOTE(witness_self_weaken_same_type,none, "consider weakening the same-type requirement %0 == %1 to a superclass " "requirement", (Type, Type)) ERROR(witness_requires_dynamic_self,none, - "method %0 in non-final class %1 must return 'Self' to conform to " - "protocol %2", - (DeclName, Type, Type)) + "%select{%error|method|property|subscript}0 %1 in non-final class %2 " + "must %select{%error|return|specify type|return}0 'Self' " + "to conform to protocol %3", + (RequirementKind, DeclName, Type, Type)) ERROR(witness_requires_class_implementation,none, - "method %0 in non-final class %1 cannot be implemented in a " - "protocol extension because it returns 'Self' and has associated type " - "requirements", - (DeclName, Type)) + "%select{%error|method|%error|subscript}0 %1 in non-final class %2 " + "cannot be implemented in a protocol extension because it returns 'Self' " + "and has associated type requirements", + (RequirementKind, DeclName, Type)) ERROR(witness_not_accessible_proto,none, "%select{initializer %1|method %1|%select{|setter for }2property %1" "|subscript%select{| setter}2}0 must be declared " diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 2dbf607223c1a..5331c6b2af90d 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4965,12 +4965,13 @@ ProtocolDecl::findProtocolSelfReferences(const ValueDecl *value, return ::findProtocolSelfReferences(this, type, skipAssocTypes); } else { - if (::findProtocolSelfReferences(this, type, - skipAssocTypes)) { - return SelfReferenceKind::Other(); - } - return SelfReferenceKind::None(); + assert(isa(value)); + + return ::findProtocolSelfReferences(this, type, + skipAssocTypes); } + + return SelfReferenceKind::None(); } bool ProtocolDecl::isAvailableInExistential(const ValueDecl *decl) const { diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index e0086b749bd2b..924acab7f773c 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -2078,7 +2078,7 @@ static Type getRequirementTypeForDisplay(ModuleDecl *module, return FunctionType::get(params, result, fnTy->getExtInfo()); } - return substType(type, /*result*/false); + return substType(type, /*result*/ true); } diag::RequirementKind @@ -3343,41 +3343,36 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement, emitDeclaredHereIfNeeded(diags, diagLoc, witness); }); } else if (selfKind.result) { - // The reference to Self occurs in the result type. A non-final class - // can satisfy this requirement with a method that returns Self. + // The reference to Self occurs in the result type of a method/subscript + // or the type of a property. A non-final class can satisfy this requirement + // by holding onto Self accordingly. + if (witness->getDeclContext()->getSelfClassDecl()) { + const bool hasDynamicSelfResult = [&] { + if (auto func = dyn_cast(witness)) { + return func->hasDynamicSelfResult(); + } else if (auto var = dyn_cast(witness)) { + return var->getInterfaceType()->hasDynamicSelfType(); + } - // If the function has a dynamic Self, it's okay. - if (auto func = dyn_cast(witness)) { - if (func->getDeclContext()->getSelfClassDecl() && - !func->hasDynamicSelfResult()) { + return cast(witness) + ->getElementInterfaceType() + ->hasDynamicSelfType(); + }(); + + if (!hasDynamicSelfResult) { diagnoseOrDefer(requirement, false, [witness, requirement](NormalProtocolConformance *conformance) { auto proto = conformance->getProtocol(); auto &diags = proto->getASTContext().Diags; SourceLoc diagLoc = getLocForDiagnosingWitness(conformance,witness); - diags.diagnose(diagLoc, - diag::witness_requires_dynamic_self, + diags.diagnose(diagLoc, diag::witness_requires_dynamic_self, + getProtocolRequirementKind(requirement), requirement->getName(), conformance->getType(), proto->getDeclaredInterfaceType()); emitDeclaredHereIfNeeded(diags, diagLoc, witness); }); } - - // Constructors conceptually also have a dynamic Self - // return type, so they're okay. - } else if (!isa(witness)) { - diagnoseOrDefer(requirement, false, - [witness, requirement](NormalProtocolConformance *conformance) { - auto proto = conformance->getProtocol(); - auto &diags = proto->getASTContext().Diags; - SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness); - diags.diagnose(diagLoc, diag::witness_self_non_subtype, - proto->getDeclaredInterfaceType(), - requirement->getName(), - conformance->getType()); - emitDeclaredHereIfNeeded(diags, diagLoc, witness); - }); } } else if (selfKind.requirement) { if (auto targetPair = getAdopteeSelfSameTypeConstraint(classDecl, @@ -3413,8 +3408,8 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement, // constraint that either the requirement not produce 'Self' in a // covariant position, or the type of the requirement does not involve // associated types. - if (auto func = dyn_cast(witness)) { - if (func->getDeclContext()->getExtendedProtocolDecl()) { + if (isa(witness) || isa(witness)) { + if (witness->getDeclContext()->getExtendedProtocolDecl()) { auto selfKindWithAssocTypes = Proto->findProtocolSelfReferences( requirement, /*allowCovariantParameters=*/false, @@ -3427,6 +3422,7 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement, auto &diags = proto->getASTContext().Diags; diags.diagnose(conformance->getLoc(), diag::witness_requires_class_implementation, + getProtocolRequirementKind(requirement), requirement->getName(), conformance->getType()); diags.diagnose(witness, diag::decl_declared_here, diff --git a/test/decl/protocol/conforms/inherited.swift b/test/decl/protocol/conforms/inherited.swift index 09461414696e3..08510fdc0bcff 100644 --- a/test/decl/protocol/conforms/inherited.swift +++ b/test/decl/protocol/conforms/inherited.swift @@ -5,7 +5,7 @@ protocol P1 { func f1(_ x: Self?) -> Bool } -// Never inheritable: property with 'Self' in its signature. +// Inheritable: property with 'Self' in its signature. protocol P2 { var prop2: Self { get set } } @@ -13,7 +13,7 @@ protocol P2a { var prop2a: Self { get set } } -// Never inheritable: subscript with 'Self' in its result type. +// Inheritable: subscript with 'Self' in its result type. protocol P3 { subscript (i: Int) -> Self { get } } @@ -94,7 +94,7 @@ class A : P1, P2, P3, P4, P5, P6, P7, P8, P9, P10 { func f1(_ x: A?) -> Bool { return true } // P2 - var prop2: A { // expected-error{{protocol 'P2' requirement 'prop2' cannot be satisfied by a non-final class ('A') because it uses 'Self' in a non-parameter, non-result type position}} + var prop2: A { // expected-error{{property 'prop2' in non-final class 'A' must specify type 'Self' to conform to protocol 'P2'}} get { return self } set {} } @@ -106,7 +106,7 @@ class A : P1, P2, P3, P4, P5, P6, P7, P8, P9, P10 { } // P3 - subscript (i: Int) -> A { // expected-error{{protocol 'P3' requirement 'subscript(_:)' cannot be satisfied by a non-final class ('A') because it uses 'Self' in a non-parameter, non-result type position}} + subscript (i: Int) -> A { // expected-error{{subscript 'subscript(_:)' in non-final class 'A' must return 'Self' to conform to protocol 'P3'}} get { return self } @@ -145,7 +145,7 @@ class A : P1, P2, P3, P4, P5, P6, P7, P8, P9, P10 { } extension A: P2a, P5a, P10a {} -// expected-error@-1 {{protocol 'P2a' requirement 'prop2a' cannot be satisfied by a non-final class ('A') because it uses 'Self' in a non-parameter, non-result type position}} +// expected-error@-1 {{property 'prop2a' in non-final class 'A' must specify type 'Self' to conform to protocol 'P2a'}} // expected-error@-2 {{method 'f5a()' in non-final class 'A' must return 'Self' to conform to protocol 'P5a'}} // expected-error@-3 {{protocol 'P10a' requirement 'f10a' cannot be satisfied by a non-final class ('A') because it uses 'Self' in a non-parameter, non-result type position}} diff --git a/test/decl/protocol/conforms/self.swift b/test/decl/protocol/conforms/self.swift index 78849f8e232eb..6f8603d288f8f 100644 --- a/test/decl/protocol/conforms/self.swift +++ b/test/decl/protocol/conforms/self.swift @@ -7,6 +7,8 @@ protocol P { func returnsSelf() -> Self func hasDefaultTakesT(_: T) func returnsSelfTakesT(_: T) -> Self + + subscript(_: T) -> Self { get } } extension P { @@ -21,19 +23,29 @@ extension P { func returnsSelfTakesT(_: T) -> Self { // expected-note {{'returnsSelfTakesT' declared here}} return self } + + subscript(_: T) -> Self { self } // expected-note {{'subscript(_:)' declared here}} } // This fails -class Class : P {} // expected-error {{method 'returnsSelfTakesT' in non-final class 'Class' cannot be implemented in a protocol extension because it returns 'Self' and has associated type requirements}} +class Class : P {} +// expected-error@-1 {{method 'returnsSelfTakesT' in non-final class 'Class' cannot be implemented in a protocol extension because it returns 'Self' and has associated type requirements}} +// expected-error@-2 {{subscript 'subscript(_:)' in non-final class 'Class' cannot be implemented in a protocol extension because it returns 'Self' and has associated type requirements}} // This succeeds, because the class is final final class FinalClass : P {} -// This succeeds, because we're not using the default implementation +// This succeeds, because we're not using the default implementations class NonFinalClass : P { + // FIXME: An explicit type witness is necessary to avoid an unrelated + // associated type inference bug. + typealias T = Never + func returnsSelfTakesT(_: T) -> Self { return self } + + subscript(_: T) -> Self { self } } // Test for default implementation that comes from a constrained extension diff --git a/test/decl/protocol/req/dynamic_self.swift b/test/decl/protocol/req/dynamic_self.swift index fc9c06fc0449f..758a301ef6f22 100644 --- a/test/decl/protocol/req/dynamic_self.swift +++ b/test/decl/protocol/req/dynamic_self.swift @@ -1,58 +1,88 @@ // RUN: %target-typecheck-verify-swift protocol P { + var p: Self { get } + // expected-note@-1{{protocol requires property 'p' with type 'Self'}} + // expected-note@-2{{protocol requires property 'p' with type 'EError'}} + // expected-note@-3{{protocol requires property 'p' with type 'SError'}} + subscript() -> Self { get } + // expected-note@-1{{protocol requires subscript with type '() -> Self'}} + // expected-note@-2{{protocol requires subscript with type '() -> EError'}} + // expected-note@-3{{protocol requires subscript with type '() -> SError'}} func f() -> Self // expected-note@-1{{protocol requires function 'f()' with type '() -> Self'}} // expected-note@-2{{protocol requires function 'f()' with type '() -> EError'}} // expected-note@-3{{protocol requires function 'f()' with type '() -> SError'}} } -// Error: Missing Self method in a class. +func takesP(_: P) {} // OK + +// Error: Missing witnesses. class W : P {} // expected-error{{type 'W' does not conform to protocol 'P'}} // Okay: Self method in class. class X : P { - func f() -> Self { return self } + var p: Self { self } + subscript() -> Self { self } + func f() -> Self { self } } class Y { - func f() -> Self { return self } + var p: Self { self } + subscript() -> Self { self } + func f() -> Self { self } } class GX : P { - func f() -> Self { return self } + var p: Self { self } + subscript() -> Self { self } + func f() -> Self { self } } // Okay: dynamic Self method in superclass. class Z : Y, P { } -// Erro: Z2 conforms, but subclass would not +// Error: Z2 conforms, but subclass would not. class Z2 : P { - func f() -> Z2 { return self } // expected-error{{method 'f()' in non-final class 'Z2' must return 'Self' to conform to protocol 'P'}} + var p: Z2 { self } //expected-error{{property 'p' in non-final class 'Z2' must specify type 'Self' to conform to protocol 'P'}} + subscript() -> Z2 { self } //expected-error{{subscript 'subscript()' in non-final class 'Z2' must return 'Self' to conform to protocol 'P'}} + func f() -> Z2 { self } // expected-error{{method 'f()' in non-final class 'Z2' must return 'Self' to conform to protocol 'P'}} } // Okay: struct conforms by returning itself struct S : P { - func f() -> S { return self } + var p: S { self } + subscript() -> S { self } + func f() -> S { self } } struct GS : P { - func f() -> GS { return self } + var p: GS { self } + subscript() -> GS { self } + func f() -> GS { self } } struct SError : P { // expected-error{{type 'SError' does not conform to protocol 'P'}} - func f() -> Int { return 0 } // expected-note{{candidate has non-matching type '() -> Int'}} + var p: Int { 0 } // expected-note{{candidate has non-matching type 'Int'}} + subscript() -> Int { 0 } // expected-note{{candidate has non-matching type '() -> Int'}} + func f() -> Int { 0 } // expected-note{{candidate has non-matching type '() -> Int'}} } // Okay: enum conforms by returning itself enum E : P { - func f() -> E { return self } + var p: E { self } + subscript() -> E { self } + func f() -> E { self } } enum GE : P { - func f() -> GE { return self } + var p: GE { self } + subscript() -> GE { self } + func f() -> GE { self } } enum EError : P { // expected-error{{type 'EError' does not conform to protocol 'P'}} - func f() -> Int { return 0 } // expected-note{{candidate has non-matching type '() -> Int'}} + var p: Int { 0 } // expected-note{{candidate has non-matching type 'Int'}} + subscript() -> Int { 0 } // expected-note{{candidate has non-matching type '() -> Int'}} + func f() -> Int { 0 } // expected-note{{candidate has non-matching type '() -> Int'}} } From 010c1cd91f26f7f8ec1dea1a06f26cf94972debf Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Sat, 25 Jul 2020 07:48:59 -0700 Subject: [PATCH 006/745] Convert OME into a function transform --- .../Mandatory/OwnershipModelEliminator.cpp | 75 +++++++++---------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp index 769ee6c2cef75..b48a1cc0edb16 100644 --- a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp +++ b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp @@ -370,7 +370,7 @@ static void prepareSILFunctionForOptimization(ModuleDecl *, SILFunction *F) { namespace { -struct OwnershipModelEliminator : SILModuleTransform { +struct OwnershipModelEliminator : SILFunctionTransform { bool SkipTransparent; bool SkipStdlibModule; @@ -379,10 +379,11 @@ struct OwnershipModelEliminator : SILModuleTransform { void run() override { if (DumpBefore.size()) { - getModule()->dump(DumpBefore.c_str()); + getFunction()->dump(DumpBefore.c_str()); } - auto &Mod = *getModule(); + auto *F = getFunction(); + auto &Mod = getFunction()->getModule(); // If we are supposed to skip the stdlib module and we are in the stdlib // module bail. @@ -390,42 +391,38 @@ struct OwnershipModelEliminator : SILModuleTransform { return; } - for (auto &F : Mod) { - // If F does not have ownership, skip it. We have no further work to do. - if (!F.hasOwnership()) - continue; - - // If we were asked to not strip ownership from transparent functions in - // /our/ module, continue. - if (SkipTransparent && F.isTransparent()) - continue; - - // Verify here to make sure ownership is correct before we strip. - { - // Add a pretty stack trace entry to tell users who see a verification - // failure triggered by this verification check that they need to re-run - // with -sil-verify-all to actually find the pass that introduced the - // verification error. - // - // DISCUSSION: This occurs due to the crash from the verification - // failure happening in the pass itself. This causes us to dump the - // SILFunction and emit a msg that this pass (OME) is the culprit. This - // is generally correct for most passes, but not for OME since we are - // verifying before we have even modified the function to ensure that - // all ownership invariants have been respected before we lower - // ownership from the function. - llvm::PrettyStackTraceString silVerifyAllMsgOnFailure( - "Found verification error when verifying before lowering " - "ownership. Please re-run with -sil-verify-all to identify the " - "actual pass that introduced the verification error."); - F.verify(); - } - - if (stripOwnership(F)) { - auto InvalidKind = - SILAnalysis::InvalidationKind::BranchesAndInstructions; - invalidateAnalysis(&F, InvalidKind); - } + if (!F->hasOwnership()) + return; + + // If we were asked to not strip ownership from transparent functions in + // /our/ module, return. + if (SkipTransparent && F->isTransparent()) + return; + + // Verify here to make sure ownership is correct before we strip. + { + // Add a pretty stack trace entry to tell users who see a verification + // failure triggered by this verification check that they need to re-run + // with -sil-verify-all to actually find the pass that introduced the + // verification error. + // + // DISCUSSION: This occurs due to the crash from the verification + // failure happening in the pass itself. This causes us to dump the + // SILFunction and emit a msg that this pass (OME) is the culprit. This + // is generally correct for most passes, but not for OME since we are + // verifying before we have even modified the function to ensure that + // all ownership invariants have been respected before we lower + // ownership from the function. + llvm::PrettyStackTraceString silVerifyAllMsgOnFailure( + "Found verification error when verifying before lowering " + "ownership. Please re-run with -sil-verify-all to identify the " + "actual pass that introduced the verification error."); + F->verify(); + } + + if (stripOwnership(*F)) { + auto InvalidKind = SILAnalysis::InvalidationKind::BranchesAndInstructions; + invalidateAnalysis(InvalidKind); } // If we were asked to strip transparent, we are at the beginning of the From 81a2157a7253197319bd429c4749ca5b2ca5ecd8 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Wed, 29 Jul 2020 13:23:55 -0700 Subject: [PATCH 007/745] Handle conversion of unchecked_value_cast to non-ossa in SILCloner --- include/swift/SIL/SILCloner.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index c27d345ee8ae2..56d5d19903a30 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1542,6 +1542,13 @@ template void SILCloner::visitUncheckedValueCastInst( UncheckedValueCastInst *Inst) { getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + if (!getBuilder().hasOwnership()) { + recordClonedInstruction(Inst, getBuilder().createUncheckedBitwiseCast( + getOpLocation(Inst->getLoc()), + getOpValue(Inst->getOperand()), + getOpType(Inst->getType()))); + return; + } recordClonedInstruction(Inst, getBuilder().createUncheckedValueCast( getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()), From 9c9a8ef2241d2be4a9d47bd2a4bbdd8b2da344b3 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Wed, 29 Jul 2020 22:31:53 -0700 Subject: [PATCH 008/745] Allow OME to run mandatorily --- .../SILOptimizer/PassManager/PassManager.h | 5 +++++ lib/SILOptimizer/PassManager/PassManager.cpp | 20 ++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/include/swift/SILOptimizer/PassManager/PassManager.h b/include/swift/SILOptimizer/PassManager/PassManager.h index b72969c96a01b..8c82ff0169dc0 100644 --- a/include/swift/SILOptimizer/PassManager/PassManager.h +++ b/include/swift/SILOptimizer/PassManager/PassManager.h @@ -281,6 +281,11 @@ class SILPassManager { /// Run the passes in Transform from \p FromTransIdx to \p ToTransIdx. void runFunctionPasses(unsigned FromTransIdx, unsigned ToTransIdx); + /// Helper function to check if the function pass should be run mandatorily + /// All passes in mandatory pass pipeline and ownership model elimination are + /// mandatory function passes. + bool isMandatoryFunctionPass(SILFunctionTransform *); + /// A helper function that returns (based on SIL stage and debug /// options) whether we should continue running passes. bool continueTransforming(); diff --git a/lib/SILOptimizer/PassManager/PassManager.cpp b/lib/SILOptimizer/PassManager/PassManager.cpp index e6bee2886a953..f315eec53f65d 100644 --- a/lib/SILOptimizer/PassManager/PassManager.cpp +++ b/lib/SILOptimizer/PassManager/PassManager.cpp @@ -383,11 +383,24 @@ void SILPassManager::dumpPassInfo(const char *Title, unsigned TransIdx, llvm::dbgs() << '\n'; } +bool SILPassManager::isMandatoryFunctionPass(SILFunctionTransform *sft) { + return isMandatory || sft->getPassKind() == + PassKind::NonTransparentFunctionOwnershipModelEliminator || + sft->getPassKind() == PassKind::OwnershipModelEliminator || + sft->getPassKind() == + PassKind::NonStdlibNonTransparentFunctionOwnershipModelEliminator; +} + void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { assert(analysesUnlocked() && "Expected all analyses to be unlocked!"); auto *SFT = cast(Transformations[TransIdx]); + + if (!F->shouldOptimize() && !isMandatoryFunctionPass(SFT)) { + return; + } + SFT->injectPassManager(this); SFT->injectFunction(F); @@ -395,9 +408,10 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { DebugPrintEnabler DebugPrint(NumPassesRun); // If nothing changed since the last run of this pass, we can skip this - // pass. + // pass if it is not mandatory CompletedPasses &completedPasses = CompletedPassesMap[F]; - if (completedPasses.test((size_t)SFT->getPassKind()) && + if (!isMandatoryFunctionPass(SFT) && + completedPasses.test((size_t)SFT->getPassKind()) && !SILDisableSkippingPasses) { if (SILPrintPassName) dumpPassInfo("(Skip)", TransIdx, F); @@ -513,7 +527,7 @@ runFunctionPasses(unsigned FromTransIdx, unsigned ToTransIdx) { // Only include functions that are definitions, and which have not // been intentionally excluded from optimization. - if (F.isDefinition() && (isMandatory || F.shouldOptimize())) + if (F.isDefinition()) FunctionWorklist.push_back(*I); } From 33401ae147a04ce628cf246504b67729063358e5 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Wed, 23 Sep 2020 21:16:12 +0300 Subject: [PATCH 009/745] CS: Use the curried opened type when building a VarDecl member ref This ensures that we erase opened archetypes before closing an existential result --- lib/Sema/CSApply.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 183641f752b7d..12edcc2f61db3 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -1061,7 +1061,6 @@ namespace { AccessSemantics semantics) { auto choice = overload.choice; auto openedType = overload.openedType; - auto openedFullType = overload.openedFullType; ValueDecl *member = choice.getDecl(); @@ -1097,13 +1096,15 @@ namespace { return result; } + auto refTy = simplifyType(overload.openedFullType); + // If we're referring to the member of a module, it's just a simple // reference. if (baseTy->is()) { assert(semantics == AccessSemantics::Ordinary && "Direct property access doesn't make sense for this"); auto ref = new (context) DeclRefExpr(memberRef, memberLoc, Implicit); - cs.setType(ref, simplifyType(openedFullType)); + cs.setType(ref, refTy); ref->setFunctionRefKind(choice.getFunctionRefKind()); auto *DSBI = cs.cacheType(new (context) DotSyntaxBaseIgnoredExpr( base, dotLoc, ref, cs.getType(ref))); @@ -1114,8 +1115,6 @@ namespace { (!baseIsInstance && member->isInstanceMember()); bool isPartialApplication = shouldBuildCurryThunk(choice, baseIsInstance); - auto refTy = simplifyType(openedFullType); - // The formal type of the 'self' value for the member's declaration. Type containerTy = getBaseType(refTy->castTo()); @@ -1278,8 +1277,8 @@ namespace { = new (context) MemberRefExpr(base, dotLoc, memberRef, memberLoc, Implicit, semantics); memberRefExpr->setIsSuper(isSuper); + cs.setType(memberRefExpr, refTy->castTo()->getResult()); - cs.setType(memberRefExpr, simplifyType(openedType)); Expr *result = memberRefExpr; closeExistential(result, locator); From 9f58968fa394444115a3bef9e72a4b7a146fa0d0 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Wed, 23 Sep 2020 23:15:51 +0300 Subject: [PATCH 010/745] Add silgen and run-time tests --- test/Interpreter/dynamic_self.swift | 171 +++++++++++++++++++++------- test/SILGen/dynamic_self.swift | 60 +++++++++- 2 files changed, 190 insertions(+), 41 deletions(-) diff --git a/test/Interpreter/dynamic_self.swift b/test/Interpreter/dynamic_self.swift index fc6820602611c..9ee543de0f6d3 100644 --- a/test/Interpreter/dynamic_self.swift +++ b/test/Interpreter/dynamic_self.swift @@ -6,27 +6,71 @@ protocol P { func f() -> Self func g() -> Self + subscript() -> Self { get } + var p: Self { get } } protocol CP : class { func f() -> Self func g() -> Self + subscript() -> Self { get } + var p: Self { get } +} + +extension P { + func f() -> Self { + print("[Self := \(Self.self), self is \(type(of: self))] P extension.f()") + return self + } + func g() -> Self { + print("[Self := \(Self.self), self is \(type(of: self))] P extension.g()") + return self + } + subscript() -> Self { + print("[Self := \(Self.self), self is \(type(of: self))] P extension.subscript()") + return self + } + var p: Self { + print("[Self := \(Self.self), self is \(type(of: self))] P extension.p") + return self + } +} + +extension CP { + func f() -> Self { + print("[Self := \(Self.self), self is \(type(of: self))] CP extension.f()") + return self + } + func g() -> Self { + print("[Self := \(Self.self), self is \(type(of: self))] CP extension.g()") + return self + } + subscript() -> Self { + print("[Self := \(Self.self), self is \(type(of: self))] CP extension.subscript()") + return self + } + var p: Self { + print("[Self := \(Self.self), self is \(type(of: self))] CP extension.p") + return self + } } func callDynamicSelfExistential(_ p: P) { - print("Before first call") - var p2 = p.f() - print("Between calls") - p2.g() - print("After second call") + print("callDynamicSelfExistential {") + let p2 = p.f() + let p3 = p2.g() + let p4 = p3[] + let p5 = p4.p + print(" } callDynamicSelfExistential") } func callDynamicSelfClassExistential(_ cp: CP) { - print("Before first call") - var cp2 = cp.f() - print("Between calls") - cp2.g() - print("After second call") + print("callDynamicSelfClassExistential {") + let cp2 = cp.f() + let cp3 = cp2.g() + let cp4 = cp3[] + let cp5 = cp4.p + print(" } callDynamicSelfClassExistential") } struct S : P { @@ -39,54 +83,101 @@ struct S : P { print("S.g()") return self } -} -class C : P, CP { - init() { - print("Allocating C") + subscript() -> S { + print("S.subscript()") + return self + } + + var p: S { + print("S.p") + return self } +} +class C1a : P, CP { func f() -> Self { - print("C.f()") + print("C1a.f()") return self } func g() -> Self { - print("C.g()") + print("C1a.g()") + return self + } + + subscript() -> Self { + print("C1a.subscript()") + return self + } + + var p: Self { + print("C1a.p") return self } } +final class C1b : C1a { + override subscript() -> Self { + print("C1b.subscript()") + return self + } +} + +class C2a : P {} +final class C2b : C2a {} + +class C3a : CP {} +final class C3b : C3a {} print("-------------------------------") -// CHECK: S() as non-class existential -print("S() as non-class existential") -// CHECK-NEXT: Before first call +// CHECK: callDynamicSelfExistential { // CHECK-NEXT: S.f() -// CHECK-NEXT: Between calls // CHECK-NEXT: S.g() -// CHECK-NEXT: After second call +// CHECK-NEXT: S.subscript() +// CHECK-NEXT: S.p +// CHECK-NEXT: } callDynamicSelfExistential callDynamicSelfExistential(S()) -// CHECK-NEXT: C() as non-class existential -print("C() as non-class existential") -// CHECK-NEXT: Allocating C -// CHECK-NEXT: Before first call -// CHECK-NEXT: C.f() -// CHECK-NEXT: Between calls -// CHECK-NEXT: C.g() -// CHECK-NEXT: After second call -callDynamicSelfExistential(C()) - -// CHECK-NEXT: C() as class existential -print("C() as class existential") -// CHECK-NEXT: Allocating C -// CHECK-NEXT: Before first call -// CHECK-NEXT: C.f() -// CHECK-NEXT: Between calls -// CHECK-NEXT: C.g() -// CHECK-NEXT: After second call -callDynamicSelfClassExistential(C()) +// CHECK-NEXT: callDynamicSelfExistential { +// CHECK-NEXT: C1a.f() +// CHECK-NEXT: C1a.g() +// CHECK-NEXT: C1a.subscript() +// CHECK-NEXT: C1a.p +// CHECK-NEXT: } callDynamicSelfExistential +callDynamicSelfExistential(C1a()) + +// CHECK-NEXT: callDynamicSelfExistential { +// CHECK-NEXT: C1a.f() +// CHECK-NEXT: C1a.g() +// CHECK-NEXT: C1b.subscript() +// CHECK-NEXT: C1a.p +// CHECK-NEXT: } callDynamicSelfExistential +callDynamicSelfExistential(C1b()) + +// CHECK-NEXT: callDynamicSelfExistential { +// CHECK-NEXT: [Self := C2a, self is C2b] P extension.f() +// CHECK-NEXT: [Self := C2a, self is C2b] P extension.g() +// CHECK-NEXT: [Self := C2a, self is C2b] P extension.subscript() +// CHECK-NEXT: [Self := C2a, self is C2b] P extension.p +// CHECK-NEXT: } callDynamicSelfExistential +callDynamicSelfExistential(C2b() as C2a) + +// CHECK-NEXT: callDynamicSelfClassExistential { +// CHECK-NEXT: C1a.f() +// CHECK-NEXT: C1a.g() +// CHECK-NEXT: C1a.subscript() +// CHECK-NEXT: C1a.p +// CHECK-NEXT: } callDynamicSelfClassExistential +callDynamicSelfClassExistential(C1a()) + +// CHECK-NEXT: callDynamicSelfClassExistential { +// CHECK-NEXT: [Self := C3b, self is C3b] CP extension.f() +// CHECK-NEXT: [Self := C3b, self is C3b] CP extension.g() +// CHECK-NEXT: [Self := C3b, self is C3b] CP extension.subscript() +// CHECK-NEXT: [Self := C3b, self is C3b] CP extension.p +// CHECK-NEXT: } callDynamicSelfClassExistential +callDynamicSelfClassExistential(C3b() as C3a) print("-------------------------------") diff --git a/test/SILGen/dynamic_self.swift b/test/SILGen/dynamic_self.swift index d2d6df2d163e6..95070d3b15894 100644 --- a/test/SILGen/dynamic_self.swift +++ b/test/SILGen/dynamic_self.swift @@ -8,10 +8,14 @@ protocol P { func f() -> Self + subscript() -> Self { get } + var p: Self { get } } protocol CP : class { func f() -> Self + subscript() -> Self { get } + var p: Self { get } } class X : P, CP { @@ -20,6 +24,12 @@ class X : P, CP { // CHECK-LABEL: sil hidden [ossa] @$s12dynamic_self1XC1f{{[_0-9a-zA-Z]*}}F : $@convention(method) (@guaranteed X) -> @owned func f() -> Self { return self } + // CHECK-LABEL: sil hidden [ossa] @$s12dynamic_self1XCACXDycig : $@convention(method) (@guaranteed X) -> @owned X + subscript() -> Self { self } + + // CHECK-LABEL: sil hidden [ossa] @$s12dynamic_self1XC1pACXDvg : $@convention(method) (@guaranteed X) -> @owned X + var p: Self { self } + // CHECK-LABEL: sil hidden [ossa] @$s12dynamic_self1XC7factory{{[_0-9a-zA-Z]*}}FZ : $@convention(method) (Int, @thick X.Type) -> @owned X // CHECK: bb0([[I:%[0-9]+]] : $Int, [[SELF:%[0-9]+]] : $@thick X.Type): // CHECK: [[DYNAMIC_SELF:%[0-9]+]] = unchecked_trivial_bit_cast [[SELF]] : $@thick X.Type to $@thick @dynamic_self X.Type @@ -83,17 +93,65 @@ func testExistentialDispatch(p: P) { // CHECK: destroy_addr [[P_RESULT]] : $*P // CHECK: dealloc_stack [[P_RESULT]] : $*P _ = p.f() + +// CHECK: [[PCOPY_ADDR:%[0-9]+]] = open_existential_addr immutable_access [[P]] : $*P to $*@opened([[N:".*"]]) P +// CHECK: [[P_RESULT:%[0-9]+]] = alloc_stack $P +// CHECK: [[PCOPY_ADDR_1:%[0-9]+]] = alloc_stack $@opened([[N]]) P +// CHECK: copy_addr [[PCOPY_ADDR]] to [initialization] [[PCOPY_ADDR_1]] : $*@opened([[N]]) P +// CHECK: [[P_P_GETTER:%[0-9]+]] = witness_method $@opened([[N]]) P, #P.p!getter : {{.*}}, [[PCOPY_ADDR]]{{.*}} : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> @out τ_0_0 +// CHECK: [[P_RESULT_ADDR2:%[0-9]+]] = init_existential_addr [[P_RESULT]] : $*P, $@opened([[N]]) P +// CHECK: apply [[P_P_GETTER]]<@opened([[N]]) P>([[P_RESULT_ADDR2]], [[PCOPY_ADDR_1]]) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> @out τ_0_0 +// CHECK: destroy_addr [[PCOPY_ADDR_1]] : $*@opened([[N]]) P +// CHECK: destroy_addr [[P_RESULT]] : $*P +// CHECK: dealloc_stack [[PCOPY_ADDR_1]] : $*@opened([[N]]) P +// CHECK: dealloc_stack [[P_RESULT]] : $*P + _ = p.p + +// CHECK: [[PCOPY_ADDR:%[0-9]+]] = open_existential_addr immutable_access [[P]] : $*P to $*@opened([[N:".*"]]) P +// CHECK: [[P_RESULT:%[0-9]+]] = alloc_stack $P +// CHECK: [[PCOPY_ADDR_1:%[0-9]+]] = alloc_stack $@opened([[N]]) P +// CHECK: copy_addr [[PCOPY_ADDR]] to [initialization] [[PCOPY_ADDR_1]] : $*@opened([[N]]) P +// CHECK: [[P_SUBSCRIPT_GETTER:%[0-9]+]] = witness_method $@opened([[N]]) P, #P.subscript!getter : {{.*}}, [[PCOPY_ADDR]]{{.*}} : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> @out τ_0_0 +// CHECK: [[P_RESULT_ADDR:%[0-9]+]] = init_existential_addr [[P_RESULT]] : $*P, $@opened([[N]]) P +// CHECK: apply [[P_SUBSCRIPT_GETTER]]<@opened([[N]]) P>([[P_RESULT_ADDR]], [[PCOPY_ADDR_1]]) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> @out τ_0_0 +// CHECK: destroy_addr [[PCOPY_ADDR_1]] : $*@opened([[N]]) P +// CHECK: destroy_addr [[P_RESULT]] : $*P +// CHECK: dealloc_stack [[PCOPY_ADDR_1]] : $*@opened([[N]]) P +// CHECK: dealloc_stack [[P_RESULT]] : $*P + _ = p[] } // CHECK-LABEL: sil hidden [ossa] @$s12dynamic_self28testExistentialDispatchClass{{[_0-9a-zA-Z]*}}F : $@convention(thin) (@guaranteed CP) -> () +func testExistentialDispatchClass(cp: CP) { // CHECK: bb0([[CP:%[0-9]+]] : @guaranteed $CP): // CHECK: [[CP_ADDR:%[0-9]+]] = open_existential_ref [[CP]] : $CP to $@opened([[N:".*"]]) CP // CHECK: [[CP_F:%[0-9]+]] = witness_method $@opened([[N]]) CP, #CP.f : {{.*}}, [[CP_ADDR]]{{.*}} : $@convention(witness_method: CP) <τ_0_0 where τ_0_0 : CP> (@guaranteed τ_0_0) -> @owned τ_0_0 // CHECK: [[CP_F_RESULT:%[0-9]+]] = apply [[CP_F]]<@opened([[N]]) CP>([[CP_ADDR]]) : $@convention(witness_method: CP) <τ_0_0 where τ_0_0 : CP> (@guaranteed τ_0_0) -> @owned τ_0_0 // CHECK: [[RESULT_EXISTENTIAL:%[0-9]+]] = init_existential_ref [[CP_F_RESULT]] : $@opened([[N]]) CP : $@opened([[N]]) CP, $CP // CHECK: destroy_value [[RESULT_EXISTENTIAL]] -func testExistentialDispatchClass(cp: CP) { _ = cp.f() + +// CHECK: [[CP_ADDR:%[0-9]+]] = open_existential_ref [[CP]] : $CP to $@opened([[N:".*"]]) CP +// CHECK: [[CP_ADDR_1:%[0-9]+]] = copy_value [[CP_ADDR]] : $@opened([[N]]) CP +// CHECK: [[CP_BORROWED:%[0-9]+]] = begin_borrow [[CP_ADDR_1]] : $@opened([[N]]) CP +// CHECK: [[CP_P_GETTER:%[0-9]+]] = witness_method $@opened([[N]]) CP, #CP.p!getter : {{.*}}, [[CP_ADDR]]{{.*}} : $@convention(witness_method: CP) <τ_0_0 where τ_0_0 : CP> (@guaranteed τ_0_0) -> @owned τ_0_0 +// CHECK: [[APPLY_RESULT:%[0-9]+]] = apply [[CP_P_GETTER]]<@opened([[N]]) CP>([[CP_BORROWED]]) : $@convention(witness_method: CP) <τ_0_0 where τ_0_0 : CP> (@guaranteed τ_0_0) -> @owned τ_0_0 +// CHECK: end_borrow [[CP_BORROWED]] : $@opened([[N]]) CP +// CHECK: destroy_value [[CP_ADDR_1]] : $@opened([[N]]) CP +// CHECK: [[RESULT_EXISTENTIAL:%[0-9]+]] = init_existential_ref [[APPLY_RESULT]] : $@opened([[N]]) CP : $@opened([[N]]) CP, $CP +// CHECK: destroy_value [[RESULT_EXISTENTIAL]] : $CP + _ = cp.p + +// CHECK: [[CP_ADDR:%[0-9]+]] = open_existential_ref [[CP]] : $CP to $@opened([[N:".*"]]) CP +// CHECK: [[CP_ADDR_1:%[0-9]+]] = copy_value [[CP_ADDR]] : $@opened([[N]]) CP +// CHECK: [[CP_BORROWED:%[0-9]+]] = begin_borrow [[CP_ADDR_1]] : $@opened([[N]]) CP +// CHECK: [[CP_SUBSCRIPT_GETTER:%[0-9]+]] = witness_method $@opened([[N]]) CP, #CP.subscript!getter : {{.*}}, [[CP_ADDR]]{{.*}} : $@convention(witness_method: CP) <τ_0_0 where τ_0_0 : CP> (@guaranteed τ_0_0) -> @owned τ_0_0 +// CHECK: [[APPLY_RESULT:%[0-9]+]] = apply [[CP_SUBSCRIPT_GETTER]]<@opened([[N]]) CP>([[CP_BORROWED]]) : $@convention(witness_method: CP) <τ_0_0 where τ_0_0 : CP> (@guaranteed τ_0_0) -> @owned τ_0_0 +// CHECK: end_borrow [[CP_BORROWED]] : $@opened([[N]]) CP +// CHECK: destroy_value [[CP_ADDR_1]] : $@opened([[N]]) CP +// CHECK: [[RESULT_EXISTENTIAL:%[0-9]+]] = init_existential_ref [[APPLY_RESULT]] : $@opened([[N]]) CP : $@opened([[N]]) CP, $CP +// CHECK: destroy_value [[RESULT_EXISTENTIAL]] : $CP + _ = cp[] } @objc class ObjC { From 7a128470bd281b254525008131e5cf639ccedaeb Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Wed, 23 Sep 2020 18:58:21 -0700 Subject: [PATCH 011/745] [test] Re-enable linux-fatal-backtrace.swift This was fixed by #33235 rdar://66389949 --- test/Runtime/linux-fatal-backtrace.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Runtime/linux-fatal-backtrace.swift b/test/Runtime/linux-fatal-backtrace.swift index edac2a7a6c476..328c4bda9e0f9 100644 --- a/test/Runtime/linux-fatal-backtrace.swift +++ b/test/Runtime/linux-fatal-backtrace.swift @@ -1,4 +1,3 @@ -// REQUIRES: rdar66283479 // RUN: %empty-directory(%t) // RUN: %target-build-swift %s -o %t/a.out // RUN: %{python} %S/../Inputs/not.py "%target-run %t/a.out" 2>&1 | %{lldb-python} %utils/symbolicate-linux-fatal %t/a.out - | %{python} %utils/backtrace-check -u From 81fc342852c29051b70bb99307afbdab2b4fd6cb Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 23 Sep 2020 19:21:51 -0400 Subject: [PATCH 012/745] ASTScope: Ignore PatternEntryDeclScope when searching for statement labels --- include/swift/AST/ASTScope.h | 1 + lib/AST/ASTScopeLookup.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 8d986f7437036..cea19cf87d9f2 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -1079,6 +1079,7 @@ class PatternEntryDeclScope final : public AbstractPatternEntryScope { protected: bool lookupLocalsOrMembers(DeclConsumer) const override; + bool isLabeledStmtLookupTerminator() const override; }; class PatternEntryInitializerScope final : public AbstractPatternEntryScope { diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index 0acd36052e74e..dcce6526f1ed3 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -510,6 +510,10 @@ bool CaseStmtBodyScope::isLabeledStmtLookupTerminator() const { return false; } +bool PatternEntryDeclScope::isLabeledStmtLookupTerminator() const { + return false; +} + llvm::SmallVector ASTScopeImpl::lookupLabeledStmts(SourceFile *sourceFile, SourceLoc loc) { // Find the innermost scope from which to start our search. From cecbf212d3359481b59dea6030b5008b0909b7d2 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 23 Sep 2020 18:30:27 -0400 Subject: [PATCH 013/745] ASTScope: PatternEntryInitializerScope needs a custom lookup parent Bindings are not visible from inside their own initializer, eg this is invalid: var (x, y) = (y, x) The PatternEntryDeclScope which starts after the 'var' introduces x and y, and it contains the PatternEntryInitializerScope which starts after the '='. To model this properly in ASTScope, the PatternEntryInitializerScope overrides getLookupParent() to skip the PatternEntryDeclScope that contains it. I believe this is simpler than having PatternEntryDeclScope begin at the source location following the initializer, because it is hard to compute this source location in a way that works with invalid code, for example the 'var' might be nested inside of a BraceStmt with the closing '}' missing. --- include/swift/AST/ASTScope.h | 2 ++ lib/AST/ASTScopeLookup.cpp | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index cea19cf87d9f2..83c079444ac9b 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -435,6 +435,7 @@ class ASTScopeImpl { return p->getParent().isNonNull() ? p : nullptr; } +public: /// The tree is organized by source location and for most nodes this is also /// what obtaines for scoping. However, guards are different. The scope after /// the guard else must hop into the innermoset scope of the guard condition. @@ -1094,6 +1095,7 @@ class PatternEntryInitializerScope final : public AbstractPatternEntryScope { protected: ASTScopeImpl *expandSpecifically(ScopeCreator &scopeCreator) override; + NullablePtr getLookupParent() const override; private: void expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator &); diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index dcce6526f1ed3..d14f88313054b 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -271,6 +271,24 @@ bool GenericTypeOrExtensionScope::areMembersVisibleFromWhereClause() const { return isa(decl) || isa(decl); } +#pragma mark custom lookup parent behavior + +NullablePtr +PatternEntryInitializerScope::getLookupParent() const { + auto parent = getParent().get(); + assert(parent->getClassName() == "PatternEntryDeclScope"); + + // Lookups from inside a pattern binding initializer skip the parent + // scope that introduces bindings bound by the pattern, since we + // want this to work: + // + // func f(x: Int) { + // let x = x + // print(x) + // } + return parent->getLookupParent(); +} + #pragma mark looking in locals or members - locals bool GenericParamScope::lookupLocalsOrMembers(DeclConsumer consumer) const { From c5edc4574df4c230c7e0f08b6ac690b02d7e1065 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 23 Sep 2020 18:38:58 -0400 Subject: [PATCH 014/745] ASTScope: PatternEntryDeclScope changes the insertion point for local bindings This gives us the desired behavior, where local bindings are in scope after their definition. Note that BraceStmt still introduces all bindings at the beginning, but now we change BraceStmt to only introduce local functions and types, allowing us to disable parse-time lookup. --- lib/AST/ASTScopeCreation.cpp | 30 +++++++++++------------ test/NameLookup/scope_map_top_level.swift | 10 ++++---- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index bbef211f3a5b2..d1ee671567f1f 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -38,11 +38,6 @@ using namespace swift; using namespace ast_scope; -/// If true, nest scopes so a variable is out of scope before its declaration -/// Does not handle capture rules for local functions properly. -/// If false don't push uses down into subscopes after decls. -static const bool handleUseBeforeDef = false; - #pragma mark source range utilities static bool rangeableIsIgnored(const Decl *d) { return d->isImplicit(); } static bool rangeableIsIgnored(const Expr *d) { @@ -746,11 +741,11 @@ class NodeAdder if (auto *var = patternBinding->getSingleVar()) scopeCreator.addChildrenForKnownAttributes(var, parentScope); - const bool isInTypeDecl = parentScope->isATypeDeclScope(); + const bool isLocalBinding = patternBinding->getDeclContext()->isLocalContext(); const DeclVisibilityKind vis = - isInTypeDecl ? DeclVisibilityKind::MemberOfCurrentNominal - : DeclVisibilityKind::LocalVariable; + isLocalBinding ? DeclVisibilityKind::LocalVariable + : DeclVisibilityKind::MemberOfCurrentNominal; auto *insertionPoint = parentScope; for (auto i : range(patternBinding->getNumPatternEntries())) { insertionPoint = @@ -759,9 +754,12 @@ class NodeAdder insertionPoint, patternBinding, i, vis) .getPtrOr(insertionPoint); } - // If in a type decl, the type search will find these, - // but if in a brace stmt, must continue under the last binding. - return isInTypeDecl ? parentScope : insertionPoint; + + ASTScopeAssert(isLocalBinding || insertionPoint == parentScope, + "Bindings at the top-level or members of types should " + "not change the insertion point"); + + return insertionPoint; } NullablePtr visitEnumElementDecl(EnumElementDecl *eed, @@ -1041,11 +1039,13 @@ PatternEntryDeclScope::expandAScopeThatCreatesANewInsertionPoint( scopeCreator.addChildrenForAllLocalizableAccessorsInSourceOrder(var, this); }); - ASTScopeAssert(!handleUseBeforeDef, - "next line is wrong otherwise; would need a use scope"); + // In local context, the PatternEntryDeclScope becomes the insertion point, so + // that all any bindings introduecd by the pattern are in scope for subsequent + // lookups. + if (vis == DeclVisibilityKind::LocalVariable) + return {this, "All code that follows is inside this scope"}; - return {getParent().get(), "When not handling use-before-def, succeeding " - "code just goes in the same scope as this one"}; + return {getParent().get(), "Global and type members do not introduce scopes"}; } void diff --git a/test/NameLookup/scope_map_top_level.swift b/test/NameLookup/scope_map_top_level.swift index 014c920862e53..eeee236dae731 100644 --- a/test/NameLookup/scope_map_top_level.swift +++ b/test/NameLookup/scope_map_top_level.swift @@ -31,8 +31,8 @@ var i: Int = b.my_identity() // CHECK-EXPANDED-NEXT: `-NominalTypeBodyScope {{.*}}, [4:11 - 4:13] // CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [6:1 - 21:28] // CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [6:1 - 21:28] -// CHECK-EXPANDED-NEXT: |-PatternEntryDeclScope {{.*}}, [6:5 - 6:15] entry 0 'a' -// CHECK-EXPANDED-NEXT: `-PatternEntryInitializerScope {{.*}}, [6:15 - 6:15] entry 0 'a' +// CHECK-EXPANDED-NEXT: `-PatternEntryDeclScope {{.*}}, [6:5 - 21:28] entry 0 'a' +// CHECK-EXPANDED-NEXT: |-PatternEntryInitializerScope {{.*}}, [6:15 - 6:15] entry 0 'a' // CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [8:1 - 21:28] // CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [8:1 - 21:28] // CHECK-EXPANDED-NEXT: `-GuardStmtScope {{.*}}, [8:1 - 21:28] @@ -46,9 +46,9 @@ var i: Int = b.my_identity() // CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [11:18 - 11:19] // CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [13:1 - 21:28] // CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [13:1 - 21:28] -// CHECK-EXPANDED-NEXT: |-PatternEntryDeclScope {{.*}}, [13:5 - 13:9] entry 0 'c' -// CHECK-EXPANDED-NEXT: `-PatternEntryInitializerScope {{.*}}, [13:9 - 13:9] entry 0 'c' -// CHECK-EXPANDED-NEXT: |-TypeAliasDeclScope {{.*}}, [15:1 - 15:15] +// CHECK-EXPANDED-NEXT: `-PatternEntryDeclScope {{.*}}, [13:5 - 21:28] entry 0 'c' +// CHECK-EXPANDED-NEXT: |-PatternEntryInitializerScope {{.*}}, [13:9 - 13:9] entry 0 'c' +// CHECK-EXPANDED-NEXT: |-TypeAliasDeclScope {{.*}}, [15:1 - 15:15] // CHECK-EXPANDED-NEXT: |-ExtensionDeclScope {{.*}}, [17:14 - 19:1] // CHECK-EXPANDED-NEXT: `-ExtensionBodyScope {{.*}}, [17:15 - 19:1] // CHECK-EXPANDED-NEXT: `-AbstractFunctionDeclScope {{.*}}, [18:3 - 18:43] 'my_identity()' From 5461508cd78c3887ba709d3d1f3b213767f84ec3 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 23 Sep 2020 18:40:27 -0400 Subject: [PATCH 015/745] ASTScope: Remove isATypeDeclScope() --- include/swift/AST/ASTScope.h | 3 --- lib/AST/ASTScopeCreation.cpp | 5 ----- 2 files changed, 8 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 83c079444ac9b..d4b1e380ca013 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -344,9 +344,6 @@ class ASTScopeImpl { virtual NullablePtr insertionPointForDeferredExpansion(); virtual SourceRange sourceRangeForDeferredExpansion() const; -public: - bool isATypeDeclScope() const; - private: virtual ScopeCreator &getScopeCreator(); diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index d1ee671567f1f..f23cbca244d2c 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -1424,11 +1424,6 @@ AbstractPatternEntryScope::AbstractPatternEntryScope( "out of bounds"); } -bool ASTScopeImpl::isATypeDeclScope() const { - Decl *const pd = getDeclIfAny().getPtrOrNull(); - return pd && (isa(pd) || isa(pd)); -} - #pragma mark new operators void *ASTScopeImpl::operator new(size_t bytes, const ASTContext &ctx, unsigned alignment) { From 30f7b9ecef834a6ca0fa1fb8f1ae37815569a126 Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Thu, 24 Sep 2020 15:04:24 +0300 Subject: [PATCH 016/745] WinSDK: extract Performance submodule Currently some of the headers get included into `WinSDK.WinSock2` via windows.h --- stdlib/public/Platform/winsdk.modulemap | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/stdlib/public/Platform/winsdk.modulemap b/stdlib/public/Platform/winsdk.modulemap index 8d7d464d27ffd..9b31072dd0cc3 100644 --- a/stdlib/public/Platform/winsdk.modulemap +++ b/stdlib/public/Platform/winsdk.modulemap @@ -251,6 +251,25 @@ module WinSDK [system] { link "OleAut32.Lib" } + module Performance { + module PerfLib { + header "perflib.h" + export * + + link "AdvAPI32.Lib" + } + + module PDH { + header "Pdh.h" + export * + + link "Pdh.Lib" + } + + header "winperf.h" + export * + } + module Printing { header "winspool.h" export * From 14188f44288cb74f0ffa51f71fe7d021565964e6 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 23 Sep 2020 12:43:01 -0600 Subject: [PATCH 017/745] Remove ~moduleonly Duplicate of Compilation Record We thought we would need this while exploring certain pre-build optimizations a la rdar://problem/37725110. This information was never used. --- include/swift/Driver/Compilation.h | 10 --- lib/Driver/Compilation.cpp | 9 -- lib/Driver/Driver.cpp | 18 +--- .../Dependencies/Inputs/moduleonly/bar.swift | 28 ------ .../Dependencies/Inputs/moduleonly/baz.swift | 20 ----- .../Dependencies/Inputs/moduleonly/foo.swift | 28 ------ .../Inputs/moduleonly/output.json | 21 ----- test/Driver/Dependencies/moduleonly.swift | 85 ------------------- 8 files changed, 1 insertion(+), 218 deletions(-) delete mode 100644 test/Driver/Dependencies/Inputs/moduleonly/bar.swift delete mode 100644 test/Driver/Dependencies/Inputs/moduleonly/baz.swift delete mode 100644 test/Driver/Dependencies/Inputs/moduleonly/foo.swift delete mode 100644 test/Driver/Dependencies/Inputs/moduleonly/output.json delete mode 100644 test/Driver/Dependencies/moduleonly.swift diff --git a/include/swift/Driver/Compilation.h b/include/swift/Driver/Compilation.h index 1da048fa802b5..6b3b4b9629a32 100644 --- a/include/swift/Driver/Compilation.h +++ b/include/swift/Driver/Compilation.h @@ -206,15 +206,6 @@ class Compilation { /// of date. bool EnableIncrementalBuild; - /// When true, emit duplicated compilation record file whose filename is - /// suffixed with '~moduleonly'. - /// - /// This compilation record is used by '-emit-module'-only incremental builds - /// so that module-only builds do not affect compilation record file for - /// normal builds, while module-only incremental builds are able to use - /// artifacts of normal builds if they are already up to date. - bool OutputCompilationRecordForModuleOnlyBuild = false; - /// Indicates whether groups of parallel frontend jobs should be merged /// together and run in composite "batch jobs" when possible, to reduce /// redundant work. @@ -313,7 +304,6 @@ class Compilation { std::unique_ptr TranslatedArgs, InputFileList InputsWithTypes, std::string CompilationRecordPath, - bool OutputCompilationRecordForModuleOnlyBuild, StringRef ArgsHash, llvm::sys::TimePoint<> StartTime, llvm::sys::TimePoint<> LastBuildTime, size_t FilelistThreshold, diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index cf53235560519..cc678fbeb176d 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -108,7 +108,6 @@ Compilation::Compilation(DiagnosticEngine &Diags, std::unique_ptr TranslatedArgs, InputFileList InputsWithTypes, std::string CompilationRecordPath, - bool OutputCompilationRecordForModuleOnlyBuild, StringRef ArgsHash, llvm::sys::TimePoint<> StartTime, llvm::sys::TimePoint<> LastBuildTime, @@ -140,8 +139,6 @@ Compilation::Compilation(DiagnosticEngine &Diags, BuildStartTime(StartTime), LastBuildTime(LastBuildTime), EnableIncrementalBuild(EnableIncrementalBuild), - OutputCompilationRecordForModuleOnlyBuild( - OutputCompilationRecordForModuleOnlyBuild), EnableBatchMode(EnableBatchMode), BatchSeed(BatchSeed), BatchCount(BatchCount), @@ -1815,12 +1812,6 @@ int Compilation::performJobsImpl(bool &abnormalExit, checkForOutOfDateInputs(Diags, InputInfo); writeCompilationRecord(CompilationRecordPath, ArgsHash, BuildStartTime, InputInfo); - - if (OutputCompilationRecordForModuleOnlyBuild) { - // TODO: Optimize with clonefile(2) ? - llvm::sys::fs::copy_file(CompilationRecordPath, - CompilationRecordPath + "~moduleonly"); - } } abnormalExit = State.hadAnyAbnormalExit(); return State.getResult(); diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 7376b49ebe13c..aba127ff05077 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -404,10 +404,7 @@ class Driver::InputInfoMap using InputInfoMap = Driver::InputInfoMap; /// Get the filename for build record. Returns true if failed. -/// Additionally, set 'outputBuildRecordForModuleOnlyBuild' to true if this is -/// full compilation with swiftmodule. static bool getCompilationRecordPath(std::string &buildRecordPath, - bool &outputBuildRecordForModuleOnlyBuild, const OutputInfo &OI, const Optional &OFM, DiagnosticEngine *Diags) { @@ -430,17 +427,6 @@ static bool getCompilationRecordPath(std::string &buildRecordPath, return true; } - // In 'emit-module' only mode, use build-record filename suffixed with - // '~moduleonly'. So that module-only mode doesn't mess up build-record - // file for full compilation. - if (OI.CompilerOutputType == file_types::TY_SwiftModuleFile) { - buildRecordPath = buildRecordPath.append("~moduleonly"); - } else if (OI.ShouldTreatModuleAsTopLevelOutput) { - // If we emit module along with full compilation, emit build record - // file for '-emit-module' only mode as well. - outputBuildRecordForModuleOnlyBuild = true; - } - return false; } @@ -960,8 +946,7 @@ Driver::buildCompilation(const ToolChain &TC, computeIncremental(ArgList.get(), ShowIncrementalBuildDecisions); std::string buildRecordPath; - bool outputBuildRecordForModuleOnlyBuild = false; - getCompilationRecordPath(buildRecordPath, outputBuildRecordForModuleOnlyBuild, + getCompilationRecordPath(buildRecordPath, OI, OFM, Incremental ? &Diags : nullptr); SmallString<32> ArgsHash; @@ -1050,7 +1035,6 @@ Driver::buildCompilation(const ToolChain &TC, std::move(TranslatedArgList), std::move(Inputs), buildRecordPath, - outputBuildRecordForModuleOnlyBuild, ArgsHash, StartTime, LastBuildTime, diff --git a/test/Driver/Dependencies/Inputs/moduleonly/bar.swift b/test/Driver/Dependencies/Inputs/moduleonly/bar.swift deleted file mode 100644 index dd3d6dee17e0a..0000000000000 --- a/test/Driver/Dependencies/Inputs/moduleonly/bar.swift +++ /dev/null @@ -1,28 +0,0 @@ -public func bar() { } - -public class Bar { - @usableFromInline internal class Inner { - - /// makeX is declared in foo.swift - var x = makeX([1,2,3,4]) - - @usableFromInline internal func f() { - print("Bar.Inner.f") - } - } -} - -#if _runtime(_ObjC) && canImport(Foundation) - -import Foundation - -public class ObjCBar : NSObject { - - @objc(foo) - public func bar() -> String { - return "" - } - -} - -#endif diff --git a/test/Driver/Dependencies/Inputs/moduleonly/baz.swift b/test/Driver/Dependencies/Inputs/moduleonly/baz.swift deleted file mode 100644 index d50c4318edf42..0000000000000 --- a/test/Driver/Dependencies/Inputs/moduleonly/baz.swift +++ /dev/null @@ -1,20 +0,0 @@ -public func baz() {} - -public protocol BazProtocol { - associatedtype Ret - func method1() -> Ret? -} - -public enum Baz : BazProtocol { - case collection(T) - case other - - public func method1() -> T.SubSequence? { - switch self { - case .collection(let collection): - return makeX(collection) - default: - return nil - } - } -} diff --git a/test/Driver/Dependencies/Inputs/moduleonly/foo.swift b/test/Driver/Dependencies/Inputs/moduleonly/foo.swift deleted file mode 100644 index 4fda8eea4859d..0000000000000 --- a/test/Driver/Dependencies/Inputs/moduleonly/foo.swift +++ /dev/null @@ -1,28 +0,0 @@ -@inlinable -public func foo(x: Int) -> Int { - return x + 1 -} - -/// something -func makeX(_ seed: T) -> T.SubSequence { - return seed.dropFirst(1) -} - -public struct Foo : BazProtocol { - /// varx - public var x = makeX("foobar") - - /// method - public func method1() -> Int? { return 1 } - - /* Foo Bar */ - @_transparent - public func method2(x: Int) -> Int { - return x * 12 - } - - @inlinable - public func method3(x: T, y: T) -> T { - return x == y ? x : y - } -} diff --git a/test/Driver/Dependencies/Inputs/moduleonly/output.json b/test/Driver/Dependencies/Inputs/moduleonly/output.json deleted file mode 100644 index 940a7ab5db703..0000000000000 --- a/test/Driver/Dependencies/Inputs/moduleonly/output.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "./foo.swift": { - "object": "./foo.o", - "swift-dependencies": "./foo.swiftdeps", - "swiftmodule": "./foo~partial.swiftmodule" - }, - "./bar.swift": { - "object": "./bar.o", - "swift-dependencies": "./bar.swiftdeps", - "swiftmodule": "./bar~partial.swiftmodule" - }, - "./baz.swift": { - "object": "./baz.o", - "swift-dependencies": "./baz.swiftdeps", - "swiftmodule": "./baz~partial.swiftmodule" - }, - "": { - "swift-dependencies": "./buildrecord.swiftdeps" - } -} - diff --git a/test/Driver/Dependencies/moduleonly.swift b/test/Driver/Dependencies/moduleonly.swift deleted file mode 100644 index a2c38a2214e76..0000000000000 --- a/test/Driver/Dependencies/moduleonly.swift +++ /dev/null @@ -1,85 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: cp -r %S/Inputs/moduleonly/* %t -// RUN: touch -t 201801230045 %t/*.swift - -// RUN: cd %t && %target-build-swift -emit-module -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 | %FileCheck -check-prefix=CHECK1 %s -// RUN: test ! -f %t/buildrecord.swiftdeps -// RUN: test -f %t/buildrecord.swiftdeps~moduleonly - -// CHECK1-DAG: -primary-file ./foo.swift -// CHECK1-DAG: -primary-file ./bar.swift -// CHECK1-DAG: -primary-file ./baz.swift - -// RUN: cd %t && %target-build-swift -c -emit-module -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 | %FileCheck -check-prefix=CHECK2 %s -// RUN: test -f %t/buildrecord.swiftdeps -// RUN: test -f %t/buildrecord.swiftdeps~moduleonly - -// CHECK2-DAG: -primary-file ./foo.swift -// CHECK2-DAG: -primary-file ./bar.swift -// CHECK2-DAG: -primary-file ./baz.swift - -// RUN: cd %t && %target-build-swift -emit-module -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 | %FileCheck -check-prefix=CHECK3 %s -// RUN: test -f %t/buildrecord.swiftdeps~moduleonly -// RUN: test -f %t/buildrecord.swiftdeps - -// CHECK3-NOT: -primary-file ./foo.swift -// CHECK3-NOT: -primary-file ./bar.swift -// CHECK3-NOT: -primary-file ./baz.swift - -// RUN: touch -t 201801230123 %t/bar.swift -// RUN: cd %t && %target-build-swift -emit-module -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 | %FileCheck -check-prefix=CHECK4 %s - -// CHECK4-NOT: -primary-file ./foo.swift -// CHECK4-NOT: -primary-file ./baz.swift -// CHECK4-DAG: -primary-file ./bar.swift - -// RUN: touch -t 201801230145 %t/baz.swift -// RUN: cd %t && %target-build-swift -c -emit-module -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 | %FileCheck -check-prefix=CHECK5 %s - -// CHECK5-NOT: -primary-file ./foo.swift -// CHECK5-DAG: -primary-file ./bar.swift -// CHECK5-DAG: -primary-file ./baz.swift - -// RUN: cd %t && %target-build-swift -emit-module -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 | %FileCheck -check-prefix=CHECK6 %s - -// CHECK6-NOT: -primary-file ./foo.swift -// CHECK6-NOT: -primary-file ./bar.swift -// CHECK6-NOT: -primary-file ./baz.swift - - -// '-c' (without '-emit-module') from clean environment. -// -// RUN: %empty-directory(%t) -// RUN: cp -r %S/Inputs/moduleonly/* %t -// RUN: touch -t 201801230045 %t/*.swift -// RUN: cd %t && %target-build-swift -c -g -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 -// RUN: test ! -f %t/buildrecord.swiftdeps~moduleonly -// RUN: test -f %t/buildrecord.swiftdeps -// RUN: test ! -f %t/foo~partial.swiftmodule - -// '-emit-library -g' (without '-emit-module') from clean environment. -// -// RUN: %empty-directory(%t) -// RUN: cp -r %S/Inputs/moduleonly/* %t -// RUN: touch -t 201801230045 %t/*.swift -// RUN: cd %t && %target-build-swift -emit-library -g -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 -// RUN: test ! -f %t/buildrecord.swiftdeps~moduleonly -// RUN: test -f %t/buildrecord.swiftdeps -// RUN: test -f %t/foo~partial.swiftmodule - -// Ensure '-emit-module' and '-c -emit-module' emits identical 'swiftmodule' and 'swiftdoc' file. -// -// RUN: rm -f %t-moduleonly.swiftmodule -// RUN: rm -f %t-moduleonly.swiftdoc -// RUN: %empty-directory(%t) -// RUN: cp -r %S/Inputs/moduleonly/* %t -// RUN: touch -t 201801230045 %t/*.swift -// RUN: cd %t && %target-build-swift -emit-module -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 -// RUN: cp -f %t/testmodule.swiftmodule %t-moduleonly.swiftmodule -// RUN: cp -f %t/testmodule.swiftdoc %t-moduleonly.swiftdoc -// RUN: %empty-directory(%t) -// RUN: cp -r %S/Inputs/moduleonly/* %t -// RUN: touch -t 201801230045 %t/*.swift -// RUN: cd %t && %target-build-swift -c -emit-module -output-file-map ./output.json -incremental ./foo.swift ./bar.swift ./baz.swift -module-name testmodule -v 2>&1 -// RUN: diff %t/testmodule.swiftmodule %t-moduleonly.swiftmodule -// RUN: diff %t/testmodule.swiftdoc %t-moduleonly.swiftdoc From 587f06c4479eb3060d8b04bd7c3d57dd2d9fd336 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 24 Sep 2020 11:54:54 -0600 Subject: [PATCH 018/745] Fixup Structure of Mock SDKSettings This better reflects the actual structure of the files as they appear in the 10.15 and 10.15.4 SDKs respectively. rdar://69412779 --- .../Inputs/MacOSX10.15.4.versioned.sdk/SDKSettings.json | 4 +++- test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/test/Driver/Inputs/MacOSX10.15.4.versioned.sdk/SDKSettings.json b/test/Driver/Inputs/MacOSX10.15.4.versioned.sdk/SDKSettings.json index 618eaced4bb51..35383d42256e9 100644 --- a/test/Driver/Inputs/MacOSX10.15.4.versioned.sdk/SDKSettings.json +++ b/test/Driver/Inputs/MacOSX10.15.4.versioned.sdk/SDKSettings.json @@ -23,5 +23,7 @@ "13.2" : "10.15.1", "13.4" : "10.15.4" } - } + }, + "DefaultDeploymentTarget" : "11.0", + "MaximumDeploymentTarget" : "11.0.99" } diff --git a/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json b/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json index cc26f6bcfac37..c14072ff3272b 100644 --- a/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json +++ b/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json @@ -9,5 +9,7 @@ "13.1" : "10.15", "13.2" : "10.15.1" } - } + }, + "DefaultDeploymentTarget" : "10.15", + "MaximumDeploymentTarget" : "10.15.99" } From ece0399d601eec1069131f465bdbe7e16f4c6a50 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Tue, 22 Sep 2020 11:04:12 -0400 Subject: [PATCH 019/745] [Runtime] Have ConcurrentReadableHashMap use 1-byte or 2-byte indices when possible. --- include/swift/Runtime/Concurrent.h | 135 +++++++++++++++++++++-------- 1 file changed, 97 insertions(+), 38 deletions(-) diff --git a/include/swift/Runtime/Concurrent.h b/include/swift/Runtime/Concurrent.h index e634fbc25660d..67101ecb9d4c6 100644 --- a/include/swift/Runtime/Concurrent.h +++ b/include/swift/Runtime/Concurrent.h @@ -588,10 +588,6 @@ template struct ConcurrentReadableHashMap { "Elements must not have destructors (they won't be called)."); private: - /// The type of the elements of the indices array. TODO: use one or two byte - /// indices for smaller tables to save more memory. - using Index = unsigned; - /// The reciprocal of the load factor at which we expand the table. A value of /// 4 means that we resize at 1/4 = 75% load factor. static const size_t ResizeProportion = 4; @@ -619,20 +615,77 @@ template struct ConcurrentReadableHashMap { /// is stored inline. We work around this contradiction by considering the /// first index to always be occupied with a value that never matches any key. struct IndexStorage { - std::atomic Mask; + // Index size is variable based on capacity, either 8, 16, or 32 bits. + // + // This is somewhat conservative. We could have, for example, a capacity of + // 512 but a maximum index of only 200, which would still allow for 8-bit + // indices. However, taking advantage of this would require reallocating + // the index storage when the element count crossed a threshold, which is + // more complex, and the advantages are minimal. This keeps it simple. + // + // The first byte of the storage is the log 2 of the capacity. The remaining + // storage is then an array of 8, 16, or 32 bit integers, depending on the + // capacity number. This union allows us to access the capacity, and then + // access the rest of the storage by taking the address of one of the + // IndexZero members and indexing into it (always avoiding index 0). + union { + uint8_t CapacityLog2; + std::atomic IndexZero8; + std::atomic IndexZero16; + std::atomic IndexZero32; + }; + + // Get the size, in bytes, of the index needed for the given capacity. + static unsigned indexSize(uint8_t capacityLog2) { + if (capacityLog2 <= sizeof(uint8_t) * CHAR_BIT) + return sizeof(uint8_t); + if (capacityLog2 <= sizeof(uint16_t) * CHAR_BIT) + return sizeof(uint16_t); + return sizeof(uint32_t); + } + + unsigned indexSize() { return indexSize(CapacityLog2); } - static IndexStorage *allocate(size_t capacity) { - assert((capacity & (capacity - 1)) == 0 && - "Capacity must be a power of 2"); - auto *ptr = - reinterpret_cast(calloc(capacity, sizeof(Mask))); + static IndexStorage *allocate(size_t capacityLog2) { + assert(capacityLog2 > 0); + size_t capacity = 1UL << capacityLog2; + auto *ptr = reinterpret_cast( + calloc(capacity, indexSize(capacityLog2))); if (!ptr) swift::crash("Could not allocate memory."); - ptr->Mask.store(capacity - 1, std::memory_order_relaxed); + ptr->CapacityLog2 = capacityLog2; return ptr; } - std::atomic &at(size_t i) { return (&Mask)[i]; } + unsigned loadIndexAt(size_t i, std::memory_order order) { + assert(i > 0 && "index zero is off-limits, used to store capacity"); + + switch (indexSize()) { + case sizeof(uint8_t): + return (&IndexZero8)[i].load(order); + case sizeof(uint16_t): + return (&IndexZero16)[i].load(order); + case sizeof(uint32_t): + return (&IndexZero32)[i].load(order); + default: + swift_runtime_unreachable("unknown index size"); + } + } + + void storeIndexAt(unsigned value, size_t i, std::memory_order order) { + assert(i > 0 && "index zero is off-limits, used to store capacity"); + + switch (indexSize()) { + case sizeof(uint8_t): + return (&IndexZero8)[i].store(value, order); + case sizeof(uint16_t): + return (&IndexZero16)[i].store(value, order); + case sizeof(uint32_t): + return (&IndexZero32)[i].store(value, order); + default: + swift_runtime_unreachable("unknown index size"); + } + } }; /// A simple linked list representing pointers that need to be freed. @@ -720,17 +773,18 @@ template struct ConcurrentReadableHashMap { /// returning the new array with all existing indices copied into it. This /// operation performs a rehash, so that the indices are in the correct /// location in the new array. - IndexStorage *resize(IndexStorage *indices, Index indicesMask, + IndexStorage *resize(IndexStorage *indices, uint8_t indicesCapacityLog2, ElemTy *elements) { - // Mask is size - 1. Double the size. Start with 4 (fits into 16-byte malloc - // bucket). - size_t newCount = indices ? 2 * (indicesMask + 1) : 4; - size_t newMask = newCount - 1; + // Double the size. Start with 16 (fits into 16-byte malloc + // bucket), which is 2^4. + size_t newCapacityLog2 = indices ? indicesCapacityLog2 + 1 : 4; + size_t newMask = (1UL << newCapacityLog2) - 1; - IndexStorage *newIndices = IndexStorage::allocate(newCount); + IndexStorage *newIndices = IndexStorage::allocate(newCapacityLog2); - for (size_t i = 1; i <= indicesMask; i++) { - Index index = indices->at(i).load(std::memory_order_relaxed); + size_t indicesCount = 1UL << indicesCapacityLog2; + for (size_t i = 1; i < indicesCount; i++) { + unsigned index = indices->loadIndexAt(i, std::memory_order_relaxed); if (index == 0) continue; @@ -738,9 +792,12 @@ template struct ConcurrentReadableHashMap { auto hash = hash_value(*element); size_t newI = hash & newMask; - while (newIndices->at(newI) != 0) + // Index 0 is unusable (occupied by the capacity), so always skip it. + while (newI == 0 || + newIndices->loadIndexAt(newI, std::memory_order_relaxed) != 0) { newI = (newI + 1) & newMask; - newIndices->at(newI).store(index, std::memory_order_relaxed); + } + newIndices->storeIndexAt(index, newI, std::memory_order_relaxed); } Indices.store(newIndices, std::memory_order_release); @@ -752,16 +809,16 @@ template struct ConcurrentReadableHashMap { /// Search for the given key within the given indices and elements arrays. If /// an entry already exists for that key, return a pointer to the element. If - /// no entry exists, return a pointer to the location in the indices array - /// where the index of the new element would be stored. + /// no entry exists, return the location in the indices array where the index + /// of the new element would be stored. template - static std::pair *> + static std::pair find(const KeyTy &key, IndexStorage *indices, size_t elementCount, ElemTy *elements) { if (!indices) - return {nullptr, nullptr}; + return {nullptr, 0}; auto hash = hash_value(key); - auto indicesMask = indices->Mask.load(std::memory_order_relaxed); + auto indicesMask = (1UL << indices->CapacityLog2) - 1; auto i = hash & indicesMask; while (true) { @@ -769,15 +826,14 @@ template struct ConcurrentReadableHashMap { if (i == 0) i++; - auto *indexPtr = &indices->at(i); - auto index = indexPtr->load(std::memory_order_acquire); + auto index = indices->loadIndexAt(i, std::memory_order_acquire); // Element indices are 1-based, 0 means no entry. if (index == 0) - return {nullptr, indexPtr}; + return {nullptr, i}; if (index - 1 < elementCount) { auto *candidate = &elements[index - 1]; if (candidate->matchesKey(key)) - return {candidate, nullptr}; + return {candidate, 0}; } i = (i + 1) & indicesMask; @@ -895,7 +951,7 @@ template struct ConcurrentReadableHashMap { if (!indices) indices = resize(indices, 0, nullptr); - auto indicesMask = indices->Mask.load(std::memory_order_relaxed); + auto indicesCapacityLog2 = indices->CapacityLog2; auto elementCount = ElementCount.load(std::memory_order_relaxed); auto *elements = Elements.load(std::memory_order_relaxed); @@ -906,12 +962,14 @@ template struct ConcurrentReadableHashMap { return; } - // The actual capacity is indicesMask + 1. The number of slots in use is - // elementCount + 1, since the mask also takes a slot. - auto emptyCount = (indicesMask + 1) - (elementCount + 1); - auto proportion = (indicesMask + 1) / emptyCount; + auto indicesCapacity = 1UL << indicesCapacityLog2; + + // The number of slots in use is elementCount + 1, since the capacity also + // takes a slot. + auto emptyCount = indicesCapacity - (elementCount + 1); + auto proportion = indicesCapacity / emptyCount; if (proportion >= ResizeProportion) { - indices = resize(indices, indicesMask, elements); + indices = resize(indices, indicesCapacityLog2, elements); found = find(key, indices, elementCount, elements); assert(!found.first && "Shouldn't suddenly find the key after rehashing"); } @@ -928,7 +986,8 @@ template struct ConcurrentReadableHashMap { assert(hash_value(key) == hash_value(*element) && "Element must have the same hash code as its key."); ElementCount.store(elementCount + 1, std::memory_order_release); - found.second->store(elementCount + 1, std::memory_order_release); + indices->storeIndexAt(elementCount + 1, found.second, + std::memory_order_release); } deallocateFreeListIfSafe(); From 9b808c67faf951c08e28626432188cf68c4a1024 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 24 Sep 2020 14:01:08 -0700 Subject: [PATCH 020/745] [Concurrency] Import "did" delegate methods as @asyncHandler. Infer @asyncHandler on a protocol methods that follow the delegate convention of reporting that something happened via a "did" method, so long as they also meet the constraints for an @asyncHandler method in Swift. This enables inference of @asyncHandler for witnesses of these methods. --- include/swift/AST/Decl.h | 4 ++ include/swift/AST/DiagnosticsSema.def | 3 + include/swift/AST/TypeCheckRequests.h | 19 ++++++ include/swift/AST/TypeCheckerTypeIDZone.def | 2 + lib/AST/Decl.cpp | 11 ++++ lib/ClangImporter/ImportDecl.cpp | 59 +++++++++++++++++++ lib/Sema/TypeCheckConcurrency.cpp | 18 +++++- lib/Sema/TypeCheckConcurrency.h | 9 --- lib/Sema/TypeCheckProtocol.cpp | 2 +- test/IDE/print_clang_objc_async.swift | 8 +++ .../usr/include/ObjCConcurrency.h | 8 +++ 11 files changed, 130 insertions(+), 13 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 280e77580a15c..2e63ee9d7e64c 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5948,6 +5948,10 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// Returns true if the function is an @asyncHandler. bool isAsyncHandler() const; + /// Returns true if the function if the signature matches the form of an + /// @asyncHandler. + bool canBeAsyncHandler() const; + /// Returns true if the function body throws. bool hasThrows() const { return Bits.AbstractFunctionDecl.Throws; } diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 0c226713806bd..6b609eb584f53 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4121,6 +4121,9 @@ ERROR(asynchandler_async,none, ERROR(asynchandler_inout_parameter,none, "'inout' parameter is not allowed in '@asyncHandler' function", ()) +ERROR(asynchandler_unsafe_pointer_parameter,none, + "'%0' parameter is not allowed in `@asyncHandler` function", + ()) ERROR(asynchandler_mutating,none, "'@asyncHandler' function cannot be 'mutating'", ()) diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index d89a0e9be011a..4b6108ccf8a01 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -797,6 +797,25 @@ class IsAsyncHandlerRequest : bool isCached() const { return true; } }; +/// Determine whether the given function can be an @asyncHandler, without +/// producing any diagnostics. +class CanBeAsyncHandlerRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + bool evaluate(Evaluator &evaluator, FuncDecl *func) const; + +public: + // Caching + bool isCached() const { return true; } +}; + /// Determine whether the given class is an actor. class IsActorRequest : public SimpleRequest(this); + if (!func) + return false; + + auto mutableFunc = const_cast(func); + return evaluateOrDefault(getASTContext().evaluator, + CanBeAsyncHandlerRequest{mutableFunc}, + false); +} + BraceStmt *AbstractFunctionDecl::getBody(bool canSynthesize) const { if ((getBodyKind() == BodyKind::Synthesize || getBodyKind() == BodyKind::Unparsed) && diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index b9271191a780c..079ded2e02094 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -40,6 +40,7 @@ #include "swift/Basic/Defer.h" #include "swift/Basic/PrettyStackTrace.h" #include "swift/Basic/Statistic.h" +#include "swift/Basic/StringExtras.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/Config.h" #include "swift/Parse/Lexer.h" @@ -7575,6 +7576,46 @@ bool importer::isSpecialUIKitStructZeroProperty(const clang::NamedDecl *decl) { return ident->isStr("UIEdgeInsetsZero") || ident->isStr("UIOffsetZero"); } +/// Determine whether any of the parameters to the given function is of an +/// unsafe pointer type. +static bool hasAnyUnsafePointerParameters(FuncDecl *func) { + for (auto param : *func->getParameters()) { + Type paramType = + param->toFunctionParam().getPlainType()->lookThroughAllOptionalTypes(); + if (paramType->getAnyPointerElementType()) { + return true; + } + } + + return false; +} + +/// Determine whether the given Objective-C method is likely to be an +/// asynchronous handler based on its name. +static bool isObjCMethodLikelyAsyncHandler( + const clang::ObjCMethodDecl *method) { + auto selector = method->getSelector(); + + for (unsigned argIdx : range(std::max(selector.getNumArgs(), 1u))) { + auto selectorPiece = selector.getNameForSlot(argIdx); + // For the first selector piece, look for the word "did" anywhere. + if (argIdx == 0) { + for (auto word : camel_case::getWords(selectorPiece)) { + if (word == "did" || word == "Did") + return true; + } + + continue; + } + + // Otherwise, check whether any subsequent selector piece starts with "did". + if (camel_case::getFirstWord(selectorPiece) == "did") + return true; + } + + return false; +} + /// Import Clang attributes as Swift attributes. void ClangImporter::Implementation::importAttributes( const clang::NamedDecl *ClangDecl, @@ -7813,6 +7854,24 @@ void ClangImporter::Implementation::importAttributes( if (ClangDecl->hasAttr()) { MappedDecl->getAttrs().add(new (C) EffectsAttr(EffectsKind::ReadOnly)); } + + // Infer @asyncHandler on imported protocol methods that meet the semantic + // requirements. + if (SwiftContext.LangOpts.EnableExperimentalConcurrency) { + if (auto func = dyn_cast(MappedDecl)) { + if (auto proto = dyn_cast(func->getDeclContext())) { + if (proto->isObjC() && isa(ClangDecl) && + func->isInstanceMember() && !isa(func) && + isObjCMethodLikelyAsyncHandler( + cast(ClangDecl)) && + func->canBeAsyncHandler() && + !hasAnyUnsafePointerParameters(func)) { + MappedDecl->getAttrs().add( + new (C) AsyncHandlerAttr(/*IsImplicit=*/false)); + } + } + } + } } Decl * diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index e1c725979909e..b5c19ab93c570 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -22,7 +22,14 @@ using namespace swift; -bool swift::checkAsyncHandler(FuncDecl *func, bool diagnose) { +/// Check whether the @asyncHandler attribute can be applied to the given +/// function declaration. +/// +/// \param diagnose Whether to emit a diagnostic when a problem is encountered. +/// +/// \returns \c true if there was a problem with adding the attribute, \c false +/// otherwise. +static bool checkAsyncHandler(FuncDecl *func, bool diagnose) { if (!func->getResultInterfaceType()->isVoid()) { if (diagnose) { func->diagnose(diag::asynchandler_returns_value) @@ -78,7 +85,7 @@ bool swift::checkAsyncHandler(FuncDecl *func, bool diagnose) { void swift::addAsyncNotes(FuncDecl *func) { func->diagnose(diag::note_add_async_to_function, func->getName()); - if (!checkAsyncHandler(func, /*diagnose=*/false)) { + if (func->canBeAsyncHandler()) { func->diagnose( diag::note_add_asynchandler_to_function, func->getName()) .fixItInsert(func->getAttributeInsertionLoc(false), "@asyncHandler "); @@ -108,7 +115,7 @@ bool IsAsyncHandlerRequest::evaluate( return false; // Is it possible to infer @asyncHandler for this function at all? - if (checkAsyncHandler(func, /*diagnose=*/false)) + if (!func->canBeAsyncHandler()) return false; // Add an implicit @asyncHandler attribute and return true. We're done. @@ -157,6 +164,11 @@ bool IsAsyncHandlerRequest::evaluate( return false; } +bool CanBeAsyncHandlerRequest::evaluate( + Evaluator &evaluator, FuncDecl *func) const { + return !checkAsyncHandler(func, /*diagnose=*/false); +} + bool IsActorRequest::evaluate( Evaluator &evaluator, ClassDecl *classDecl) const { // If concurrency is not enabled, we don't have actors. diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index 030b466537a8c..ea0f92a7ff3a6 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -26,15 +26,6 @@ class Expr; class FuncDecl; class ValueDecl; -/// Check whether the @asyncHandler attribute can be applied to the given -/// function declaration. -/// -/// \param diagnose Whether to emit a diagnostic when a problem is encountered. -/// -/// \returns \c true if there was a problem with adding the attribute, \c false -/// otherwise. -bool checkAsyncHandler(FuncDecl *func, bool diagnose); - /// Add notes suggesting the addition of 'async' or '@asyncHandler', as /// appropriate, to a diagnostic for a function that isn't an async context. void addAsyncNotes(FuncDecl *func); diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 38f4b57308c07..be4d19166e2f0 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -2458,7 +2458,7 @@ diagnoseMatch(ModuleDecl *module, NormalProtocolConformance *conformance, bool canBeAsyncHandler = false; if (auto witnessFunc = dyn_cast(match.Witness)) { canBeAsyncHandler = !witnessFunc->isAsyncHandler() && - !checkAsyncHandler(witnessFunc, /*diagnose=*/false); + witnessFunc->canBeAsyncHandler(); } auto diag = match.Witness->diagnose( canBeAsyncHandler ? diag::actor_isolated_witness_could_be_async_handler diff --git a/test/IDE/print_clang_objc_async.swift b/test/IDE/print_clang_objc_async.swift index 7d3029fff8be6..a836ac927ff5f 100644 --- a/test/IDE/print_clang_objc_async.swift +++ b/test/IDE/print_clang_objc_async.swift @@ -18,3 +18,11 @@ // CHECK-DAG: func findAnswerFailingly() async throws -> String? // CHECK-DAG: func doSomethingFun(_ operation: String) async // CHECK: {{^[}]$}} + +// CHECK-LABEL: protocol RefrigeratorDelegate +// CHECK-NEXT: @asyncHandler func someoneDidOpenRefrigerator(_ fridge: Any) +// CHECK-NEXT: @asyncHandler func refrigerator(_ fridge: Any, didGetFilledWithItems items: [Any]) +// CHECK-NEXT: {{^}} func refrigerator(_ fridge: Any, didGetFilledWithIntegers items: UnsafeMutablePointer, count: Int) +// CHECK-NEXT: {{^}} func refrigerator(_ fridge: Any, willAddItem item: Any) +// CHECK-NEXT: {{^}} func refrigerator(_ fridge: Any, didRemoveItem item: Any) -> Bool +// CHECK-NEXT: {{^[}]$}} diff --git a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h index b03e3530a4968..a74a5a8500499 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h @@ -13,4 +13,12 @@ @property(readwrite) void (^completionHandler)(NSInteger); @end +@protocol RefrigeratorDelegate +- (void)someoneDidOpenRefrigerator:(id)fridge; +- (void)refrigerator:(id)fridge didGetFilledWithItems:(NSArray *)items; +- (void)refrigerator:(id)fridge didGetFilledWithIntegers:(NSInteger *)items count:(NSInteger)count; +- (void)refrigerator:(id)fridge willAddItem:(id)item; +- (BOOL)refrigerator:(id)fridge didRemoveItem:(id)item; +@end + #pragma clang assume_nonnull end From a000375881ce37668c136fdaed71c997b194cebe Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 24 Sep 2020 14:23:58 -0700 Subject: [PATCH 021/745] Remove unused new diagnostic --- include/swift/AST/DiagnosticsSema.def | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 6b609eb584f53..0c226713806bd 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4121,9 +4121,6 @@ ERROR(asynchandler_async,none, ERROR(asynchandler_inout_parameter,none, "'inout' parameter is not allowed in '@asyncHandler' function", ()) -ERROR(asynchandler_unsafe_pointer_parameter,none, - "'%0' parameter is not allowed in `@asyncHandler` function", - ()) ERROR(asynchandler_mutating,none, "'@asyncHandler' function cannot be 'mutating'", ()) From a415d87a28d48345760ea47c3550a6f3da1c9834 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Wed, 29 Jul 2020 14:38:12 -0700 Subject: [PATCH 022/745] Register deserialization notification handlers in ome only once --- include/swift/SIL/SILModule.h | 22 +++++++++++++++++++ lib/SIL/IR/SILModule.cpp | 2 ++ .../Mandatory/OwnershipModelEliminator.cpp | 15 +++++++++---- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index 0610893425ba2..33f7b07ff875f 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -263,6 +263,15 @@ class SILModule { /// to ensure that the module is serialized only once. bool serialized; + /// Set if we have registered a deserialization notification handler for + /// lowering ownership in non transparent functions. + /// This gets set in NonTransparent OwnershipModelEliminator pass. + bool regDeserializationNotificationHandlerForNonTransparentFuncOME; + /// Set if we have registered a deserialization notification handler for + /// lowering ownership in transparent functions. + /// This gets set in OwnershipModelEliminator pass. + bool regDeserializationNotificationHandlerForAllFuncOME; + /// Action to be executed for serializing the SILModule. ActionCallback SerializeSILAction; @@ -301,6 +310,19 @@ class SILModule { deserializationNotificationHandlers.erase(handler); } + bool hasRegisteredDeserializationNotificationHandlerForNonTransparentFuncOME() { + return regDeserializationNotificationHandlerForNonTransparentFuncOME; + } + bool hasRegisteredDeserializationNotificationHandlerForAllFuncOME() { + return regDeserializationNotificationHandlerForAllFuncOME; + } + void setRegisteredDeserializationNotificationHandlerForNonTransparentFuncOME() { + regDeserializationNotificationHandlerForNonTransparentFuncOME = true; + } + void setRegisteredDeserializationNotificationHandlerForAllFuncOME() { + regDeserializationNotificationHandlerForAllFuncOME = true; + } + /// Add a delete notification handler \p Handler to the module context. void registerDeleteNotificationHandler(DeleteNotificationHandler* Handler); diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index c70e4865c3641..2966cb89e7ba7 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -95,6 +95,8 @@ class SILModule::SerializationCallback final SILModule::SILModule(llvm::PointerUnion context, Lowering::TypeConverter &TC, const SILOptions &Options) : Stage(SILStage::Raw), Options(Options), serialized(false), + regDeserializationNotificationHandlerForNonTransparentFuncOME(false), + regDeserializationNotificationHandlerForAllFuncOME(false), SerializeSILAction(), Types(TC) { assert(!context.isNull()); if (auto *file = context.dyn_cast()) { diff --git a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp index b48a1cc0edb16..0e57fc67d8a6e 100644 --- a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp +++ b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp @@ -432,12 +432,19 @@ struct OwnershipModelEliminator : SILFunctionTransform { FunctionBodyDeserializationNotificationHandler; std::unique_ptr ptr; if (SkipTransparent) { - ptr.reset(new NotificationHandlerTy( - prepareNonTransparentSILFunctionForOptimization)); + if (!Mod.hasRegisteredDeserializationNotificationHandlerForNonTransparentFuncOME()) { + ptr.reset(new NotificationHandlerTy( + prepareNonTransparentSILFunctionForOptimization)); + Mod.registerDeserializationNotificationHandler(std::move(ptr)); + Mod.setRegisteredDeserializationNotificationHandlerForNonTransparentFuncOME(); + } } else { - ptr.reset(new NotificationHandlerTy(prepareSILFunctionForOptimization)); + if (!Mod.hasRegisteredDeserializationNotificationHandlerForAllFuncOME()) { + ptr.reset(new NotificationHandlerTy(prepareSILFunctionForOptimization)); + Mod.registerDeserializationNotificationHandler(std::move(ptr)); + Mod.setRegisteredDeserializationNotificationHandlerForAllFuncOME(); + } } - Mod.registerDeserializationNotificationHandler(std::move(ptr)); } }; From f5baf7a8f175659b0bb4b458a1a5e29c103c59aa Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Thu, 30 Jul 2020 08:46:39 -0700 Subject: [PATCH 023/745] Update CHECK lines in tests after moving ossa to a function pass --- test/DebugInfo/dbgvalue-insertpt.swift | 12 +----------- test/IRGen/big_types_corner_cases.swift | 6 ------ test/SILOptimizer/prespecialize.swift | 6 ------ 3 files changed, 1 insertion(+), 23 deletions(-) diff --git a/test/DebugInfo/dbgvalue-insertpt.swift b/test/DebugInfo/dbgvalue-insertpt.swift index 0d670e4bb7dba..469f1223f4a93 100644 --- a/test/DebugInfo/dbgvalue-insertpt.swift +++ b/test/DebugInfo/dbgvalue-insertpt.swift @@ -1,19 +1,9 @@ -// RUN: %target-swift-frontend -g -emit-ir %s | %FileCheck %s +// RUN: %target-swift-frontend -g -emit-ir -Xllvm '-sil-inline-never-functions=next' %s | %FileCheck %s // FIXME: This test should be testing a non-shadow-copied value instead. for i in 0 ..< 3 { // CHECK: %[[ALLOCA:[0-9]+]] = alloca %TSiSg // CHECK: %i.debug = alloca i{{32|64}} // CHECK-NEXT: call void @llvm.dbg.declare(metadata i{{32|64}}* %i.debug, // CHECK-SAME: metadata ![[I:[0-9]+]], - // CHECK: call swiftcc{{.*}} @{{.*}}next{{.*}} - // CHECK: %[[LD:[0-9]+]] = load i{{32|64}}, i{{32|64}}* - // CHECK: br i1 {{%.*}}, label %[[FAIL:.*]], label %[[SUCCESS:.*]], - // - // CHECK: [[SUCCESS]]: - // CHECK: br label %[[NEXT_BB:.*]], - // - // CHECK: [[NEXT_BB]]: - // CHECK: %[[PHI_VAL:.*]] = phi i{{32|64}} [ %[[LD]], %[[SUCCESS]] ] - // CHECK: store i{{32|64}} %[[PHI_VAL]], i{{32|64}}* %i.debug // CHECK: ![[I]] = !DILocalVariable(name: "i", } diff --git a/test/IRGen/big_types_corner_cases.swift b/test/IRGen/big_types_corner_cases.swift index f956b23f7effe..7acfaa13c601d 100644 --- a/test/IRGen/big_types_corner_cases.swift +++ b/test/IRGen/big_types_corner_cases.swift @@ -204,12 +204,6 @@ public func testGetFunc() { // CHECK: [[CALL1:%.*]] = call {{.*}} @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$sSayy22big_types_corner_cases9BigStructVcSgGMD" // CHECK: [[CALL2:%.*]] = call i8** @"$sSayy22big_types_corner_cases9BigStructVcSgGSayxGSlsWl // CHECK: call swiftcc void @"$sSlsE10firstIndex5where0B0QzSgSb7ElementQzKXE_tKF"(%TSq.{{.*}}* noalias nocapture sret %{{[0-9]+}}, i8* bitcast ({{.*}}* @"$s22big_types_corner_cases9BigStruct{{.*}}_TRTA{{(\.ptrauth)?}}" to i8*), %swift.opaque* %{{[0-9]+}}, %swift.type* %{{[0-9]+}}, i8** [[CALL2]] - -// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} hidden swiftcc void @"$s22big_types_corner_cases7TestBigC5test2yyF"(%T22big_types_corner_cases7TestBigC* swiftself %0) -// CHECK: [[CALL1:%.*]] = call {{.*}} @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$sSaySS2ID_y22big_types_corner_cases9BigStructVcSg7handlertGMD" -// CHECK: [[CALL2:%.*]] = call i8** @"$sSaySS2ID_y22big_types_corner_cases9BigStructVcSg7handlertGSayxGSlsWl" -// CHECK: call swiftcc void @"$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF"(%Ts16IndexingIteratorV{{.*}}* noalias nocapture sret {{.*}}, %swift.type* [[CALL1]], i8** [[CALL2]], %swift.opaque* noalias nocapture swiftself {{.*}}) -// CHECK: ret void class TestBig { typealias Handler = (BigStruct) -> Void diff --git a/test/SILOptimizer/prespecialize.swift b/test/SILOptimizer/prespecialize.swift index f9c27e9e02cb9..1cbc165230253 100644 --- a/test/SILOptimizer/prespecialize.swift +++ b/test/SILOptimizer/prespecialize.swift @@ -7,12 +7,6 @@ // CHECK-LABEL: sil [noinline] @$s13prespecialize4test_4sizeySaySiGz_SitF // -// function_ref specialized Collection.makeIterator() -> IndexingIterator -// CHECK: function_ref @$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyFSnySiG_Tg5 -// -// function_ref specialized IndexingIterator.next() -> A.Element? -// CHECK: function_ref @$ss16IndexingIteratorV4next7ElementQzSgyFSnySiG_Tg5 -// // Look for generic specialization of Swift.Array.subscript.getter : (Swift.Int) -> A // CHECK: function_ref @$sSayxSicigSi_Tg5 // CHECK: return From 0a94737ff1496e048bbda720f9d8b2184b7d4aa8 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Thu, 24 Sep 2020 15:21:01 -0700 Subject: [PATCH 024/745] Emit -enable-experimental-concurrency into module interfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …when the module is built with that flag. Fixes rdar://69322538. --- include/swift/Option/FrontendOptions.td | 8 ++++---- test/ModuleInterface/concurrency.swift | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 test/ModuleInterface/concurrency.swift diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 2b1f8efb047d2..63c68b14c918e 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -202,6 +202,10 @@ def disable_objc_attr_requires_foundation_module : HelpText<"Disable requiring uses of @objc to require importing the " "Foundation module">; +def enable_experimental_concurrency : + Flag<["-"], "enable-experimental-concurrency">, + HelpText<"Enable experimental concurrency model">; + def enable_resilience : Flag<["-"], "enable-resilience">, HelpText<"Deprecated, use -enable-library-evolution instead">; @@ -389,10 +393,6 @@ def enable_experimental_static_assert : Flag<["-"], "enable-experimental-static-assert">, HelpText<"Enable experimental #assert">; -def enable_experimental_concurrency : - Flag<["-"], "enable-experimental-concurrency">, - HelpText<"Enable experimental concurrency model">; - def enable_subst_sil_function_types_for_function_values : Flag<["-"], "enable-subst-sil-function-types-for-function-values">, HelpText<"Use substituted function types for SIL type lowering of function values">; diff --git a/test/ModuleInterface/concurrency.swift b/test/ModuleInterface/concurrency.swift new file mode 100644 index 0000000000000..27c5b55ac5d7d --- /dev/null +++ b/test/ModuleInterface/concurrency.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -typecheck -enable-library-evolution -enable-experimental-concurrency -emit-module-interface-path %t/Library.swiftinterface -DLIBRARY -module-name Library %s + +#if LIBRARY +public func fn() async { + fatalError() +} + +// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -I %t + +#else +import Library + +func callFn() async { + await fn() +} +#endif + +// RUN: %FileCheck %s <%t/Library.swiftinterface +// CHECK: // swift-module-flags:{{.*}} -enable-experimental-concurrency +// CHECK: public func fn() async From 8c6098a3de956ca058d55a0d4875b29e3ce38498 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 24 Sep 2020 15:47:07 -0700 Subject: [PATCH 025/745] [CSFix] Add a fix to detect when type of couldn't be inferred --- lib/Sema/CSFix.cpp | 11 +++++++++++ lib/Sema/CSFix.h | 23 +++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 9808f8d63e7c8..602cf3b22886e 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -1603,3 +1603,14 @@ IgnoreInvalidFunctionBuilderBody *IgnoreInvalidFunctionBuilderBody::create( return new (cs.getAllocator()) IgnoreInvalidFunctionBuilderBody(cs, phase, locator); } + +bool SpecifyContextualTypeForNil::diagnose(const Solution &solution, + bool asNote) const { + return false; +} + +SpecifyContextualTypeForNil * +SpecifyContextualTypeForNil::create(ConstraintSystem &cs, + ConstraintLocator *locator) { + return new (cs.getAllocator()) SpecifyContextualTypeForNil(cs, locator); +} diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h index f03f2d0c6ebd2..77933617db862 100644 --- a/lib/Sema/CSFix.h +++ b/lib/Sema/CSFix.h @@ -279,6 +279,9 @@ enum class FixKind : uint8_t { /// Ignore function builder body which fails `pre-check` call. IgnoreInvalidFunctionBuilderBody, + + /// Resolve type of `nil` by providing a contextual type. + SpecifyContextualTypeForNil, }; class ConstraintFix { @@ -2026,6 +2029,26 @@ class IgnoreInvalidFunctionBuilderBody final : public ConstraintFix { create(ConstraintSystem &cs, ErrorInPhase phase, ConstraintLocator *locator); }; +class SpecifyContextualTypeForNil final : public ConstraintFix { + SpecifyContextualTypeForNil(ConstraintSystem &cs, + ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::SpecifyContextualTypeForNil, locator) {} + +public: + std::string getName() const override { + return "specify contextual type to resolve `nil` literal"; + } + + bool diagnose(const Solution &solution, bool asNote = false) const override; + + bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override { + return diagnose(*commonFixes.front().first); + } + + static SpecifyContextualTypeForNil *create(ConstraintSystem & cs, + ConstraintLocator * locator); +}; + } // end namespace constraints } // end namespace swift From 48415ad2288ba0449b62456634395edfd98adfcf Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 24 Sep 2020 17:10:57 -0600 Subject: [PATCH 026/745] [NFC] Stage in driver flags for experimental cross-module incremental builds --- include/swift/Driver/Compilation.h | 10 +++++++++- include/swift/Option/Options.td | 5 +++++ lib/Driver/Compilation.cpp | 6 ++++-- lib/Driver/Driver.cpp | 5 ++++- lib/Driver/ToolChains.cpp | 4 ++++ 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/include/swift/Driver/Compilation.h b/include/swift/Driver/Compilation.h index 6b3b4b9629a32..d970a9cf834cd 100644 --- a/include/swift/Driver/Compilation.h +++ b/include/swift/Driver/Compilation.h @@ -281,6 +281,9 @@ class Compilation { /// Experiment with source-range-based dependencies const bool EnableSourceRangeDependencies; + /// (experimental) Enable cross-module incremental build scheduling. + const bool EnableCrossModuleIncrementalBuild; + public: /// Will contain a comparator if an argument demands it. Optional IncrementalComparator; @@ -323,7 +326,8 @@ class Compilation { bool FineGrainedDependenciesIncludeIntrafileOnes = false, bool EnableSourceRangeDependencies = false, bool CompareIncrementalSchemes = false, - StringRef CompareIncrementalSchemesPath = ""); + StringRef CompareIncrementalSchemesPath = "", + bool EnableCrossModuleIncrementalBuild = false); // clang-format on ~Compilation(); @@ -429,6 +433,10 @@ class Compilation { return ShowDriverTimeCompilation; } + bool getEnableCrossModuleIncrementalBuild() const { + return EnableCrossModuleIncrementalBuild; + } + size_t getFilelistThreshold() const { return FilelistThreshold; } diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index aab9f604e7caf..a1c2b34fc1f59 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -584,6 +584,11 @@ def experimental_cxx_stdlib : Separate<["-"], "experimental-cxx-stdlib">, HelpText<"C++ standard library to use; forwarded to Clang's -stdlib flag">; +def enable_experimental_cross_module_incremental_build : + Flag<["-"], "enable-experimental-cross-module-incremental-build">, + HelpText<"(experimental) Enable cross-module incremental build metadata and " + "driver scheduling">; + // Diagnostic control options def suppress_warnings : Flag<["-"], "suppress-warnings">, diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index cc678fbeb176d..f2cc64f05fa51 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -127,7 +127,8 @@ Compilation::Compilation(DiagnosticEngine &Diags, bool FineGrainedDependenciesIncludeIntrafileOnes, bool EnableSourceRangeDependencies, bool CompareIncrementalSchemes, - StringRef CompareIncrementalSchemesPath) + StringRef CompareIncrementalSchemesPath, + bool EnableCrossModuleIncrementalBuild) : Diags(Diags), TheToolChain(TC), TheOutputInfo(OI), Level(Level), @@ -155,7 +156,8 @@ Compilation::Compilation(DiagnosticEngine &Diags, EmitFineGrainedDependencyDotFileAfterEveryImport), FineGrainedDependenciesIncludeIntrafileOnes( FineGrainedDependenciesIncludeIntrafileOnes), - EnableSourceRangeDependencies(EnableSourceRangeDependencies) + EnableSourceRangeDependencies(EnableSourceRangeDependencies), + EnableCrossModuleIncrementalBuild(EnableCrossModuleIncrementalBuild) { if (CompareIncrementalSchemes) IncrementalComparator.emplace( diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index aba127ff05077..6394cb4f65807 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1027,6 +1027,8 @@ Driver::buildCompilation(const ToolChain &TC, OPT_driver_emit_fine_grained_dependency_dot_file_after_every_import); const bool FineGrainedDependenciesIncludeIntrafileOnes = ArgList->hasArg(options::OPT_fine_grained_dependency_include_intrafile); + const bool EnableCrossModuleDependencies = ArgList->hasArg( + options::OPT_enable_experimental_cross_module_incremental_build); // clang-format off C = std::make_unique( @@ -1054,7 +1056,8 @@ Driver::buildCompilation(const ToolChain &TC, FineGrainedDependenciesIncludeIntrafileOnes, EnableSourceRangeDependencies, CompareIncrementalSchemes, - CompareIncrementalSchemesPath); + CompareIncrementalSchemesPath, + EnableCrossModuleDependencies); // clang-format on } diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 7b738c0a74511..8d5d0b30d7ede 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -1047,6 +1047,10 @@ ToolChain::constructInvocation(const MergeModuleJobAction &job, context.Args.AddLastArg(Arguments, options::OPT_import_objc_header); + context.Args.AddLastArg( + Arguments, + options::OPT_enable_experimental_cross_module_incremental_build); + Arguments.push_back("-module-name"); Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName)); From 574ee39397a36be8dc9fb1efd65551f188fed089 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 24 Sep 2020 17:11:28 -0600 Subject: [PATCH 027/745] Stage in frontend flags for experimental cross-module incremental builds --- include/swift/Frontend/FrontendOptions.h | 8 ++++++++ include/swift/Serialization/SerializationOptions.h | 1 + lib/Frontend/ArgsToFrontendOptionsConverter.cpp | 3 +++ lib/Frontend/Frontend.cpp | 3 +++ test/Driver/cross_module.swift | 6 ++++++ 5 files changed, 21 insertions(+) create mode 100644 test/Driver/cross_module.swift diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index f39915e8a202d..155ebcb7adf81 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -276,6 +276,14 @@ class FrontendOptions { /// of the main Swift module's source files. bool ImportPrescan = false; + /// When performing an incremental build, ensure that cross-module incremental + /// build metadata is available in any swift modules emitted by this frontend + /// job. + /// + /// This flag is currently only propagated from the driver to + /// any merge-modules jobs. + bool EnableExperimentalCrossModuleIncrementalBuild = false; + /// The different modes for validating TBD against the LLVM IR. enum class TBDValidationMode { Default, ///< Do the default validation for the current platform. diff --git a/include/swift/Serialization/SerializationOptions.h b/include/swift/Serialization/SerializationOptions.h index c252ca20a0a71..1623b00aa38b9 100644 --- a/include/swift/Serialization/SerializationOptions.h +++ b/include/swift/Serialization/SerializationOptions.h @@ -131,6 +131,7 @@ namespace swift { bool SerializeAllSIL = false; bool SerializeOptionsForDebugging = false; bool IsSIB = false; + bool ExperimentalCrossModuleIncrementalInfo = false; }; } // end namespace swift diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 4ef0b90131e8e..c3cee42314da2 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -92,6 +92,9 @@ bool ArgsToFrontendOptionsConverter::convert( Opts.ImportPrescan |= Args.hasArg(OPT_import_prescan); + Opts.EnableExperimentalCrossModuleIncrementalBuild |= + Args.hasArg(OPT_enable_experimental_cross_module_incremental_build); + // Always track system dependencies when scanning dependencies. if (const Arg *ModeArg = Args.getLastArg(OPT_modes_Group)) { if (ModeArg->getOption().matches(OPT_scan_dependencies)) { diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 798f1538a4bb1..a337d876eedc1 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -175,6 +175,9 @@ SerializationOptions CompilerInvocation::computeSerializationOptions( opts.SerializeOptionsForDebugging.getValueOr( !isModuleExternallyConsumed(module)); + serializationOpts.ExperimentalCrossModuleIncrementalInfo = + opts.EnableExperimentalCrossModuleIncrementalBuild; + return serializationOpts; } diff --git a/test/Driver/cross_module.swift b/test/Driver/cross_module.swift new file mode 100644 index 0000000000000..ca07a5b10b0d3 --- /dev/null +++ b/test/Driver/cross_module.swift @@ -0,0 +1,6 @@ +// RUN: %empty-directory(%t/sub) +// RUN: echo "{\"\": {\"swift-dependencies\": \"cross_module.swiftdeps\"}}" > %t/ofm.json +// RUN: %swiftc_driver -incremental -emit-module -module-name cross_module -output-file-map=%/t/ofm.json -driver-print-jobs -target x86_64-apple-macosx10.9 %s -enable-experimental-cross-module-incremental-build 2>^1 | %FileCheck -check-prefix ENABLE %s + +// ENABLE: {{bin(/|\\\\)swift(-frontend|c)?(\.exe)?"?}} -frontend -merge-modules +// ENABLE-SAME: -enable-experimental-cross-module-incremental-build From a20118b54deebb2aa768294b19ef25de62339b23 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Thu, 24 Sep 2020 17:23:07 -0700 Subject: [PATCH 028/745] When building modules on secondary threads via RunSafelyOnThread, request 8 MB of stack --- lib/Frontend/ModuleInterfaceBuilder.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index 70259522380ea..63ad78f6ecefd 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -153,6 +153,7 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( auto outerPrettyStackState = llvm::SavePrettyStackState(); bool SubError = false; + static const size_t ThreadStackSize = 8 << 20; // 8 MB. bool RunSuccess = llvm::CrashRecoveryContext().RunSafelyOnThread([&] { // Pretend we're on the original thread for pretty-stack-trace purposes. auto savedInnerPrettyStackState = llvm::SavePrettyStackState(); @@ -265,7 +266,7 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( } return std::error_code(); }); - }); + }, ThreadStackSize); return !RunSuccess || SubError; } From 5eafc20cdd0ecebe30a2621f9e5f73b96de7c3a1 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Sat, 29 Aug 2020 01:31:24 -0700 Subject: [PATCH 029/745] Fix undefined behavior in SmallString.withUTF8 withUTF8 currently vends a typed UInt8 pointer to the underlying SmallString. That pointer type differs from SmallString's representation. It should simply vend a raw pointer, which would be both type safe and convenient for UTF8 data. However, since this method is already @inlinable, I added calls to bindMemory to prevent the optimizer from reasoning about access to the typed pointer that we vend. rdar://67983613 (Undefinied behavior in SmallString.withUTF8 is miscompiled) Additional commentary: SmallString creates a situation where there are two types, the in-memory type, (UInt64, UInt64), vs. the element type, UInt8. `UnsafePointer` specifies the in-memory type of the pointee, because that's how C works. If you want to specify an element type, not the in-memory type, then you need to use something other than UnsafePointer to view the memory. A trivial `BufferView` would be fine, although, frankly, I think UnsafeRawPointer is a perfectly good type on its own for UTF8 bytes. Unfortunately, a lot of the UTF8 helper code is ABI-exposed, so to work around this, we need to insert calls to bindMemory at strategic points to avoid undefined behavior. This is high-risk and can negatively affect performance. So far, I was able to resolve the regressions in our microbenchmarks just by tweaking the inliner. --- stdlib/public/core/SmallString.swift | 34 +++++++++++++++++++-------- stdlib/public/core/StringBridge.swift | 8 +++---- stdlib/public/core/StringGuts.swift | 4 +++- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/stdlib/public/core/SmallString.swift b/stdlib/public/core/SmallString.swift index 744962635a020..8ef19f5765228 100644 --- a/stdlib/public/core/SmallString.swift +++ b/stdlib/public/core/SmallString.swift @@ -197,11 +197,18 @@ extension _SmallString { internal func withUTF8( _ f: (UnsafeBufferPointer) throws -> Result ) rethrows -> Result { + let count = self.count var raw = self.zeroTerminatedRawCodeUnits - return try Swift.withUnsafeBytes(of: &raw) { rawBufPtr in - let ptr = rawBufPtr.baseAddress._unsafelyUnwrappedUnchecked - .assumingMemoryBound(to: UInt8.self) - return try f(UnsafeBufferPointer(start: ptr, count: self.count)) + return try Swift.withUnsafeBytes(of: &raw) { + let rawPtr = $0.baseAddress._unsafelyUnwrappedUnchecked + // Rebind the underlying (UInt64, UInt64) tuple to UInt8 for the + // duration of the closure. Accessing self after this rebind is undefined. + let ptr = rawPtr.bindMemory(to: UInt8.self, capacity: count) + defer { + // Restore the memory type of self._storage + _ = rawPtr.bindMemory(to: RawBitPattern.self, capacity: 1) + } + return try f(UnsafeBufferPointer(start: ptr, count: count)) } } @@ -209,14 +216,11 @@ extension _SmallString { // new count. @inline(__always) internal mutating func withMutableCapacity( - _ f: (UnsafeMutableBufferPointer) throws -> Int + _ f: (UnsafeMutableRawBufferPointer) throws -> Int ) rethrows { let len = try withUnsafeMutableBytes(of: &self._storage) { (rawBufPtr: UnsafeMutableRawBufferPointer) -> Int in - let ptr = rawBufPtr.baseAddress._unsafelyUnwrappedUnchecked - .assumingMemoryBound(to: UInt8.self) - return try f(UnsafeMutableBufferPointer( - start: ptr, count: _SmallString.capacity)) + return try f(rawBufPtr) } if len == 0 { self = _SmallString() @@ -273,7 +277,17 @@ extension _SmallString { ) rethrows { self.init() try self.withMutableCapacity { - return try initializer($0) + let capacity = $0.count + let rawPtr = $0.baseAddress._unsafelyUnwrappedUnchecked + // Rebind the underlying (UInt64, UInt64) tuple to UInt8 for the + // duration of the closure. Accessing self after this rebind is undefined. + let ptr = rawPtr.bindMemory(to: UInt8.self, capacity: capacity) + defer { + // Restore the memory type of self._storage + _ = rawPtr.bindMemory(to: RawBitPattern.self, capacity: 1) + } + return try initializer( + UnsafeMutableBufferPointer(start: ptr, count: capacity)) } self._invariantCheck() } diff --git a/stdlib/public/core/StringBridge.swift b/stdlib/public/core/StringBridge.swift index a24d721d0e630..dc67d1ce47243 100644 --- a/stdlib/public/core/StringBridge.swift +++ b/stdlib/public/core/StringBridge.swift @@ -169,7 +169,7 @@ internal func _cocoaStringSubscript( @_effects(releasenone) private func _NSStringCopyUTF8( _ o: _StringSelectorHolder, - into bufPtr: UnsafeMutableBufferPointer + into bufPtr: UnsafeMutableRawBufferPointer ) -> Int? { let ptr = bufPtr.baseAddress._unsafelyUnwrappedUnchecked let len = o.length @@ -193,7 +193,7 @@ private func _NSStringCopyUTF8( @_effects(releasenone) internal func _cocoaStringCopyUTF8( _ target: _CocoaString, - into bufPtr: UnsafeMutableBufferPointer + into bufPtr: UnsafeMutableRawBufferPointer ) -> Int? { return _NSStringCopyUTF8(_objc(target), into: bufPtr) } @@ -206,7 +206,7 @@ private func _NSStringUTF8Count( var remainingRange = _SwiftNSRange(location: 0, length: 0) var usedLen = 0 let success = 0 != o.getBytes( - UnsafeMutablePointer(Builtin.inttoptr_Word(0._builtinWordValue)), + UnsafeMutableRawPointer(Builtin.inttoptr_Word(0._builtinWordValue)), maxLength: 0, usedLength: &usedLen, encoding: _cocoaUTF8Encoding, @@ -340,7 +340,7 @@ internal enum _KnownCocoaString { @_effects(releasenone) // @opaque internal func _bridgeTagged( _ cocoa: _CocoaString, - intoUTF8 bufPtr: UnsafeMutableBufferPointer + intoUTF8 bufPtr: UnsafeMutableRawBufferPointer ) -> Int? { _internalInvariant(_isObjCTaggedPointer(cocoa)) return _cocoaStringCopyUTF8(cocoa, into: bufPtr) diff --git a/stdlib/public/core/StringGuts.swift b/stdlib/public/core/StringGuts.swift index 038cd5abda1ad..77c76d39858aa 100644 --- a/stdlib/public/core/StringGuts.swift +++ b/stdlib/public/core/StringGuts.swift @@ -250,7 +250,9 @@ extension _StringGuts { ) -> Int? { #if _runtime(_ObjC) // Currently, foreign means NSString - if let res = _cocoaStringCopyUTF8(_object.cocoaObject, into: mbp) { + if let res = _cocoaStringCopyUTF8(_object.cocoaObject, + into: UnsafeMutableRawBufferPointer(start: mbp.baseAddress, + count: mbp.count)) { return res } From 42bf92a216a802b68bd071fb3f91e854be121295 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Sat, 29 Aug 2020 13:14:58 -0700 Subject: [PATCH 030/745] Make bind_memory free to inline. bind_memory has no actual code size cost, and this is the only way to allow rebinding memory within critical standard library code like SmallString without regressing performance. --- lib/SILOptimizer/Utils/SILInliner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index b08f9832e0237..ef35d29e9cbca 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -688,6 +688,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::BaseAddrForOffsetInst: case SILInstructionKind::EndLifetimeInst: case SILInstructionKind::UncheckedOwnershipConversionInst: + case SILInstructionKind::BindMemoryInst: return InlineCost::Free; // Typed GEPs are free. @@ -792,7 +793,6 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::AllocRefDynamicInst: case SILInstructionKind::AllocStackInst: case SILInstructionKind::AllocValueBufferInst: - case SILInstructionKind::BindMemoryInst: case SILInstructionKind::BeginApplyInst: case SILInstructionKind::ValueMetatypeInst: case SILInstructionKind::WitnessMethodInst: From f1f5961e0f7c2aa359586b94da4aa55c34199bc8 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 24 Sep 2020 18:54:17 -0600 Subject: [PATCH 031/745] [NFC] Add Incremental Info Bits to ModuleDecl --- include/swift/AST/Decl.h | 7 +++++-- include/swift/AST/Module.h | 6 ++++++ lib/AST/Module.cpp | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 280e77580a15c..c68ad726f78b9 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -578,7 +578,7 @@ class alignas(1 << DeclAlignInBits) Decl { HasAnyUnavailableValues : 1 ); - SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1, /// If the module was or is being compiled with `-enable-testing`. TestingEnabled : 1, @@ -607,7 +607,10 @@ class alignas(1 << DeclAlignInBits) Decl { IsNonSwiftModule : 1, /// Whether this module is the main module. - IsMainModule : 1 + IsMainModule : 1, + + /// Whether this module has incremental dependency information available. + HasIncrementalInfo : 1 ); SWIFT_INLINE_BITFIELD(PrecedenceGroupDecl, Decl, 1+2, diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index 0d8fa3f1012be..7717bc02cfee3 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -517,6 +517,12 @@ class ModuleDecl : public DeclContext, public TypeDecl { Bits.ModuleDecl.RawResilienceStrategy = unsigned(strategy); } + /// Returns true if this module was or is being compiled for testing. + bool hasIncrementalInfo() const { return Bits.ModuleDecl.HasIncrementalInfo; } + void setHasIncrementalInfo(bool enabled = true) { + Bits.ModuleDecl.HasIncrementalInfo = enabled; + } + /// \returns true if this module is a system module; note that the StdLib is /// considered a system module. bool isSystemModule() const { diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index ee539e9ad01bd..135efbd415b97 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -484,6 +484,7 @@ ModuleDecl::ModuleDecl(Identifier name, ASTContext &ctx, Bits.ModuleDecl.IsSystemModule = 0; Bits.ModuleDecl.IsNonSwiftModule = 0; Bits.ModuleDecl.IsMainModule = 0; + Bits.ModuleDecl.HasIncrementalInfo = 0; } ArrayRef ModuleDecl::getImplicitImports() const { From 8e73d213a0ded811e53acf81fdb2f0dd9db08df8 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 24 Sep 2020 19:00:26 -0600 Subject: [PATCH 032/745] [NFC] Add Incremental Info Bits to ModuleFileSharedCore --- lib/Serialization/ModuleFile.h | 3 +++ lib/Serialization/ModuleFileSharedCore.h | 11 +++++++++-- lib/Serialization/SerializedModuleLoader.cpp | 2 ++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index baec8af6adcba..7749f82adee91 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -465,6 +465,9 @@ class ModuleFile return Core->Bits.IsImplicitDynamicEnabled; } + /// \c true if this module has incremental dependency information. + bool hasIncrementalInfo() const { return Core->hasIncrementalInfo(); } + /// Associates this module file with the AST node representing it. /// /// Checks that the file is compatible with the AST module it's being loaded diff --git a/lib/Serialization/ModuleFileSharedCore.h b/lib/Serialization/ModuleFileSharedCore.h index 17e60d85e687d..0bdceb68303f9 100644 --- a/lib/Serialization/ModuleFileSharedCore.h +++ b/lib/Serialization/ModuleFileSharedCore.h @@ -69,10 +69,13 @@ class ModuleFileSharedCore { /// The data blob containing all of the module's identifiers. StringRef IdentifierData; - // Full blob from the misc. version field of the metadata block. This should - // include the version string of the compiler that built the module. + /// Full blob from the misc. version field of the metadata block. This should + /// include the version string of the compiler that built the module. StringRef MiscVersion; + /// \c true if this module has incremental dependency information. + bool HasIncrementalInfo = false; + public: /// Represents another module that has been imported as a dependency. class Dependency { @@ -490,6 +493,10 @@ class ModuleFileSharedCore { ArrayRef getDependencies() const { return Dependencies; } + + /// Returns \c true if this module file contains a section with incremental + /// information. + bool hasIncrementalInfo() const { return HasIncrementalInfo; } }; template diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index b35b2f6561889..98e499b6aebba 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -714,6 +714,8 @@ LoadedFile *SerializedModuleLoaderBase::loadAST( M.setPrivateImportsEnabled(); if (loadedModuleFile->isImplicitDynamicEnabled()) M.setImplicitDynamicEnabled(); + if (loadedModuleFile->hasIncrementalInfo()) + M.setHasIncrementalInfo(); auto diagLocOrInvalid = diagLoc.getValueOr(SourceLoc()); loadInfo.status = From d2e7bdcfab5cd57e245e828840b7790d3115fa14 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 24 Sep 2020 19:22:39 -0600 Subject: [PATCH 033/745] Teach SwiftModules To Embed Incremental Information Take advantage of the binary swiftdeps serialization utliities built during #32131. Add a new optional information block to swiftdeps files. For now, don't actually serialize swiftdeps information. Frontends will use this information to determine whether to write incremental dependencies across modules into their swiftdeps files. We will then teach the driver to deserialize the data from this section and integrate it into its incremental decision making. --- .../swift/AST/FineGrainedDependencyFormat.h | 6 +++- include/swift/Subsystems.h | 10 ++++-- lib/AST/FineGrainedDependencyFormat.cpp | 31 +++++++++-------- lib/AST/FrontendSourceFileDepGraphFactory.cpp | 2 +- lib/Serialization/CMakeLists.txt | 1 + lib/Serialization/ModuleFileSharedCore.cpp | 11 ++++++ lib/Serialization/ModuleFormat.h | 7 +++- lib/Serialization/Serialization.cpp | 21 ++++++++---- lib/Serialization/Serialization.h | 17 ++++++++-- lib/Serialization/SerializeIncremental.cpp | 34 +++++++++++++++++++ .../swift-dependency-tool.cpp | 3 +- 11 files changed, 112 insertions(+), 31 deletions(-) create mode 100644 lib/Serialization/SerializeIncremental.cpp diff --git a/include/swift/AST/FineGrainedDependencyFormat.h b/include/swift/AST/FineGrainedDependencyFormat.h index a4d35695ceebb..d3b487a561628 100644 --- a/include/swift/AST/FineGrainedDependencyFormat.h +++ b/include/swift/AST/FineGrainedDependencyFormat.h @@ -124,7 +124,11 @@ bool readFineGrainedDependencyGraph(llvm::StringRef path, /// Tries to write the dependency graph to the given path name. /// Returns true if there was an error. -bool writeFineGrainedDependencyGraph(DiagnosticEngine &diags, llvm::StringRef path, +bool writeFineGrainedDependencyGraphToPath(DiagnosticEngine &diags, + llvm::StringRef path, + const SourceFileDepGraph &g); + +void writeFineGrainedDependencyGraph(llvm::BitstreamWriter &Out, const SourceFileDepGraph &g); } // namespace fine_grained_dependencies diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index 16348c0421b77..d93141e262ae6 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -75,6 +75,10 @@ namespace swift { class TypeConverter; } + namespace fine_grained_dependencies { + class SourceFileDepGraph; + } + /// @{ /// \returns true if the declaration should be verified. This can return @@ -174,8 +178,10 @@ namespace swift { using ModuleOrSourceFile = PointerUnion; /// Serializes a module or single source file to the given output file. - void serialize(ModuleOrSourceFile DC, const SerializationOptions &options, - const SILModule *M = nullptr); + void + serialize(ModuleOrSourceFile DC, const SerializationOptions &options, + const SILModule *M = nullptr, + const fine_grained_dependencies::SourceFileDepGraph *DG = nullptr); /// Serializes a module or single source file to the given output file and /// returns back the file's contents as a memory buffer. diff --git a/lib/AST/FineGrainedDependencyFormat.cpp b/lib/AST/FineGrainedDependencyFormat.cpp index b52cc2af98baf..291897804d5ab 100644 --- a/lib/AST/FineGrainedDependencyFormat.cpp +++ b/lib/AST/FineGrainedDependencyFormat.cpp @@ -289,16 +289,13 @@ class Serializer { unsigned LastIdentifierID = 0; std::vector IdentifiersToWrite; - SmallVector Buffer; - llvm::BitstreamWriter Out{Buffer}; + llvm::BitstreamWriter &Out; /// A reusable buffer for emitting records. SmallVector ScratchRecord; std::array AbbrCodes; - void writeFineGrainedDependencyGraph(const SourceFileDepGraph &g); - void addIdentifier(StringRef str); unsigned getIdentifier(StringRef str); @@ -321,8 +318,10 @@ class Serializer { void writeMetadata(); public: - void writeFineGrainedDependencyGraph(llvm::raw_ostream &os, - const SourceFileDepGraph &g); + Serializer(llvm::BitstreamWriter &ExistingOut) : Out(ExistingOut) {} + +public: + void writeFineGrainedDependencyGraph(const SourceFileDepGraph &g); }; } // end namespace @@ -467,20 +466,22 @@ unsigned Serializer::getIdentifier(StringRef str) { return iter->second; } -void Serializer::writeFineGrainedDependencyGraph(llvm::raw_ostream &os, - const SourceFileDepGraph &g) { - writeFineGrainedDependencyGraph(g); - os.write(Buffer.data(), Buffer.size()); - os.flush(); +void swift::fine_grained_dependencies::writeFineGrainedDependencyGraph( + llvm::BitstreamWriter &Out, const SourceFileDepGraph &g) { + Serializer serializer{Out}; + serializer.writeFineGrainedDependencyGraph(g); } -bool swift::fine_grained_dependencies::writeFineGrainedDependencyGraph( +bool swift::fine_grained_dependencies::writeFineGrainedDependencyGraphToPath( DiagnosticEngine &diags, StringRef path, const SourceFileDepGraph &g) { PrettyStackTraceStringAction stackTrace("saving fine-grained dependency graph", path); return withOutputFile(diags, path, [&](llvm::raw_ostream &out) { - Serializer serializer; - serializer.writeFineGrainedDependencyGraph(out, g); + SmallVector Buffer; + llvm::BitstreamWriter Writer{Buffer}; + writeFineGrainedDependencyGraph(Writer, g); + out.write(Buffer.data(), Buffer.size()); + out.flush(); return false; }); -} \ No newline at end of file +} diff --git a/lib/AST/FrontendSourceFileDepGraphFactory.cpp b/lib/AST/FrontendSourceFileDepGraphFactory.cpp index c6e75bc3aa7a9..33723a4e47452 100644 --- a/lib/AST/FrontendSourceFileDepGraphFactory.cpp +++ b/lib/AST/FrontendSourceFileDepGraphFactory.cpp @@ -251,7 +251,7 @@ bool fine_grained_dependencies::emitReferenceDependencies( SF, outputPath, depTracker, alsoEmitDotFile) .construct(); - bool hadError = writeFineGrainedDependencyGraph(diags, outputPath, g); + bool hadError = writeFineGrainedDependencyGraphToPath(diags, outputPath, g); // If path is stdout, cannot read it back, so check for "-" assert(outputPath == "-" || g.verifyReadsWhatIsWritten(outputPath)); diff --git a/lib/Serialization/CMakeLists.txt b/lib/Serialization/CMakeLists.txt index 9a795e3a9b64d..55a20aef848ea 100644 --- a/lib/Serialization/CMakeLists.txt +++ b/lib/Serialization/CMakeLists.txt @@ -8,6 +8,7 @@ add_swift_host_library(swiftSerialization STATIC SerializedModuleLoader.cpp SerializedSILLoader.cpp SerializeDoc.cpp + SerializeIncremental.cpp SerializeSIL.cpp LLVM_LINK_COMPONENTS diff --git a/lib/Serialization/ModuleFileSharedCore.cpp b/lib/Serialization/ModuleFileSharedCore.cpp index f72b3924923c6..2d7c4c5f249d4 100644 --- a/lib/Serialization/ModuleFileSharedCore.cpp +++ b/lib/Serialization/ModuleFileSharedCore.cpp @@ -1427,6 +1427,17 @@ ModuleFileSharedCore::ModuleFileSharedCore( break; } + case INCREMENTAL_INFORMATION_BLOCK_ID: { + HasIncrementalInfo = true; + // Skip incremental info if present. The Frontend currently doesn't do + // anything with this. + if (cursor.SkipBlock()) { + info.status = error(Status::Malformed); + return; + } + break; + } + default: // Unknown top-level block, possibly for use by a future version of the // module format. diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 9213caae40c06..0c37b5679e379 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -55,7 +55,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 578; // Remove hasNonPatternBindingInit +const uint16_t SWIFTMODULE_VERSION_MINOR = 579; // incremental deps info block /// A standard hash seed used for all string hashes in a serialized module. /// @@ -729,6 +729,11 @@ enum BlockID { /// /// \sa decl_locs_block DECL_LOCS_BLOCK_ID, + + /// The incremental dependency information block. + /// + /// This is part of a stable format and should not be renumbered. + INCREMENTAL_INFORMATION_BLOCK_ID = 196, }; /// The record types within the control block. diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 83173a1a8b8fa..1d5bb74a1e28e 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -5212,9 +5212,11 @@ SerializerBase::SerializerBase(ArrayRef signature, this->SF = DC.dyn_cast(); } -void Serializer::writeToStream(raw_ostream &os, ModuleOrSourceFile DC, - const SILModule *SILMod, - const SerializationOptions &options) { +void Serializer::writeToStream( + raw_ostream &os, ModuleOrSourceFile DC, + const SILModule *SILMod, + const SerializationOptions &options, + const fine_grained_dependencies::SourceFileDepGraph *DepGraph) { Serializer S{SWIFTMODULE_SIGNATURE, DC}; // FIXME: This is only really needed for debugging. We don't actually use it. @@ -5226,6 +5228,9 @@ void Serializer::writeToStream(raw_ostream &os, ModuleOrSourceFile DC, S.writeInputBlock(options); S.writeSIL(SILMod, options.SerializeAllSIL); S.writeAST(DC); + if (options.ExperimentalCrossModuleIncrementalInfo) { + S.writeIncrementalInfo(DepGraph); + } } S.writeToStream(os); @@ -5244,7 +5249,8 @@ void swift::serializeToBuffers( "Serialization, swiftmodule, to buffer"); llvm::SmallString<1024> buf; llvm::raw_svector_ostream stream(buf); - Serializer::writeToStream(stream, DC, M, options); + Serializer::writeToStream(stream, DC, M, options, + /*dependency info*/ nullptr); bool hadError = withOutputFile(getContext(DC).Diags, options.OutputPath, [&](raw_ostream &out) { @@ -5295,12 +5301,13 @@ void swift::serializeToBuffers( void swift::serialize(ModuleOrSourceFile DC, const SerializationOptions &options, - const SILModule *M) { + const SILModule *M, + const fine_grained_dependencies::SourceFileDepGraph *DG) { assert(!StringRef::withNullAsEmpty(options.OutputPath).empty()); if (StringRef(options.OutputPath) == "-") { // Special-case writing to stdout. - Serializer::writeToStream(llvm::outs(), DC, M, options); + Serializer::writeToStream(llvm::outs(), DC, M, options, DG); assert(StringRef::withNullAsEmpty(options.DocOutputPath).empty()); return; } @@ -5310,7 +5317,7 @@ void swift::serialize(ModuleOrSourceFile DC, [&](raw_ostream &out) { FrontendStatsTracer tracer(getContext(DC).Stats, "Serialization, swiftmodule"); - Serializer::writeToStream(out, DC, M, options); + Serializer::writeToStream(out, DC, M, options, DG); return false; }); if (hadError) diff --git a/lib/Serialization/Serialization.h b/lib/Serialization/Serialization.h index 8529620ae9957..757c55b159d0c 100644 --- a/lib/Serialization/Serialization.h +++ b/lib/Serialization/Serialization.h @@ -34,6 +34,10 @@ namespace clang { namespace swift { class SILModule; + namespace fine_grained_dependencies { + class SourceFileDepGraph; + } + namespace serialization { using FilenamesTy = ArrayRef; @@ -389,14 +393,21 @@ class Serializer : public SerializerBase { /// Top-level entry point for serializing a module. void writeAST(ModuleOrSourceFile DC); + /// Serializes the given dependnecy graph into the incremental information + /// section of this swift module. + void writeIncrementalInfo( + const fine_grained_dependencies::SourceFileDepGraph *DepGraph); + using SerializerBase::SerializerBase; using SerializerBase::writeToStream; public: /// Serialize a module to the given stream. - static void writeToStream(raw_ostream &os, ModuleOrSourceFile DC, - const SILModule *M, - const SerializationOptions &options); + static void + writeToStream(raw_ostream &os, ModuleOrSourceFile DC, + const SILModule *M, + const SerializationOptions &options, + const fine_grained_dependencies::SourceFileDepGraph *DepGraph); /// Records the use of the given Type. /// diff --git a/lib/Serialization/SerializeIncremental.cpp b/lib/Serialization/SerializeIncremental.cpp new file mode 100644 index 0000000000000..02eca8660acb4 --- /dev/null +++ b/lib/Serialization/SerializeIncremental.cpp @@ -0,0 +1,34 @@ +//===--- SerializeIncremental.cpp - Write incremental swiftdeps -----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "serialize-incremental" +#include "Serialization.h" +#include "swift/AST/FineGrainedDependencyFormat.h" + +#include + +using namespace swift; +using namespace swift::serialization; +using namespace llvm::support; +using llvm::BCBlockRAII; + +void Serializer::writeIncrementalInfo( + const fine_grained_dependencies::SourceFileDepGraph *DepGraph) { + if (!DepGraph) + return; + + { + BCBlockRAII restoreBlock(Out, INCREMENTAL_INFORMATION_BLOCK_ID, 5); + swift::fine_grained_dependencies::writeFineGrainedDependencyGraph( + Out, *DepGraph); + } +} diff --git a/tools/swift-dependency-tool/swift-dependency-tool.cpp b/tools/swift-dependency-tool/swift-dependency-tool.cpp index cc6f50c0f8b8f..c5b6096387c40 100644 --- a/tools/swift-dependency-tool/swift-dependency-tool.cpp +++ b/tools/swift-dependency-tool/swift-dependency-tool.cpp @@ -238,7 +238,8 @@ int main(int argc, char *argv[]) { return 1; } - if (writeFineGrainedDependencyGraph(diags, options::OutputFilename, fg)) { + if (writeFineGrainedDependencyGraphToPath( + diags, options::OutputFilename, fg)) { llvm::errs() << "Failed to write binary swiftdeps\n"; return 1; } From dfdebfaddf8cf18e999dae6eab24f3bf63714df0 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 24 Sep 2020 19:31:26 -0600 Subject: [PATCH 034/745] [NFC] Gate Cross-Module Dependency Sinks on Incremental Info This has the net effect of only recording cross-module dependency information in the current module if the module under scrutiny can possibly provide dependency information of its own. For now, because none of this is turned on, this does not actually record additional dependencies from extant modules. --- lib/AST/NameLookupRequests.cpp | 38 ++++++++++++++-------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/lib/AST/NameLookupRequests.cpp b/lib/AST/NameLookupRequests.cpp index 7f370ad229fd1..379a2748fd9dc 100644 --- a/lib/AST/NameLookupRequests.cpp +++ b/lib/AST/NameLookupRequests.cpp @@ -300,16 +300,15 @@ void DirectLookupRequest::writeDependencySink( void LookupInModuleRequest::writeDependencySink( evaluator::DependencyCollector &reqTracker, QualifiedLookupResult l) const { - auto *module = std::get<0>(getStorage()); + auto *DC = std::get<0>(getStorage()); auto member = std::get<1>(getStorage()); - auto *DC = std::get<4>(getStorage()); - // Decline to record lookups outside our module. - if (!DC->getParentSourceFile() || - module->getParentModule() != DC->getParentModule()) { - return; + // Decline to record lookups if the module in question has no incremental + // dependency information available. + auto *module = DC->getParentModule(); + if (module->isMainModule() || module->hasIncrementalInfo()) { + reqTracker.addTopLevelName(member.getBaseName()); } - reqTracker.addTopLevelName(member.getBaseName()); } //----------------------------------------------------------------------------// @@ -343,16 +342,14 @@ swift::extractNearestSourceLoc(const LookupConformanceDescriptor &desc) { void ModuleQualifiedLookupRequest::writeDependencySink( evaluator::DependencyCollector &reqTracker, QualifiedLookupResult l) const { - auto *DC = std::get<0>(getStorage()); auto *module = std::get<1>(getStorage()); auto member = std::get<2>(getStorage()); - // Decline to record lookups outside our module. - if (!DC->getParentSourceFile() || - module != DC->getModuleScopeContext()->getParentModule()) { - return; + // Decline to record lookups if the module in question has no incremental + // dependency information available. + if (module->isMainModule() || module->hasIncrementalInfo()) { + reqTracker.addTopLevelName(member.getBaseName()); } - reqTracker.addTopLevelName(member.getBaseName()); } //----------------------------------------------------------------------------// @@ -370,16 +367,13 @@ void LookupConformanceInModuleRequest::writeDependencySink( if (!Adoptee) return; - auto source = reqTracker.getRecorder().getActiveDependencySourceOrNull(); - if (source.isNull()) - return; - - // Decline to record conformances defined outside of the active module. + // Decline to record lookups if the module in question has no incremental + // dependency information available. auto *conformance = lookupResult.getConcrete(); - if (source.get()->getParentModule() != - conformance->getDeclContext()->getParentModule()) - return; - reqTracker.addPotentialMember(Adoptee); + auto *module = conformance->getDeclContext()->getParentModule(); + if (module->isMainModule() || module->hasIncrementalInfo()) { + reqTracker.addPotentialMember(Adoptee); + } } //----------------------------------------------------------------------------// From 3c2b376aa250b2ccfcd6a514ed33d286a99667b7 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 24 Sep 2020 19:57:24 -0600 Subject: [PATCH 035/745] [NFC] Refactor Reference Dependency Emission Invert the responsibility of the entrypoint so that FrontendTool is directing the actual serialization work. The entrypoint now solely exists to construct a dependency graph. While I'm here, prepare the way for serializing dependency graphs for modules by optimistically modeling a ModuleOrSourceFile input. --- include/swift/AST/FineGrainedDependencies.h | 13 +++--- lib/AST/FrontendSourceFileDepGraphFactory.cpp | 38 +++++++----------- lib/FrontendTool/FrontendTool.cpp | 40 ++++++++++++++++--- 3 files changed, 56 insertions(+), 35 deletions(-) diff --git a/include/swift/AST/FineGrainedDependencies.h b/include/swift/AST/FineGrainedDependencies.h index a73c026580c00..98c0aa51db6e4 100644 --- a/include/swift/AST/FineGrainedDependencies.h +++ b/include/swift/AST/FineGrainedDependencies.h @@ -59,11 +59,14 @@ namespace swift { class DependencyTracker; class DiagnosticEngine; class FrontendOptions; +class ModuleDecl; class SourceFile; /// Use a new namespace to help keep the experimental code from clashing. namespace fine_grained_dependencies { +class SourceFileDepGraph; + using StringVec = std::vector; template using ConstPtrVec = std::vector; @@ -343,11 +346,11 @@ class BiIndexedTwoStageMap { // MARK: Start of fine-grained-dependency-specific code //============================================================================== -/// The entry point into this system from the frontend: -/// Write out the .swiftdeps file for a frontend compilation of a primary file. -bool emitReferenceDependencies(DiagnosticEngine &diags, SourceFile *SF, - const DependencyTracker &depTracker, - StringRef outputPath, bool alsoEmitDotFile); +bool withReferenceDependencies( + llvm::PointerUnion MSF, + const DependencyTracker &depTracker, StringRef outputPath, + bool alsoEmitDotFile, llvm::function_ref); + //============================================================================== // MARK: Enums //============================================================================== diff --git a/lib/AST/FrontendSourceFileDepGraphFactory.cpp b/lib/AST/FrontendSourceFileDepGraphFactory.cpp index 33723a4e47452..1b3827df09449 100644 --- a/lib/AST/FrontendSourceFileDepGraphFactory.cpp +++ b/lib/AST/FrontendSourceFileDepGraphFactory.cpp @@ -236,30 +236,20 @@ std::string DependencyKey::computeNameForProvidedEntity< // MARK: Entry point into frontend graph construction //============================================================================== -bool fine_grained_dependencies::emitReferenceDependencies( - DiagnosticEngine &diags, SourceFile *const SF, - const DependencyTracker &depTracker, - StringRef outputPath, - const bool alsoEmitDotFile) { - - // Before writing to the dependencies file path, preserve any previous file - // that may have been there. No error handling -- this is just a nicety, it - // doesn't matter if it fails. - llvm::sys::fs::rename(outputPath, outputPath + "~"); - - SourceFileDepGraph g = FrontendSourceFileDepGraphFactory( - SF, outputPath, depTracker, alsoEmitDotFile) - .construct(); - - bool hadError = writeFineGrainedDependencyGraphToPath(diags, outputPath, g); - - // If path is stdout, cannot read it back, so check for "-" - assert(outputPath == "-" || g.verifyReadsWhatIsWritten(outputPath)); - - if (alsoEmitDotFile) - g.emitDotFile(outputPath, diags); - - return hadError; +bool fine_grained_dependencies::withReferenceDependencies( + llvm::PointerUnion MSF, + const DependencyTracker &depTracker, StringRef outputPath, + bool alsoEmitDotFile, + llvm::function_ref cont) { + if (MSF.dyn_cast()) { + llvm_unreachable("Cannot construct dependency graph for modules!"); + } else { + auto *SF = MSF.get(); + SourceFileDepGraph g = FrontendSourceFileDepGraphFactory( + SF, outputPath, depTracker, alsoEmitDotFile) + .construct(); + return cont(std::move(g)); + } } //============================================================================== diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index d75af390e817e..c6bc2e00f1eed 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -30,6 +30,7 @@ #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/FileSystem.h" #include "swift/AST/FineGrainedDependencies.h" +#include "swift/AST/FineGrainedDependencyFormat.h" #include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/IRGenRequests.h" @@ -1370,6 +1371,35 @@ static bool dumpAST(CompilerInstance &Instance) { return Instance.getASTContext().hadError(); } +static bool emitReferenceDependencies(CompilerInstance &Instance, + SourceFile *const SF, + StringRef outputPath) { + const auto alsoEmitDotFile = Instance.getInvocation() + .getLangOptions() + .EmitFineGrainedDependencySourcefileDotFiles; + + // Before writing to the dependencies file path, preserve any previous file + // that may have been there. No error handling -- this is just a nicety, it + // doesn't matter if it fails. + llvm::sys::fs::rename(outputPath, outputPath + "~"); + + using SourceFileDepGraph = fine_grained_dependencies::SourceFileDepGraph; + return fine_grained_dependencies::withReferenceDependencies( + SF, *Instance.getDependencyTracker(), outputPath, alsoEmitDotFile, + [&](SourceFileDepGraph &&g) -> bool { + const bool hadError = + fine_grained_dependencies::writeFineGrainedDependencyGraphToPath( + Instance.getDiags(), outputPath, g); + + // If path is stdout, cannot read it back, so check for "-" + assert(outputPath == "-" || g.verifyReadsWhatIsWritten(outputPath)); + + if (alsoEmitDotFile) + g.emitDotFile(outputPath, Instance.getDiags()); + return hadError; + }); +} + static void emitReferenceDependenciesForAllPrimaryInputsIfNeeded( CompilerInstance &Instance) { const auto &Invocation = Instance.getInvocation(); @@ -1384,13 +1414,11 @@ static void emitReferenceDependenciesForAllPrimaryInputsIfNeeded( const std::string &referenceDependenciesFilePath = Invocation.getReferenceDependenciesFilePathForPrimary( SF->getFilename()); - if (!referenceDependenciesFilePath.empty()) { - const auto LangOpts = Invocation.getLangOptions(); - (void)fine_grained_dependencies::emitReferenceDependencies( - Instance.getDiags(), SF, *Instance.getDependencyTracker(), - referenceDependenciesFilePath, - LangOpts.EmitFineGrainedDependencySourcefileDotFiles); + if (referenceDependenciesFilePath.empty()) { + continue; } + + emitReferenceDependencies(Instance, SF, referenceDependenciesFilePath); } } static void From 28294725d4c468d41da49c48f0323d494f314a03 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Fri, 18 Sep 2020 17:38:53 -0700 Subject: [PATCH 036/745] Change SIL ref_element_addr getFieldNo() to return a unique index. I don't have a test case for this bug based on the current code. But the fix is clearly needed to have a unique AccessStorage object for each property. The AccessPath commits will contain test cases for this functionality. --- include/swift/SIL/Projection.h | 7 +++-- include/swift/SIL/SILInstruction.h | 20 +++++++++++++++ lib/SIL/IR/SILInstructions.cpp | 41 ++++++++++++++++++++++++------ lib/SIL/Utils/MemAccessUtils.cpp | 2 +- lib/SIL/Utils/Projection.cpp | 4 +++ 5 files changed, 61 insertions(+), 13 deletions(-) diff --git a/include/swift/SIL/Projection.h b/include/swift/SIL/Projection.h index 0202e099d1caa..66f19e122531d 100644 --- a/include/swift/SIL/Projection.h +++ b/include/swift/SIL/Projection.h @@ -302,10 +302,9 @@ class Projection { assert(isValid()); assert((getKind() == ProjectionKind::Struct || getKind() == ProjectionKind::Class)); - assert(BaseType.getNominalOrBoundGenericNominal() && - "This should only be called with a nominal type"); - auto *NDecl = BaseType.getNominalOrBoundGenericNominal(); - return NDecl->getStoredProperties()[getIndex()]; + auto *nominalDecl = BaseType.getNominalOrBoundGenericNominal(); + assert(nominalDecl && "This should only be called with a nominal type"); + return getIndexedField(nominalDecl, getIndex()); } EnumElementDecl *getEnumElementDecl(SILType BaseType) const { diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 6ad16592afbc2..f61c915dba139 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -5757,6 +5757,26 @@ class TupleElementAddrInst } }; +/// Get a unique index for a struct or class field in layout order. +/// +/// Precondition: \p decl must be a non-resilient struct or class. +/// +/// Precondition: \p field must be a stored property declared in \p decl, +/// not in a superclass. +/// +/// Postcondition: The returned index is unique across all properties in the +/// object, including properties declared in a superclass. +unsigned getFieldIndex(NominalTypeDecl *decl, VarDecl *property); + +/// Get the property for a struct or class by its unique index. +/// +/// Precondition: \p decl must be a non-resilient struct or class. +/// +/// Precondition: \p index must be the index of a stored property +/// (as returned by getFieldIndex()) which is declared +/// in \p decl, not in a superclass. +VarDecl *getIndexedField(NominalTypeDecl *decl, unsigned index); + /// A common base for instructions that require a cached field index. /// /// "Field" is a term used here to refer to the ordered, accessible stored diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index bef2b17c534bc..392e75baf021b 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1323,18 +1323,43 @@ bool TupleExtractInst::isEltOnlyNonTrivialElt() const { return true; } -unsigned FieldIndexCacheBase::cacheFieldIndex() { - unsigned i = 0; - for (VarDecl *property : getParentDecl()->getStoredProperties()) { +/// Get a unique index for a struct or class field in layout order. +unsigned swift::getFieldIndex(NominalTypeDecl *decl, VarDecl *field) { + unsigned index = 0; + if (auto *classDecl = dyn_cast(decl)) { + for (auto *superDecl = classDecl->getSuperclassDecl(); superDecl != nullptr; + superDecl = superDecl->getSuperclassDecl()) { + index += superDecl->getStoredProperties().size(); + } + } + for (VarDecl *property : decl->getStoredProperties()) { if (field == property) { - SILInstruction::Bits.FieldIndexCacheBase.FieldIndex = i; - return i; + return index; } - ++i; + ++index; } llvm_unreachable("The field decl for a struct_extract, struct_element_addr, " - "or ref_element_addr must be an accessible stored property " - "of the operand's type"); + "or ref_element_addr must be an accessible stored " + "property of the operand type"); +} + +/// Get the property for a struct or class by its unique index. +VarDecl *swift::getIndexedField(NominalTypeDecl *decl, unsigned index) { + if (auto *classDecl = dyn_cast(decl)) { + for (auto *superDecl = classDecl->getSuperclassDecl(); superDecl != nullptr; + superDecl = superDecl->getSuperclassDecl()) { + assert(index > superDecl->getStoredProperties().size() + && "field index cannot refer to a superclass field"); + index -= superDecl->getStoredProperties().size(); + } + } + return decl->getStoredProperties()[index]; +} + +unsigned FieldIndexCacheBase::cacheFieldIndex() { + unsigned index = getFieldIndex(getParentDecl(), getField()); + SILInstruction::Bits.FieldIndexCacheBase.FieldIndex = index; + return index; } // FIXME: this should be cached during cacheFieldIndex(). diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index dedba04a24ce1..1d0b0bd1c9059 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -156,7 +156,7 @@ const ValueDecl *AccessedStorage::getDecl() const { case Class: { auto *decl = getObject()->getType().getNominalOrBoundGenericNominal(); - return decl->getStoredProperties()[getPropertyIndex()]; + return getIndexedField(decl, getPropertyIndex()); } case Tail: return nullptr; diff --git a/lib/SIL/Utils/Projection.cpp b/lib/SIL/Utils/Projection.cpp index 31bebe00aa415..01c79c10ccbfa 100644 --- a/lib/SIL/Utils/Projection.cpp +++ b/lib/SIL/Utils/Projection.cpp @@ -321,6 +321,10 @@ void Projection::getFirstLevelProjections( if (auto *C = Ty.getClassOrBoundGenericClass()) { unsigned Count = 0; + for (auto *superDecl = C->getSuperclassDecl(); superDecl != nullptr; + superDecl = superDecl->getSuperclassDecl()) { + Count += superDecl->getStoredProperties().size(); + } for (auto *VDecl : C->getStoredProperties()) { (void) VDecl; Projection P(ProjectionKind::Class, Count++); From be2f85c1022747987c9243593c9be35131b5034b Mon Sep 17 00:00:00 2001 From: Zoe Carver Date: Thu, 24 Sep 2020 20:08:19 -0700 Subject: [PATCH 037/745] [cxx-interop] [NFC] Rename MemberInline.IntBox -> LoadableIntWrapper. (#33930) --- test/Interop/Cxx/operators/Inputs/member-inline.h | 6 ++++-- .../Cxx/operators/member-inline-irgen.swift | 6 +++--- .../operators/member-inline-module-interface.swift | 4 ++-- .../Cxx/operators/member-inline-silgen.swift | 14 +++++++------- .../Cxx/operators/member-inline-typechecker.swift | 4 ++-- test/Interop/Cxx/operators/member-inline.swift | 6 +++--- 6 files changed, 21 insertions(+), 19 deletions(-) diff --git a/test/Interop/Cxx/operators/Inputs/member-inline.h b/test/Interop/Cxx/operators/Inputs/member-inline.h index 4333f6ef4cb88..0ec5f164ea246 100644 --- a/test/Interop/Cxx/operators/Inputs/member-inline.h +++ b/test/Interop/Cxx/operators/Inputs/member-inline.h @@ -1,9 +1,11 @@ #ifndef TEST_INTEROP_CXX_OPERATORS_INPUTS_MEMBER_INLINE_H #define TEST_INTEROP_CXX_OPERATORS_INPUTS_MEMBER_INLINE_H -struct IntBox { +struct LoadableIntWrapper { int value; - IntBox operator-(IntBox rhs) { return IntBox{.value = value - rhs.value}; } + LoadableIntWrapper operator-(LoadableIntWrapper rhs) { + return LoadableIntWrapper{.value = value - rhs.value}; + } }; #endif diff --git a/test/Interop/Cxx/operators/member-inline-irgen.swift b/test/Interop/Cxx/operators/member-inline-irgen.swift index 169c10d280f06..7395e06f39504 100644 --- a/test/Interop/Cxx/operators/member-inline-irgen.swift +++ b/test/Interop/Cxx/operators/member-inline-irgen.swift @@ -5,7 +5,7 @@ import MemberInline -public func sub(_ lhs: inout IntBox, _ rhs: IntBox) -> IntBox { lhs - rhs } +public func sub(_ lhs: inout LoadableIntWrapper, _ rhs: LoadableIntWrapper) -> LoadableIntWrapper { lhs - rhs } -// CHECK: call [[RES:i32|i64]] [[NAME:@(_ZN6IntBoxmiES_|"\?\?GIntBox@@QEAA\?AU0@U0@@Z")]](%struct.IntBox* {{%[0-9]+}}, {{i32|\[1 x i32\]|i64|%struct.IntBox\* byval align 4}} {{%[0-9]+}}) -// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.IntBox* %this, {{i32 %rhs.coerce|\[1 x i32\] %rhs.coerce|i64 %rhs.coerce|%struct.IntBox\* byval\(%struct.IntBox\) align 4 %rhs}}) +// CHECK: call [[RES:i32|i64]] [[NAME:@(_ZN18LoadableIntWrappermiES_|"\?\?GLoadableIntWrapper@@QEAA\?AU0@U0@@Z")]](%struct.LoadableIntWrapper* {{%[0-9]+}}, {{i32|\[1 x i32\]|i64|%struct.LoadableIntWrapper\* byval align 4}} {{%[0-9]+}}) +// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.LoadableIntWrapper* %this, {{i32 %rhs.coerce|\[1 x i32\] %rhs.coerce|i64 %rhs.coerce|%struct.LoadableIntWrapper\* byval\(%struct.LoadableIntWrapper\) align 4 %rhs}}) diff --git a/test/Interop/Cxx/operators/member-inline-module-interface.swift b/test/Interop/Cxx/operators/member-inline-module-interface.swift index b90893ac11e32..c6781ef101caa 100644 --- a/test/Interop/Cxx/operators/member-inline-module-interface.swift +++ b/test/Interop/Cxx/operators/member-inline-module-interface.swift @@ -1,5 +1,5 @@ // RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s -// CHECK: struct IntBox { -// CHECK: static func - (lhs: inout IntBox, rhs: IntBox) -> IntBox +// CHECK: struct LoadableIntWrapper { +// CHECK: static func - (lhs: inout LoadableIntWrapper, rhs: LoadableIntWrapper) -> LoadableIntWrapper // CHECK: } diff --git a/test/Interop/Cxx/operators/member-inline-silgen.swift b/test/Interop/Cxx/operators/member-inline-silgen.swift index 3b88723d43149..c9e3c982971a1 100644 --- a/test/Interop/Cxx/operators/member-inline-silgen.swift +++ b/test/Interop/Cxx/operators/member-inline-silgen.swift @@ -2,13 +2,13 @@ import MemberInline -public func sub(_ lhs: inout IntBox, _ rhs: IntBox) -> IntBox { lhs - rhs } +public func sub(_ lhs: inout LoadableIntWrapper, _ rhs: LoadableIntWrapper) -> LoadableIntWrapper { lhs - rhs } -// CHECK: bb0([[SELF:%.*]] : $*IntBox, [[RHS:%.*]] : $IntBox): +// CHECK: bb0([[SELF:%.*]] : $*LoadableIntWrapper, [[RHS:%.*]] : $LoadableIntWrapper): -// CHECK: [[SELFACCESS:%.*]] = begin_access [modify] [static] [[SELF]] : $*IntBox -// CHECK: [[OP:%.*]] = function_ref [[NAME:@(_ZN6IntBoxmiES_|\?\?GIntBox@@QEAA\?AU0@U0@@Z)]] : $@convention(c) (@inout IntBox, IntBox) -> IntBox -// CHECK: apply [[OP]]([[SELFACCESS]], [[RHS]]) : $@convention(c) (@inout IntBox, IntBox) -> IntBox -// CHECK: end_access [[SELFACCESS]] : $*IntBox +// CHECK: [[SELFACCESS:%.*]] = begin_access [modify] [static] [[SELF]] : $*LoadableIntWrapper +// CHECK: [[OP:%.*]] = function_ref [[NAME:@(_ZN18LoadableIntWrappermiES_|\?\?GLoadableIntWrapper@@QEAA\?AU0@U0@@Z)]] : $@convention(c) (@inout LoadableIntWrapper, LoadableIntWrapper) -> LoadableIntWrapper +// CHECK: apply [[OP]]([[SELFACCESS]], [[RHS]]) : $@convention(c) (@inout LoadableIntWrapper, LoadableIntWrapper) -> LoadableIntWrapper +// CHECK: end_access [[SELFACCESS]] : $*LoadableIntWrapper -// CHECK: sil [clang IntBox."-"] [[NAME]] : $@convention(c) (@inout IntBox, IntBox) -> IntBox +// CHECK: sil [clang LoadableIntWrapper."-"] [[NAME]] : $@convention(c) (@inout LoadableIntWrapper, LoadableIntWrapper) -> LoadableIntWrapper diff --git a/test/Interop/Cxx/operators/member-inline-typechecker.swift b/test/Interop/Cxx/operators/member-inline-typechecker.swift index 3b1989865d605..d94c545884c40 100644 --- a/test/Interop/Cxx/operators/member-inline-typechecker.swift +++ b/test/Interop/Cxx/operators/member-inline-typechecker.swift @@ -2,7 +2,7 @@ import MemberInline -var lhs = IntBox(value: 42) -let rhs = IntBox(value: 23) +var lhs = LoadableIntWrapper(value: 42) +let rhs = LoadableIntWrapper(value: 23) let resultPlus = lhs - rhs diff --git a/test/Interop/Cxx/operators/member-inline.swift b/test/Interop/Cxx/operators/member-inline.swift index f3b1e29e26377..67b2f801d0398 100644 --- a/test/Interop/Cxx/operators/member-inline.swift +++ b/test/Interop/Cxx/operators/member-inline.swift @@ -10,9 +10,9 @@ import StdlibUnittest var OperatorsTestSuite = TestSuite("Operators") -OperatorsTestSuite.test("plus") { - var lhs = IntBox(value: 42) - let rhs = IntBox(value: 23) +OperatorsTestSuite.test("LoadableIntWrapper.plus") { + var lhs = LoadableIntWrapper(value: 42) + let rhs = LoadableIntWrapper(value: 23) let result = lhs - rhs From 6fe5245899d6613e0d7e35a1ee539d8a84114678 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 24 Sep 2020 20:56:59 -0700 Subject: [PATCH 038/745] Fix grammatical error in a new comment --- include/swift/AST/Decl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 2e63ee9d7e64c..b7e5e4e1928d0 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5948,7 +5948,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// Returns true if the function is an @asyncHandler. bool isAsyncHandler() const; - /// Returns true if the function if the signature matches the form of an + /// Returns true if the function signature matches the form of an /// @asyncHandler. bool canBeAsyncHandler() const; From ef972eb34de9b540971ebfd4d6b2718ad32931cb Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Thu, 24 Sep 2020 20:59:28 -0700 Subject: [PATCH 039/745] [ownership] Move OME after CopyForwarding (#33106) * Move OME after CopyForwarding * Minor fix in CopyForwarding test --- lib/SILOptimizer/PassManager/PassPipeline.cpp | 9 +++++---- test/SILOptimizer/copyforward_ossa.sil | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index fda5b5fe7f589..f93042ad18bcb 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -283,6 +283,10 @@ void addFunctionPasses(SILPassPipelinePlan &P, // splits up copy_addr. P.addCopyForwarding(); + // We earlier eliminated ownership if we are not compiling the stdlib. Now + // handle the stdlib functions. + P.addNonTransparentFunctionOwnershipModelEliminator(); + // Optimize copies from a temporary (an "l-value") to a destination. P.addTempLValueOpt(); @@ -489,10 +493,7 @@ static void addHighLevelFunctionPipeline(SILPassPipelinePlan &P) { // FIXME: update EagerSpecializer to be a function pass! P.addEagerSpecializer(); - // We earlier eliminated ownership if we are not compiling the stdlib. Now - // handle the stdlib functions. - P.addNonTransparentFunctionOwnershipModelEliminator(); - + // stdlib ownership model elimination is done within addFunctionPasses addFunctionPasses(P, OptimizationLevelKind::HighLevel); addHighLevelLoopOptPasses(P); diff --git a/test/SILOptimizer/copyforward_ossa.sil b/test/SILOptimizer/copyforward_ossa.sil index 9719fc0ec92cc..11be2a5140875 100644 --- a/test/SILOptimizer/copyforward_ossa.sil +++ b/test/SILOptimizer/copyforward_ossa.sil @@ -265,7 +265,7 @@ sil [ossa] @_TFSq4someU__fMGSqQ__FQ_GSqQ__ : $@convention(thin) <τ_0_0> (@in τ sil [ossa] @_TFsoi2neU__FTGSqQ__Vs26_OptionalNilComparisonType_Sb : $@convention(thin) <τ_0_0> (@in Optional<τ_0_0>, _OptionalNilComparisonType) -> Bool -// CHECK-LABEL: sil hidden [ossa] @option_init +// CHECK-LABEL: sil hidden [ossa] @option_init : // CHECK: alloc_stack // CHECK: alloc_stack // CHECK: alloc_stack From 76cd4bf16079909137ae6d8542f0ebe15f295cbf Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 24 Sep 2020 23:25:47 -0600 Subject: [PATCH 040/745] [NFC] Differential Incremental External Dependencies in DependencyTracker Also perform the plumbing necessary to convince the rest of the compiler that they're just ordinary external dependencies. In particular, we will still emit these depenencies into .d files, and code completion will still index them. --- include/swift/AST/ModuleLoader.h | 13 +++++++++++++ include/swift/Basic/Statistics.def | 1 + lib/AST/ModuleLoader.cpp | 11 +++++++++++ lib/FrontendTool/FrontendTool.cpp | 7 +++++++ lib/IDE/CompletionInstance.cpp | 4 ++++ 5 files changed, 36 insertions(+) diff --git a/include/swift/AST/ModuleLoader.h b/include/swift/AST/ModuleLoader.h index 2cee4a7970209..9e5e836bac514 100644 --- a/include/swift/AST/ModuleLoader.h +++ b/include/swift/AST/ModuleLoader.h @@ -75,6 +75,9 @@ enum class IntermoduleDepTrackingMode { /// implemented in terms of a wrapped clang::DependencyCollector. class DependencyTracker { std::shared_ptr clangCollector; + SmallVector incrementalDeps; + llvm::StringSet<> incrementalDepsUniquer; + public: explicit DependencyTracker( IntermoduleDepTrackingMode Mode, @@ -87,9 +90,19 @@ class DependencyTracker { /// No path canonicalization is done. void addDependency(StringRef File, bool IsSystem); + /// Adds a file as an incremental dependency. + /// + /// No additional canonicalization or adulteration of the file path in + /// \p File is performed. + void addIncrementalDependency(StringRef File); + /// Fetches the list of dependencies. ArrayRef getDependencies() const; + /// Fetches the list of dependencies that are known to have incremental swift + /// dependency information embedded inside of them. + ArrayRef getIncrementalDependencies() const; + /// Return the underlying clang::DependencyCollector that this /// class wraps. std::shared_ptr getClangCollector(); diff --git a/include/swift/Basic/Statistics.def b/include/swift/Basic/Statistics.def index 419567fc8bba5..c7fdc1071ef99 100644 --- a/include/swift/Basic/Statistics.def +++ b/include/swift/Basic/Statistics.def @@ -114,6 +114,7 @@ FRONTEND_STATISTIC(AST, NumASTBytesAllocated) /// Number of file-level dependencies of this frontend job, as tracked in the /// AST context's dependency collector. FRONTEND_STATISTIC(AST, NumDependencies) +FRONTEND_STATISTIC(AST, NumIncrementalDependencies) /// Number of top-level, dynamic, and member names referenced in this frontend /// job's source file, as tracked by the AST context's referenced-name tracker. diff --git a/lib/AST/ModuleLoader.cpp b/lib/AST/ModuleLoader.cpp index 851cbdba9e254..0a62cbd1c07c2 100644 --- a/lib/AST/ModuleLoader.cpp +++ b/lib/AST/ModuleLoader.cpp @@ -50,11 +50,22 @@ DependencyTracker::addDependency(StringRef File, bool IsSystem) { /*IsMissing=*/false); } +void DependencyTracker::addIncrementalDependency(StringRef File) { + if (incrementalDepsUniquer.insert(File).second) { + incrementalDeps.emplace_back(File.str()); + } +} + ArrayRef DependencyTracker::getDependencies() const { return clangCollector->getDependencies(); } +ArrayRef +DependencyTracker::getIncrementalDependencies() const { + return incrementalDeps; +} + std::shared_ptr DependencyTracker::getClangCollector() { return clangCollector; diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index c6bc2e00f1eed..20dcce758b28a 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -209,6 +209,12 @@ static bool emitMakeDependenciesIfNeeded(DiagnosticEngine &diags, dependencyString.push_back(' '); dependencyString.append(frontend::utils::escapeForMake(path, buffer).str()); } + auto incrementalDependencyPaths = + reversePathSortedFilenames(depTracker->getIncrementalDependencies()); + for (auto const &path : incrementalDependencyPaths) { + dependencyString.push_back(' '); + dependencyString.append(frontend::utils::escapeForMake(path, buffer).str()); + } // FIXME: Xcode can't currently handle multiple targets in a single // dependency line. @@ -1176,6 +1182,7 @@ static void countASTStats(UnifiedStatsReporter &Stats, if (auto *D = Instance.getDependencyTracker()) { C.NumDependencies = D->getDependencies().size(); + C.NumIncrementalDependencies = D->getIncrementalDependencies().size(); } for (auto SF : Instance.getPrimarySourceFiles()) { diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 2c2babd019b7c..9c4ba9940e0e1 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -198,6 +198,10 @@ forEachDependencyUntilTrue(CompilerInstance &CI, unsigned excludeBufferID, if (callback(dep)) return true; } + for (auto &dep : CI.getDependencyTracker()->getIncrementalDependencies()) { + if (callback(dep)) + return true; + } return false; } From 1b01f213830ab7ce351c83c91721634ee41aa394 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 24 Sep 2020 23:26:00 -0600 Subject: [PATCH 041/745] [Gardening] Add a spot of documentation about withReferenceDependencies --- include/swift/AST/FineGrainedDependencies.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/swift/AST/FineGrainedDependencies.h b/include/swift/AST/FineGrainedDependencies.h index 98c0aa51db6e4..9fa481f430b5b 100644 --- a/include/swift/AST/FineGrainedDependencies.h +++ b/include/swift/AST/FineGrainedDependencies.h @@ -346,6 +346,10 @@ class BiIndexedTwoStageMap { // MARK: Start of fine-grained-dependency-specific code //============================================================================== +/// Uses the provided module or source file to construct a dependency graph, +/// which is provided back to the caller in the continuation callback. +/// +/// \Note The returned graph should not be escaped from the callback. bool withReferenceDependencies( llvm::PointerUnion MSF, const DependencyTracker &depTracker, StringRef outputPath, From 044f85ad9c48aac43affa6c91686292d9a3d4bde Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 24 Sep 2020 23:27:11 -0600 Subject: [PATCH 042/745] Add ModuleDepGraphFactory This completes the missing piece from #34073 and means the frontend may now serialize moduledecl-based dependencies. --- lib/AST/FrontendSourceFileDepGraphFactory.cpp | 78 ++++++++++++++++++- lib/AST/FrontendSourceFileDepGraphFactory.h | 24 ++++++ lib/FrontendTool/FrontendTool.cpp | 18 ++++- 3 files changed, 117 insertions(+), 3 deletions(-) diff --git a/lib/AST/FrontendSourceFileDepGraphFactory.cpp b/lib/AST/FrontendSourceFileDepGraphFactory.cpp index 1b3827df09449..6439266e908f5 100644 --- a/lib/AST/FrontendSourceFileDepGraphFactory.cpp +++ b/lib/AST/FrontendSourceFileDepGraphFactory.cpp @@ -241,8 +241,10 @@ bool fine_grained_dependencies::withReferenceDependencies( const DependencyTracker &depTracker, StringRef outputPath, bool alsoEmitDotFile, llvm::function_ref cont) { - if (MSF.dyn_cast()) { - llvm_unreachable("Cannot construct dependency graph for modules!"); + if (auto *MD = MSF.dyn_cast()) { + SourceFileDepGraph g = + ModuleDepGraphFactory(MD, alsoEmitDotFile).construct(); + return cont(std::move(g)); } else { auto *SF = MSF.get(); SourceFileDepGraph g = FrontendSourceFileDepGraphFactory( @@ -632,3 +634,75 @@ FrontendSourceFileDepGraphFactory::getFingerprintIfAny(const Decl *d) { } return None; } + +//============================================================================== +// MARK: ModuleDepGraphFactory +//============================================================================== + +ModuleDepGraphFactory::ModuleDepGraphFactory(ModuleDecl *Mod, bool emitDot) + : AbstractSourceFileDepGraphFactory( + /*include private*/ true, Mod->getASTContext().hadError(), + Mod->getNameStr(), "0xBADBEEF", emitDot, Mod->getASTContext().Diags), + Mod(Mod) {} + +void ModuleDepGraphFactory::addAllDefinedDecls() { + // TODO: express the multiple provides and depends streams with variadic + // templates + + // Many kinds of Decls become top-level depends. + + SmallVector TopLevelDecls; + Mod->getTopLevelDecls(TopLevelDecls); + DeclFinder declFinder(TopLevelDecls, includePrivateDeps, + [this](VisibleDeclConsumer &consumer) { + return Mod->lookupClassMembers({}, consumer); + }); + + addAllDefinedDeclsOfAGivenType( + declFinder.precedenceGroups); + addAllDefinedDeclsOfAGivenType( + declFinder.memberOperatorDecls); + addAllDefinedDeclsOfAGivenType(declFinder.operators); + addAllDefinedDeclsOfAGivenType(declFinder.topNominals); + addAllDefinedDeclsOfAGivenType(declFinder.topValues); + addAllDefinedDeclsOfAGivenType(declFinder.allNominals); + addAllDefinedDeclsOfAGivenType( + declFinder.potentialMemberHolders); + addAllDefinedDeclsOfAGivenType( + declFinder.valuesInExtensions); + addAllDefinedDeclsOfAGivenType( + declFinder.classMembers); +} + +/// Given an array of Decls or pairs of them in \p declsOrPairs +/// create node pairs for context and name +template +void ModuleDepGraphFactory::addAllDefinedDeclsOfAGivenType( + std::vector &contentsVec) { + for (const auto &declOrPair : contentsVec) { + Optional fp = getFingerprintIfAny(declOrPair); + addADefinedDecl( + DependencyKey::createForProvidedEntityInterface(declOrPair), + fp ? StringRef(fp.getValue()) : Optional()); + } +} + +//============================================================================== +// MARK: ModuleDepGraphFactory - adding individual defined Decls +//============================================================================== + +/// At present, only \c NominalTypeDecls have (body) fingerprints +Optional ModuleDepGraphFactory::getFingerprintIfAny( + std::pair) { + return None; +} +Optional +ModuleDepGraphFactory::getFingerprintIfAny(const Decl *d) { + if (const auto *idc = dyn_cast(d)) { + auto result = idc->getBodyFingerprint(); + assert((!result || !result->empty()) && + "Fingerprint should never be empty"); + return result; + } + return None; +} diff --git a/lib/AST/FrontendSourceFileDepGraphFactory.h b/lib/AST/FrontendSourceFileDepGraphFactory.h index 496c458412732..898357f72117f 100644 --- a/lib/AST/FrontendSourceFileDepGraphFactory.h +++ b/lib/AST/FrontendSourceFileDepGraphFactory.h @@ -54,6 +54,30 @@ class FrontendSourceFileDepGraphFactory static Optional getFingerprintIfAny(const Decl *d); }; +class ModuleDepGraphFactory : public AbstractSourceFileDepGraphFactory { + ModuleDecl *const Mod; + +public: + ModuleDepGraphFactory(ModuleDecl *Mod, bool emitDot); + + ~ModuleDepGraphFactory() override = default; + +private: + void addAllDefinedDecls() override; + void addAllUsedDecls() override {} + + /// Given an array of Decls or pairs of them in \p declsOrPairs + /// create node pairs for context and name + template + void addAllDefinedDeclsOfAGivenType(std::vector &contentsVec); + + /// At present, only nominals, protocols, and extensions have (body) + /// fingerprints + static Optional getFingerprintIfAny( + std::pair); + static Optional getFingerprintIfAny(const Decl *d); +}; + } // namespace fine_grained_dependencies } // namespace swift diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 20dcce758b28a..19973d3328947 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -2292,7 +2292,23 @@ static bool performCompileStepsPostSILGen(CompilerInstance &Instance, SerializationOptions serializationOpts = Invocation.computeSerializationOptions(outs, Instance.getMainModule()); - serialize(MSF, serializationOpts, SM.get()); + if (serializationOpts.ExperimentalCrossModuleIncrementalInfo) { + const auto alsoEmitDotFile = + Instance.getInvocation() + .getLangOptions() + .EmitFineGrainedDependencySourcefileDotFiles; + + using SourceFileDepGraph = fine_grained_dependencies::SourceFileDepGraph; + auto *Mod = MSF.get(); + fine_grained_dependencies::withReferenceDependencies( + Mod, *Instance.getDependencyTracker(), Mod->getModuleFilename(), + alsoEmitDotFile, [](SourceFileDepGraph &&g) { + serialize(MSF, serializationOpts, SM.get(), &g); + return false; + }); + } else { + serialize(MSF, serializationOpts, SM.get()); + } }; // Set the serialization action, so that the SIL module From 5ae231eaabc96939c0141c5388617df84018ca31 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 24 Sep 2020 22:40:48 -0700 Subject: [PATCH 043/745] Rename getFieldNo() to getFieldIndex(). Do I really need to justify this? --- include/swift/SIL/PatternMatch.h | 2 +- include/swift/SIL/Projection.h | 10 +++++----- include/swift/SIL/SILCloner.h | 4 ++-- include/swift/SIL/SILInstruction.h | 7 +++---- lib/IRGen/IRGenSIL.cpp | 4 ++-- lib/SIL/IR/SILGlobalVariable.cpp | 2 +- lib/SIL/IR/SILInstruction.cpp | 4 ++-- lib/SIL/IR/SILInstructions.cpp | 8 ++++---- lib/SIL/IR/SILPrinter.cpp | 4 ++-- lib/SIL/Utils/MemAccessUtils.cpp | 2 +- lib/SIL/Utils/Projection.cpp | 20 +++++++++---------- lib/SIL/Verifier/MemoryLifetime.cpp | 4 ++-- lib/SIL/Verifier/SILVerifier.cpp | 10 +++++----- lib/SILOptimizer/Analysis/ARCAnalysis.cpp | 4 ++-- .../Analysis/AccessSummaryAnalysis.cpp | 4 ++-- lib/SILOptimizer/Analysis/ArraySemantic.cpp | 2 +- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 4 ++-- .../Analysis/SimplifyInstruction.cpp | 8 ++++---- lib/SILOptimizer/Analysis/ValueTracking.cpp | 2 +- .../Differentiation/JVPCloner.cpp | 4 ++-- .../Differentiation/PullbackCloner.cpp | 4 ++-- lib/SILOptimizer/IPO/GlobalOpt.cpp | 2 +- lib/SILOptimizer/LoopTransforms/LICM.cpp | 2 +- .../Mandatory/AddressLowering.cpp | 4 ++-- .../Mandatory/DIMemoryUseCollector.cpp | 2 +- .../Mandatory/PredictableMemOpt.cpp | 2 +- .../SILCombiner/SILCombinerMiscVisitors.cpp | 8 ++++---- lib/SILOptimizer/Transforms/CSE.cpp | 4 ++-- .../Transforms/DeadObjectElimination.cpp | 4 ++-- .../Transforms/DestroyHoisting.cpp | 2 +- .../Transforms/ObjectOutliner.cpp | 2 +- lib/SILOptimizer/Transforms/SILSROA.cpp | 2 +- lib/SILOptimizer/Transforms/SimplifyCFG.cpp | 2 +- .../Transforms/SpeculativeDevirtualizer.cpp | 2 +- lib/SILOptimizer/Utils/ConstExpr.cpp | 8 ++++---- lib/SILOptimizer/Utils/ConstantFolding.cpp | 2 +- lib/SILOptimizer/Utils/InstOptUtils.cpp | 2 +- lib/Serialization/SerializeSIL.cpp | 4 ++-- 38 files changed, 83 insertions(+), 84 deletions(-) diff --git a/include/swift/SIL/PatternMatch.h b/include/swift/SIL/PatternMatch.h index 7b3a83bee1c0d..9a7ab469ac947 100644 --- a/include/swift/SIL/PatternMatch.h +++ b/include/swift/SIL/PatternMatch.h @@ -406,7 +406,7 @@ template struct tupleextractoperation_ty { template bool match(ITy *V) { if (auto *TEI = dyn_cast(V)) { - return TEI->getFieldNo() == index && + return TEI->getFieldIndex() == index && L.match((ValueBase *)TEI->getOperand()); } diff --git a/include/swift/SIL/Projection.h b/include/swift/SIL/Projection.h index 66f19e122531d..c78c11ed7e2a6 100644 --- a/include/swift/SIL/Projection.h +++ b/include/swift/SIL/Projection.h @@ -152,13 +152,13 @@ struct ProjectionIndex { } case ValueKind::StructElementAddrInst: { StructElementAddrInst *SEA = cast(V); - Index = SEA->getFieldNo(); + Index = SEA->getFieldIndex(); Aggregate = SEA->getOperand(); break; } case ValueKind::RefElementAddrInst: { RefElementAddrInst *REA = cast(V); - Index = REA->getFieldNo(); + Index = REA->getFieldIndex(); Aggregate = REA->getOperand(); break; } @@ -177,19 +177,19 @@ struct ProjectionIndex { } case ValueKind::TupleElementAddrInst: { TupleElementAddrInst *TEA = cast(V); - Index = TEA->getFieldNo(); + Index = TEA->getFieldIndex(); Aggregate = TEA->getOperand(); break; } case ValueKind::StructExtractInst: { StructExtractInst *SEA = cast(V); - Index = SEA->getFieldNo(); + Index = SEA->getFieldIndex(); Aggregate = SEA->getOperand(); break; } case ValueKind::TupleExtractInst: { TupleExtractInst *TEA = cast(V); - Index = TEA->getFieldNo(); + Index = TEA->getFieldIndex(); Aggregate = TEA->getOperand(); break; } diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index c27d345ee8ae2..07efa4a4ef15b 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1946,7 +1946,7 @@ SILCloner::visitTupleExtractInst(TupleExtractInst *Inst) { recordClonedInstruction( Inst, getBuilder().createTupleExtract( getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()), - Inst->getFieldNo(), getOpType(Inst->getType()))); + Inst->getFieldIndex(), getOpType(Inst->getType()))); } template @@ -1956,7 +1956,7 @@ SILCloner::visitTupleElementAddrInst(TupleElementAddrInst *Inst) { recordClonedInstruction( Inst, getBuilder().createTupleElementAddr( getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()), - Inst->getFieldNo(), getOpType(Inst->getType()))); + Inst->getFieldIndex(), getOpType(Inst->getType()))); } template diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index f61c915dba139..42b62c869c3f3 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -5715,7 +5715,7 @@ class TupleExtractInst } public: - unsigned getFieldNo() const { + unsigned getFieldIndex() const { return SILInstruction::Bits.TupleExtractInst.FieldNo; } @@ -5747,7 +5747,7 @@ class TupleElementAddrInst } public: - unsigned getFieldNo() const { + unsigned getFieldIndex() const { return SILInstruction::Bits.TupleElementAddrInst.FieldNo; } @@ -5811,8 +5811,7 @@ class FieldIndexCacheBase : public SingleValueInstruction { VarDecl *getField() const { return field; } - // FIXME: this should be called getFieldIndex(). - unsigned getFieldNo() const { + unsigned getFieldIndex() const { unsigned idx = SILInstruction::Bits.FieldIndexCacheBase.FieldIndex; if (idx != InvalidFieldIndex) return idx; diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 0d85e1c18a607..43fa8b25592c6 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -3787,7 +3787,7 @@ void IRGenSILFunction::visitTupleExtractInst(swift::TupleExtractInst *i) { projectTupleElementFromExplosion(*this, baseType, fullTuple, - i->getFieldNo(), + i->getFieldIndex(), output); (void)fullTuple.claimAll(); setLoweredExplosion(i, output); @@ -3799,7 +3799,7 @@ void IRGenSILFunction::visitTupleElementAddrInst(swift::TupleElementAddrInst *i) SILType baseType = i->getOperand()->getType(); Address field = projectTupleElementAddress(*this, base, baseType, - i->getFieldNo()); + i->getFieldIndex()); setLoweredAddress(i, field); } diff --git a/lib/SIL/IR/SILGlobalVariable.cpp b/lib/SIL/IR/SILGlobalVariable.cpp index 116cf3572c3c3..729b53a815fad 100644 --- a/lib/SIL/IR/SILGlobalVariable.cpp +++ b/lib/SIL/IR/SILGlobalVariable.cpp @@ -85,7 +85,7 @@ BuiltinInst *SILGlobalVariable::getOffsetSubtract(const TupleExtractInst *TE, // Match the pattern: // tuple_extract(usub_with_overflow(x, integer_literal, integer_literal 0), 0) - if (TE->getFieldNo() != 0) + if (TE->getFieldIndex() != 0) return nullptr; auto *BI = dyn_cast(TE->getOperand()); diff --git a/lib/SIL/IR/SILInstruction.cpp b/lib/SIL/IR/SILInstruction.cpp index afb60bea88446..235fa1918494f 100644 --- a/lib/SIL/IR/SILInstruction.cpp +++ b/lib/SIL/IR/SILInstruction.cpp @@ -579,7 +579,7 @@ namespace { auto *X = cast(LHS); if (X->getTupleType() != RHS->getTupleType()) return false; - if (X->getFieldNo() != RHS->getFieldNo()) + if (X->getFieldIndex() != RHS->getFieldIndex()) return false; return true; } @@ -590,7 +590,7 @@ namespace { auto *X = cast(LHS); if (X->getTupleType() != RHS->getTupleType()) return false; - if (X->getFieldNo() != RHS->getFieldNo()) + if (X->getFieldIndex() != RHS->getFieldIndex()) return false; return true; } diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index 392e75baf021b..1a69383a94168 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1258,7 +1258,7 @@ bool TupleExtractInst::isTrivialEltOfOneRCIDTuple() const { // parent tuple has only one non-trivial field. bool FoundNonTrivialField = false; SILType OpTy = getOperand()->getType(); - unsigned FieldNo = getFieldNo(); + unsigned FieldNo = getFieldIndex(); // For each element index of the tuple... for (unsigned i = 0, e = getNumTupleElts(); i != e; ++i) { @@ -1300,7 +1300,7 @@ bool TupleExtractInst::isEltOnlyNonTrivialElt() const { // Ok, we know that the elt we are extracting is non-trivial. Make sure that // we have no other non-trivial elts. SILType OpTy = getOperand()->getType(); - unsigned FieldNo = getFieldNo(); + unsigned FieldNo = getFieldIndex(); // For each element index of the tuple... for (unsigned i = 0, e = getNumTupleElts(); i != e; ++i) { @@ -1348,7 +1348,7 @@ VarDecl *swift::getIndexedField(NominalTypeDecl *decl, unsigned index) { if (auto *classDecl = dyn_cast(decl)) { for (auto *superDecl = classDecl->getSuperclassDecl(); superDecl != nullptr; superDecl = superDecl->getSuperclassDecl()) { - assert(index > superDecl->getStoredProperties().size() + assert(index >= superDecl->getStoredProperties().size() && "field index cannot refer to a superclass field"); index -= superDecl->getStoredProperties().size(); } @@ -1357,7 +1357,7 @@ VarDecl *swift::getIndexedField(NominalTypeDecl *decl, unsigned index) { } unsigned FieldIndexCacheBase::cacheFieldIndex() { - unsigned index = getFieldIndex(getParentDecl(), getField()); + unsigned index = ::getFieldIndex(getParentDecl(), getField()); SILInstruction::Bits.FieldIndexCacheBase.FieldIndex = index; return index; } diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 3fe377a57792e..779c9c4ba14ea 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -1774,11 +1774,11 @@ class SILPrinter : public SILInstructionVisitor { } void visitTupleExtractInst(TupleExtractInst *EI) { - *this << getIDAndType(EI->getOperand()) << ", " << EI->getFieldNo(); + *this << getIDAndType(EI->getOperand()) << ", " << EI->getFieldIndex(); } void visitTupleElementAddrInst(TupleElementAddrInst *EI) { - *this << getIDAndType(EI->getOperand()) << ", " << EI->getFieldNo(); + *this << getIDAndType(EI->getOperand()) << ", " << EI->getFieldIndex(); } void visitStructExtractInst(StructExtractInst *EI) { *this << getIDAndType(EI->getOperand()) << ", #"; diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index 1d0b0bd1c9059..6377fd2643c08 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -118,7 +118,7 @@ AccessedStorage::AccessedStorage(SILValue base, Kind kind) { // conservative given that classes are not "uniquely identified". auto *REA = cast(base); value = stripBorrow(REA->getOperand()); - setElementIndex(REA->getFieldNo()); + setElementIndex(REA->getFieldIndex()); break; } case Tail: { diff --git a/lib/SIL/Utils/Projection.cpp b/lib/SIL/Utils/Projection.cpp index 01c79c10ccbfa..e94adb426d6ac 100644 --- a/lib/SIL/Utils/Projection.cpp +++ b/lib/SIL/Utils/Projection.cpp @@ -70,23 +70,23 @@ Projection::Projection(SingleValueInstruction *I) : Value() { return; case SILInstructionKind::StructElementAddrInst: { auto *SEAI = cast(I); - Value = ValueTy(ProjectionKind::Struct, SEAI->getFieldNo()); + Value = ValueTy(ProjectionKind::Struct, SEAI->getFieldIndex()); assert(getKind() == ProjectionKind::Struct); - assert(getIndex() == SEAI->getFieldNo()); + assert(getIndex() == SEAI->getFieldIndex()); break; } case SILInstructionKind::StructExtractInst: { auto *SEI = cast(I); - Value = ValueTy(ProjectionKind::Struct, SEI->getFieldNo()); + Value = ValueTy(ProjectionKind::Struct, SEI->getFieldIndex()); assert(getKind() == ProjectionKind::Struct); - assert(getIndex() == SEI->getFieldNo()); + assert(getIndex() == SEI->getFieldIndex()); break; } case SILInstructionKind::RefElementAddrInst: { auto *REAI = cast(I); - Value = ValueTy(ProjectionKind::Class, REAI->getFieldNo()); + Value = ValueTy(ProjectionKind::Class, REAI->getFieldIndex()); assert(getKind() == ProjectionKind::Class); - assert(getIndex() == REAI->getFieldNo()); + assert(getIndex() == REAI->getFieldIndex()); break; } case SILInstructionKind::RefTailAddrInst: { @@ -106,16 +106,16 @@ Projection::Projection(SingleValueInstruction *I) : Value() { } case SILInstructionKind::TupleExtractInst: { auto *TEI = cast(I); - Value = ValueTy(ProjectionKind::Tuple, TEI->getFieldNo()); + Value = ValueTy(ProjectionKind::Tuple, TEI->getFieldIndex()); assert(getKind() == ProjectionKind::Tuple); - assert(getIndex() == TEI->getFieldNo()); + assert(getIndex() == TEI->getFieldIndex()); break; } case SILInstructionKind::TupleElementAddrInst: { auto *TEAI = cast(I); - Value = ValueTy(ProjectionKind::Tuple, TEAI->getFieldNo()); + Value = ValueTy(ProjectionKind::Tuple, TEAI->getFieldIndex()); assert(getKind() == ProjectionKind::Tuple); - assert(getIndex() == TEAI->getFieldNo()); + assert(getIndex() == TEAI->getFieldIndex()); break; } case SILInstructionKind::UncheckedEnumDataInst: { diff --git a/lib/SIL/Verifier/MemoryLifetime.cpp b/lib/SIL/Verifier/MemoryLifetime.cpp index 0ee60934e8265..25c31c55bfc97 100644 --- a/lib/SIL/Verifier/MemoryLifetime.cpp +++ b/lib/SIL/Verifier/MemoryLifetime.cpp @@ -261,14 +261,14 @@ bool MemoryLocations::analyzeLocationUsesRecursively(SILValue V, unsigned locIdx switch (user->getKind()) { case SILInstructionKind::StructElementAddrInst: { auto SEAI = cast(user); - if (!analyzeAddrProjection(SEAI, locIdx, SEAI->getFieldNo(), + if (!analyzeAddrProjection(SEAI, locIdx, SEAI->getFieldIndex(), collectedVals, subLocationMap)) return false; break; } case SILInstructionKind::TupleElementAddrInst: { auto *TEAI = cast(user); - if (!analyzeAddrProjection(TEAI, locIdx, TEAI->getFieldNo(), + if (!analyzeAddrProjection(TEAI, locIdx, TEAI->getFieldIndex(), collectedVals, subLocationMap)) return false; break; diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index fc635471a69cc..1fbf19738bef0 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -2747,11 +2747,11 @@ class SILVerifier : public SILVerifierBase { require(EI->getType().isObject(), "result of tuple_extract must be object"); - require(EI->getFieldNo() < operandTy->getNumElements(), + require(EI->getFieldIndex() < operandTy->getNumElements(), "invalid field index for tuple_extract instruction"); if (EI->getModule().getStage() != SILStage::Lowered) { requireSameType(EI->getType().getASTType(), - operandTy.getElementType(EI->getFieldNo()), + operandTy.getElementType(EI->getFieldIndex()), "type of tuple_extract does not match type of element"); } } @@ -2793,12 +2793,12 @@ class SILVerifier : public SILVerifierBase { "must derive tuple_element_addr from tuple"); ArrayRef fields = operandTy.castTo()->getElements(); - require(EI->getFieldNo() < fields.size(), + require(EI->getFieldIndex() < fields.size(), "invalid field index for element_addr instruction"); if (EI->getModule().getStage() != SILStage::Lowered) { requireSameType( EI->getType().getASTType(), - CanType(fields[EI->getFieldNo()].getType()), + CanType(fields[EI->getFieldIndex()].getType()), "type of tuple_element_addr does not match type of element"); } } @@ -2856,7 +2856,7 @@ class SILVerifier : public SILVerifierBase { loweredFieldTy, EI->getType(), "result of ref_element_addr does not match type of field"); } - EI->getFieldNo(); // Make sure we can access the field without crashing. + EI->getFieldIndex(); // Make sure we can access the field without crashing. } void checkRefTailAddrInst(RefTailAddrInst *RTAI) { diff --git a/lib/SILOptimizer/Analysis/ARCAnalysis.cpp b/lib/SILOptimizer/Analysis/ARCAnalysis.cpp index c64298fad8890..2b36c0e62a0c0 100644 --- a/lib/SILOptimizer/Analysis/ARCAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/ARCAnalysis.cpp @@ -1084,11 +1084,11 @@ swift::getSingleUnsafeGuaranteedValueResult(BuiltinInst *BI) { if (!TE || TE->getOperand() != BI) return Failed; - if (TE->getFieldNo() == 0 && !GuaranteedValue) { + if (TE->getFieldIndex() == 0 && !GuaranteedValue) { GuaranteedValue = TE; continue; } - if (TE->getFieldNo() == 1 && !Token) { + if (TE->getFieldIndex() == 1 && !Token) { Token = TE; continue; } diff --git a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp index b280a5b74efe7..d3906e4af74d2 100644 --- a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp @@ -506,13 +506,13 @@ getSingleAddressProjectionUser(SingleValueInstruction *I) { switch (User->getKind()) { case SILInstructionKind::StructElementAddrInst: { auto inst = cast(User); - ProjectionIndex = inst->getFieldNo(); + ProjectionIndex = inst->getFieldIndex(); SingleUser = inst; break; } case SILInstructionKind::TupleElementAddrInst: { auto inst = cast(User); - ProjectionIndex = inst->getFieldNo(); + ProjectionIndex = inst->getFieldIndex(); SingleUser = inst; break; } diff --git a/lib/SILOptimizer/Analysis/ArraySemantic.cpp b/lib/SILOptimizer/Analysis/ArraySemantic.cpp index abfd4510a4cbe..a6732c60f8132 100644 --- a/lib/SILOptimizer/Analysis/ArraySemantic.cpp +++ b/lib/SILOptimizer/Analysis/ArraySemantic.cpp @@ -672,7 +672,7 @@ static SILValue getArrayUninitializedInitResult(ArraySemanticsCall arrayCall, auto *tupleElt = dyn_cast(op->getUser()); if (!tupleElt) return SILValue(); - if (tupleElt->getFieldNo() != tupleElementIndex) + if (tupleElt->getFieldIndex() != tupleElementIndex) continue; tupleExtractInst = tupleElt; break; diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index c651cb5bf2943..bcfb689ea1018 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -1871,11 +1871,11 @@ EscapeAnalysis::canOptimizeArrayUninitializedCall(ApplyInst *ai) { // uses must be mapped to ConnectionGraph nodes by the client of this API. for (Operand *use : getNonDebugUses(ai)) { if (auto *tei = dyn_cast(use->getUser())) { - if (tei->getFieldNo() == 0 && !call.arrayStruct) { + if (tei->getFieldIndex() == 0 && !call.arrayStruct) { call.arrayStruct = tei; continue; } - if (tei->getFieldNo() == 1 && !call.arrayElementPtr) { + if (tei->getFieldIndex() == 1 && !call.arrayElementPtr) { call.arrayElementPtr = tei; continue; } diff --git a/lib/SILOptimizer/Analysis/SimplifyInstruction.cpp b/lib/SILOptimizer/Analysis/SimplifyInstruction.cpp index 86d91eb494161..a16af7c7975fc 100644 --- a/lib/SILOptimizer/Analysis/SimplifyInstruction.cpp +++ b/lib/SILOptimizer/Analysis/SimplifyInstruction.cpp @@ -97,7 +97,7 @@ SILValue InstSimplifier::visitStructInst(StructInst *SI) { return SILValue(); // And the order of the field must be identical to the construction order. - if (Ex->getFieldNo() != i) + if (Ex->getFieldIndex() != i) return SILValue(); } @@ -132,7 +132,7 @@ SILValue InstSimplifier::visitTupleInst(TupleInst *TI) { return SILValue(); // And the order of the field must be identical to the construction order. - if (Ex->getFieldNo() != i) + if (Ex->getFieldIndex() != i) return SILValue(); } @@ -145,11 +145,11 @@ SILValue InstSimplifier::visitTupleInst(TupleInst *TI) { SILValue InstSimplifier::visitTupleExtractInst(TupleExtractInst *TEI) { // tuple_extract(tuple(x, y), 0) -> x if (auto *TheTuple = dyn_cast(TEI->getOperand())) - return TheTuple->getElement(TEI->getFieldNo()); + return TheTuple->getElement(TEI->getFieldIndex()); // tuple_extract(apply([add|sub|...]overflow(x,y)), 0) -> x // tuple_extract(apply(checked_trunc(ext(x))), 0) -> x - if (TEI->getFieldNo() == 0) + if (TEI->getFieldIndex() == 0) if (auto *BI = dyn_cast(TEI->getOperand())) return simplifyOverflowBuiltin(BI); diff --git a/lib/SILOptimizer/Analysis/ValueTracking.cpp b/lib/SILOptimizer/Analysis/ValueTracking.cpp index 17a8994f8f9e9..32cfcce18a344 100644 --- a/lib/SILOptimizer/Analysis/ValueTracking.cpp +++ b/lib/SILOptimizer/Analysis/ValueTracking.cpp @@ -136,7 +136,7 @@ IsZeroKind swift::isZeroValue(SILValue Value) { if (auto *T = dyn_cast(Value)) { // Make sure we are extracting the number value and not // the overflow flag. - if (T->getFieldNo() != 0) + if (T->getFieldIndex() != 0) return IsZeroKind::Unknown; auto *BI = dyn_cast(T->getOperand()); diff --git a/lib/SILOptimizer/Differentiation/JVPCloner.cpp b/lib/SILOptimizer/Differentiation/JVPCloner.cpp index 26477af0d56aa..9f068289716e1 100644 --- a/lib/SILOptimizer/Differentiation/JVPCloner.cpp +++ b/lib/SILOptimizer/Differentiation/JVPCloner.cpp @@ -1113,7 +1113,7 @@ class JVPCloner::Implementation final auto loc = tei->getLoc(); auto origTupleTy = tei->getOperand()->getType().castTo(); unsigned tanIndex = 0; - for (unsigned i : range(tei->getFieldNo())) { + for (unsigned i : range(tei->getFieldIndex())) { if (getTangentSpace( origTupleTy->getElement(i).getType()->getCanonicalType())) ++tanIndex; @@ -1142,7 +1142,7 @@ class JVPCloner::Implementation final auto &diffBuilder = getDifferentialBuilder(); auto origTupleTy = teai->getOperand()->getType().castTo(); unsigned tanIndex = 0; - for (unsigned i : range(teai->getFieldNo())) { + for (unsigned i : range(teai->getFieldIndex())) { if (getTangentSpace( origTupleTy->getElement(i).getType()->getCanonicalType())) ++tanIndex; diff --git a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp index c0be157f43174..29adb43cfca57 100644 --- a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp +++ b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp @@ -1350,7 +1350,7 @@ class PullbackCloner::Implementation final if (!getTangentSpace( tupleTy->getElement(i).getType()->getCanonicalType())) continue; - if (tei->getFieldNo() == i) + if (tei->getFieldIndex() == i) elements.push_back(av); else elements.push_back(makeZeroAdjointValue( @@ -2718,7 +2718,7 @@ SILValue PullbackCloner::Implementation::getAdjointProjection( return adjSource; auto origTupleTy = source->getType().castTo(); unsigned adjIndex = 0; - for (unsigned i : range(teai->getFieldNo())) { + for (unsigned i : range(teai->getFieldIndex())) { if (getTangentSpace( origTupleTy->getElement(i).getType()->getCanonicalType())) ++adjIndex; diff --git a/lib/SILOptimizer/IPO/GlobalOpt.cpp b/lib/SILOptimizer/IPO/GlobalOpt.cpp index 9c9e7bfc3b7a9..e8093e53dffc3 100644 --- a/lib/SILOptimizer/IPO/GlobalOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalOpt.cpp @@ -358,7 +358,7 @@ static void replaceLoadsFromGlobal(SILValue addr, if (auto *teai = dyn_cast(user)) { auto *ti = cast(initVal); auto *member = cast( - ti->getElement(teai->getFieldNo())); + ti->getElement(teai->getFieldIndex())); replaceLoadsFromGlobal(teai, member, cloner); continue; } diff --git a/lib/SILOptimizer/LoopTransforms/LICM.cpp b/lib/SILOptimizer/LoopTransforms/LICM.cpp index 69a0f32be343d..39c4628324e7c 100644 --- a/lib/SILOptimizer/LoopTransforms/LICM.cpp +++ b/lib/SILOptimizer/LoopTransforms/LICM.cpp @@ -940,7 +940,7 @@ static SILValue projectLoadValue(SILValue addr, SILValue rootAddr, SILValue val = projectLoadValue(TEI->getOperand(), rootAddr, rootVal, beforeInst); SILBuilder B(beforeInst); - return B.createTupleExtract(beforeInst->getLoc(), val, TEI->getFieldNo(), + return B.createTupleExtract(beforeInst->getLoc(), val, TEI->getFieldIndex(), TEI->getType().getObjectType()); } llvm_unreachable("unknown projection"); diff --git a/lib/SILOptimizer/Mandatory/AddressLowering.cpp b/lib/SILOptimizer/Mandatory/AddressLowering.cpp index 788dd4760e03a..d4814087f681a 100644 --- a/lib/SILOptimizer/Mandatory/AddressLowering.cpp +++ b/lib/SILOptimizer/Mandatory/AddressLowering.cpp @@ -891,7 +891,7 @@ void ApplyRewriter::convertApplyWithIndirectResults() { if (origCallInst->getType().is()) { for (Operand *operand : origCallInst->getUses()) { if (auto *extract = dyn_cast(operand->getUser())) - origDirectResultValues[extract->getFieldNo()] = extract; + origDirectResultValues[extract->getFieldIndex()] = extract; else nonCanonicalUses.push_back(operand); } @@ -1002,7 +1002,7 @@ void ApplyRewriter::convertApplyWithIndirectResults() { assert(pass.valueStorageMap.contains(origCallInst)); continue; } - unsigned origResultIdx = extractInst->getFieldNo(); + unsigned origResultIdx = extractInst->getFieldIndex(); auto resultInfo = origFnConv.getResults()[origResultIdx]; if (extractInst->getType().isAddressOnly(*pass.F)) { diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 4c884da10b911..789ed908d3c35 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -613,7 +613,7 @@ void ElementUseCollector::collectTupleElementUses(TupleElementAddrInst *TEAI, // tuple_element_addr P, 42 indexes into the current tuple element. // Recursively process its uses with the adjusted element number. - unsigned FieldNo = TEAI->getFieldNo(); + unsigned FieldNo = TEAI->getFieldIndex(); auto T = TEAI->getOperand()->getType(); if (T.is()) { for (unsigned i = 0; i != FieldNo; ++i) { diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 1b7a6c384fc80..a45a3d19316b3 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -134,7 +134,7 @@ static unsigned computeSubelement(SILValue Pointer, SILType TT = TEAI->getOperand()->getType(); // Keep track of what subelement is being referenced. - for (unsigned i = 0, e = TEAI->getFieldNo(); i != e; ++i) { + for (unsigned i = 0, e = TEAI->getFieldIndex(); i != e; ++i) { SubElementNumber += getNumSubElements(TT.getTupleElementType(i), M, TypeExpansionContext(*RootInst->getFunction())); diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp index 4321485140fb2..d47dca47efee1 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp @@ -803,7 +803,7 @@ static SingleValueInstruction *getValueFromStaticLet(SILValue v) { if (!tupleVal) return nullptr; return cast( - cast(tupleVal)->getElement(teai->getFieldNo())); + cast(tupleVal)->getElement(teai->getFieldIndex())); } return nullptr; } @@ -1111,9 +1111,9 @@ static SILValue createValueFromAddr(SILValue addr, SILBuilder *builder, kind = tuple; } if (kind == tuple) { - if (elems[telem->getFieldNo()]) + if (elems[telem->getFieldIndex()]) return SILValue(); - elems[telem->getFieldNo()] = createValueFromAddr(telem, builder, loc); + elems[telem->getFieldIndex()] = createValueFromAddr(telem, builder, loc); continue; } } @@ -1802,7 +1802,7 @@ SILInstruction *SILCombiner::visitTupleExtractInst(TupleExtractInst *TEI) { // tuple_extract(apply([add|sub|...]overflow(x, 0)), 1) -> 0 // if it can be proven that no overflow can happen. - if (TEI->getFieldNo() != 1) + if (TEI->getFieldIndex() != 1) return nullptr; Builder.setCurrentDebugScope(TEI->getDebugScope()); diff --git a/lib/SILOptimizer/Transforms/CSE.cpp b/lib/SILOptimizer/Transforms/CSE.cpp index 4c88beb860bbe..ab8a5f0c88115 100644 --- a/lib/SILOptimizer/Transforms/CSE.cpp +++ b/lib/SILOptimizer/Transforms/CSE.cpp @@ -220,12 +220,12 @@ class HashVisitor : public SILInstructionVisitor { } hash_code visitTupleExtractInst(TupleExtractInst *X) { - return llvm::hash_combine(X->getKind(), X->getTupleType(), X->getFieldNo(), + return llvm::hash_combine(X->getKind(), X->getTupleType(), X->getFieldIndex(), X->getOperand()); } hash_code visitTupleElementAddrInst(TupleElementAddrInst *X) { - return llvm::hash_combine(X->getKind(), X->getTupleType(), X->getFieldNo(), + return llvm::hash_combine(X->getKind(), X->getTupleType(), X->getFieldIndex(), X->getOperand()); } diff --git a/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp b/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp index a040d1166d176..384d7291e07d5 100644 --- a/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp @@ -573,9 +573,9 @@ static bool removeAndReleaseArray(SingleValueInstruction *NewArrayValue, auto *TupleElt = dyn_cast(Op->getUser()); if (!TupleElt) return false; - if (TupleElt->getFieldNo() == 0 && !ArrayDef) { + if (TupleElt->getFieldIndex() == 0 && !ArrayDef) { ArrayDef = TupleElt; - } else if (TupleElt->getFieldNo() == 1 && !StorageAddress) { + } else if (TupleElt->getFieldIndex() == 1 && !StorageAddress) { StorageAddress = TupleElt; } else { return false; diff --git a/lib/SILOptimizer/Transforms/DestroyHoisting.cpp b/lib/SILOptimizer/Transforms/DestroyHoisting.cpp index f6ca2fa454d10..bda18ec671b4e 100644 --- a/lib/SILOptimizer/Transforms/DestroyHoisting.cpp +++ b/lib/SILOptimizer/Transforms/DestroyHoisting.cpp @@ -594,7 +594,7 @@ SILValue DestroyHoisting::createAddress(unsigned locIdx, SILBuilder &builder) { } else { auto *TEA = dyn_cast(projInst); newProj = projBuilder.createTupleElementAddr(TEA->getLoc(), baseAddr, - TEA->getFieldNo(), TEA->getType()); + TEA->getFieldIndex(), TEA->getType()); } assert(domTree->properlyDominates(newProj, ip) && "new projection does not dominate insert point"); diff --git a/lib/SILOptimizer/Transforms/ObjectOutliner.cpp b/lib/SILOptimizer/Transforms/ObjectOutliner.cpp index 7f7289e195acd..ee236efd2746f 100644 --- a/lib/SILOptimizer/Transforms/ObjectOutliner.cpp +++ b/lib/SILOptimizer/Transforms/ObjectOutliner.cpp @@ -210,7 +210,7 @@ bool ObjectOutliner::handleTailAddr(int TailIdx, SILInstruction *TailAddr, EndCOWMutationInst *toIgnore) { if (NumTailTupleElements > 0) { if (auto *TEA = dyn_cast(TailAddr)) { - unsigned TupleIdx = TEA->getFieldNo(); + unsigned TupleIdx = TEA->getFieldIndex(); assert(TupleIdx < NumTailTupleElements); for (Operand *Use : TEA->getUses()) { if (!handleTailAddr(TailIdx * NumTailTupleElements + TupleIdx, Use->getUser(), 0, diff --git a/lib/SILOptimizer/Transforms/SILSROA.cpp b/lib/SILOptimizer/Transforms/SILSROA.cpp index b931a3c156769..5ebe88c589b56 100644 --- a/lib/SILOptimizer/Transforms/SILSROA.cpp +++ b/lib/SILOptimizer/Transforms/SILSROA.cpp @@ -87,7 +87,7 @@ SROAMemoryUseAnalyzer::createAgg(SILBuilder &B, SILLocation Loc, unsigned SROAMemoryUseAnalyzer::getEltNoForProjection(SILInstruction *Inst) { if (TT) - return cast(Inst)->getFieldNo(); + return cast(Inst)->getFieldIndex(); assert(SD && "SD should not be null since either it or TT must be set at " "this point."); diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index b2de748e580db..44ee8e01f3998 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -3144,7 +3144,7 @@ static SILValue getInsertedValue(SILInstruction *Aggregate, } auto *Tuple = cast(Aggregate); auto *TEI = cast(Extract); - return Tuple->getElement(TEI->getFieldNo()); + return Tuple->getElement(TEI->getFieldIndex()); } /// Find a parent SwitchEnumInst of the block \p BB. The block \p BB is a diff --git a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp index 325c9bd7a7daf..abee9e0f0ed4d 100644 --- a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp +++ b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp @@ -380,7 +380,7 @@ static bool tryToSpeculateTarget(FullApplySite AI, ClassHierarchyAnalysis *CHA, dyn_cast(TupleExtract->getOperand())) if (UnsafeGuaranteedSelf->getBuiltinKind() == BuiltinValueKind::UnsafeGuaranteed && - TupleExtract->getFieldNo() == 0) + TupleExtract->getFieldIndex() == 0) return false; // Strip any upcasts off of our 'self' value, potentially leaving us diff --git a/lib/SILOptimizer/Utils/ConstExpr.cpp b/lib/SILOptimizer/Utils/ConstExpr.cpp index 28d760e070a91..ad862054d5df8 100644 --- a/lib/SILOptimizer/Utils/ConstExpr.cpp +++ b/lib/SILOptimizer/Utils/ConstExpr.cpp @@ -300,7 +300,7 @@ SymbolicValue ConstExprFunctionState::computeConstantValue(SILValue value) { auto val = getConstantValue(tei->getOperand()); if (!val.isConstant()) return val; - return val.getAggregateMembers()[tei->getFieldNo()]; + return val.getAggregateMembers()[tei->getFieldIndex()]; } // If this is a struct extract from a fragile type, then we can return the @@ -312,7 +312,7 @@ SymbolicValue ConstExprFunctionState::computeConstantValue(SILValue value) { return val; } assert(val.getKind() == SymbolicValue::Aggregate); - return val.getAggregateMembers()[sei->getFieldNo()]; + return val.getAggregateMembers()[sei->getFieldIndex()]; } // If this is an unchecked_enum_data from a fragile type, then we can return @@ -379,9 +379,9 @@ SymbolicValue ConstExprFunctionState::computeConstantValue(SILValue value) { // Add our index onto the next of the list. unsigned index; if (auto sea = dyn_cast(inst)) - index = sea->getFieldNo(); + index = sea->getFieldIndex(); else - index = cast(inst)->getFieldNo(); + index = cast(inst)->getFieldIndex(); accessPath.push_back(index); return SymbolicValue::getAddress(memObject, accessPath, evaluator.getAllocator()); diff --git a/lib/SILOptimizer/Utils/ConstantFolding.cpp b/lib/SILOptimizer/Utils/ConstantFolding.cpp index 84eaaf2d0cfc4..b7d4a051ef9ad 100644 --- a/lib/SILOptimizer/Utils/ConstantFolding.cpp +++ b/lib/SILOptimizer/Utils/ConstantFolding.cpp @@ -1395,7 +1395,7 @@ static bool constantFoldInstruction(Operand *Op, Optional &ResultsInError, // Constant fold extraction of a constant element. if (auto *TEI = dyn_cast(User)) { if (auto *TheTuple = dyn_cast(TEI->getOperand())) { - Results.push_back(TheTuple->getElement(TEI->getFieldNo())); + Results.push_back(TheTuple->getElement(TEI->getFieldIndex())); return true; } } diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index 19ce86358904c..7cfd7f4147715 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -1644,7 +1644,7 @@ void swift::replaceLoadSequence(SILInstruction *inst, SILValue value) { if (auto *teai = dyn_cast(inst)) { SILBuilder builder(teai); auto *tei = - builder.createTupleExtract(teai->getLoc(), value, teai->getFieldNo()); + builder.createTupleExtract(teai->getLoc(), value, teai->getFieldIndex()); for (auto teaiUse : teai->getUses()) { replaceLoadSequence(teaiUse->getUser(), tei); } diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 7c81a0c7bf579..492b18f98b64f 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -1902,11 +1902,11 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { default: llvm_unreachable("Out of sync with parent switch"); case SILInstructionKind::TupleElementAddrInst: operand = cast(&SI)->getOperand(); - FieldNo = cast(&SI)->getFieldNo(); + FieldNo = cast(&SI)->getFieldIndex(); break; case SILInstructionKind::TupleExtractInst: operand = cast(&SI)->getOperand(); - FieldNo = cast(&SI)->getFieldNo(); + FieldNo = cast(&SI)->getFieldIndex(); break; } From bbd79a2db2b88a6e086b897ddebb424356c913b4 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 24 Sep 2020 15:27:26 -0400 Subject: [PATCH 044/745] ASTScope: unqualifiedLookup() entry point does not need the name If we're searching for a declaration with a given name, the name should be entirely encapsulated inside the DeclConsumer. Otherwise, there might not be a specific name at all, if we're performing code completion for example (once LookupVisibleDecls starts to use ASTScope, anyway). --- include/swift/AST/ASTScope.h | 3 +-- include/swift/AST/NameLookup.h | 2 +- lib/AST/ASTScope.cpp | 4 ++-- lib/AST/ASTScopeLookup.cpp | 11 +++-------- lib/AST/UnqualifiedLookup.cpp | 7 ++++--- 5 files changed, 11 insertions(+), 16 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index d4b1e380ca013..2ef8fdad2629b 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -354,7 +354,7 @@ class ASTScopeImpl { /// Entry point into ASTScopeImpl-land for lookups static void - unqualifiedLookup(SourceFile *, DeclNameRef, SourceLoc, DeclConsumer); + unqualifiedLookup(SourceFile *, SourceLoc, DeclConsumer); /// Entry point into ASTScopeImpl-land for labeled statement lookups. static llvm::SmallVector @@ -366,7 +366,6 @@ class ASTScopeImpl { #pragma mark - - lookup- starting point private: static const ASTScopeImpl *findStartingScopeForLookup(SourceFile *, - const DeclNameRef name, const SourceLoc where); protected: diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 80c3d72aa1051..5715546dc3383 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -681,7 +681,7 @@ class ASTScope { /// Flesh out the tree for dumping void buildFullyExpandedTree(); - static void unqualifiedLookup(SourceFile *, DeclNameRef, SourceLoc, + static void unqualifiedLookup(SourceFile *, SourceLoc, namelookup::AbstractASTScopeDeclConsumer &); /// Entry point to record the visible statement labels from the given diff --git a/lib/AST/ASTScope.cpp b/lib/AST/ASTScope.cpp index a2d80a9ef3cac..5ed238fbb4927 100644 --- a/lib/AST/ASTScope.cpp +++ b/lib/AST/ASTScope.cpp @@ -39,11 +39,11 @@ using namespace ast_scope; #pragma mark ASTScope void ASTScope::unqualifiedLookup( - SourceFile *SF, DeclNameRef name, SourceLoc loc, + SourceFile *SF, SourceLoc loc, namelookup::AbstractASTScopeDeclConsumer &consumer) { if (auto *s = SF->getASTContext().Stats) ++s->getFrontendCounters().NumASTScopeLookups; - ASTScopeImpl::unqualifiedLookup(SF, name, loc, consumer); + ASTScopeImpl::unqualifiedLookup(SF, loc, consumer); } llvm::SmallVector ASTScope::lookupLabeledStmts( diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index d14f88313054b..17d986431e691 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -36,21 +36,16 @@ using namespace namelookup; using namespace ast_scope; void ASTScopeImpl::unqualifiedLookup( - SourceFile *sourceFile, const DeclNameRef name, const SourceLoc loc, - DeclConsumer consumer) { + SourceFile *sourceFile, const SourceLoc loc, DeclConsumer consumer) { const auto *start = - findStartingScopeForLookup(sourceFile, name, loc); + findStartingScopeForLookup(sourceFile, loc); if (start) start->lookup(nullptr, nullptr, consumer); } const ASTScopeImpl *ASTScopeImpl::findStartingScopeForLookup( - SourceFile *sourceFile, const DeclNameRef name, const SourceLoc loc) { + SourceFile *sourceFile, const SourceLoc loc) { auto *const fileScope = sourceFile->getScope().impl; - // Parser may have added decls to source file, since previous lookup - if (name.isOperator()) - return fileScope; // operators always at file scope - const auto *innermost = fileScope->findInnermostEnclosingScope(loc, nullptr); ASTScopeAssert(innermost->getWasExpanded(), "If looking in a scope, it must have been expanded."); diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index b32cf8eaede13..2423bf48bd094 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -303,7 +303,9 @@ void UnqualifiedLookupFactory::performUnqualifiedLookup() { DC->getParentSourceFile()); if (Loc.isValid()) { - lookInASTScopes(); + // Operator lookup is always global, for the time being. + if (!Name.isOperator()) + lookInASTScopes(); } else { assert(DC->isModuleScopeContext() && "Unqualified lookup without a source location must start from " @@ -543,8 +545,7 @@ void UnqualifiedLookupFactory::lookInASTScopes() { stopForDebuggingIfStartingTargetLookup(true); #endif - ASTScope::unqualifiedLookup(DC->getParentSourceFile(), - Name, Loc, consumer); + ASTScope::unqualifiedLookup(DC->getParentSourceFile(), Loc, consumer); } void ASTScopeDeclConsumerForUnqualifiedLookup::maybeUpdateSelfDC( From 49e371c563aa3afc2df811ad7af343cb1921a48f Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 24 Sep 2020 17:22:37 -0400 Subject: [PATCH 045/745] ASTScope: Remove crossCheckWithAST() --- include/swift/AST/ASTScope.h | 1 - lib/AST/ASTScopeCreation.cpp | 130 ----------------------------------- 2 files changed, 131 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 2ef8fdad2629b..5e1dbe78dfdaf 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -496,7 +496,6 @@ class ASTSourceFileScope final : public ASTScopeImpl { const SourceFile *getSourceFile() const override; NullablePtr addressForPrinting() const override { return SF; } - bool crossCheckWithAST(); protected: ASTScopeImpl *expandSpecifically(ScopeCreator &scopeCreator) override; diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index f23cbca244d2c..3a76406d8ea4e 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -462,67 +462,6 @@ class ScopeCreator final { return -1 == signum; } -public: - /// For debugging. Return true if scope tree contains all the decl contexts in - /// the AST May modify the scope tree in order to update obsolete scopes. - /// Likely slow. - bool containsAllDeclContextsFromAST() { - auto allDeclContexts = findLocalizableDeclContextsInAST(); - llvm::DenseMap bogusDCs; - sourceFileScope->preOrderDo([&](ASTScopeImpl *scope) { - scope->expandAndBeCurrentDetectingRecursion(*this); - }); - sourceFileScope->postOrderDo([&](ASTScopeImpl *scope) { - if (auto *dc = scope->getDeclContext().getPtrOrNull()) { - auto iter = allDeclContexts.find(dc); - if (iter != allDeclContexts.end()) - ++iter->second; - else - bogusDCs.insert({dc, scope}); - } - }); - - auto printDecl = [&](const Decl *d) { - llvm::errs() << "\ngetAsDecl() -> " << d << " "; - d->getSourceRange().print(llvm::errs(), ctx.SourceMgr); - llvm::errs() << " : "; - d->dump(llvm::errs()); - llvm::errs() << "\n"; - }; - bool foundOmission = false; - for (const auto &p : allDeclContexts) { - if (p.second == 0) { - if (auto *d = p.first->getAsDecl()) { - if (isLocalizable(d)) { - llvm::errs() << "\nASTScope tree omitted DeclContext: " << p.first - << " " - << ":\n"; - p.first->printContext(llvm::errs()); - printDecl(d); - foundOmission = true; - } - } else { - // If no decl, no source range, so no scope - } - } - } - for (const auto &dcAndScope : bogusDCs) { - llvm::errs() << "ASTScope tree confabulated: " << dcAndScope.getFirst() - << ":\n"; - dcAndScope.getFirst()->printContext(llvm::errs()); - if (auto *d = dcAndScope.getFirst()->getAsDecl()) - printDecl(d); - dcAndScope.getSecond()->print(llvm::errs(), 0, false); - } - return !foundOmission && bogusDCs.empty(); - } - -private: - /// Return a map of every DeclContext in the AST, and zero in the 2nd element. - /// For debugging. - llvm::DenseMap - findLocalizableDeclContextsInAST() const; - public: SWIFT_DEBUG_DUMP { print(llvm::errs()); } @@ -1535,75 +1474,6 @@ IterableTypeBodyPortion::insertionPointForDeferredExpansion( #pragma mark verification -namespace { -class LocalizableDeclContextCollector : public ASTWalker { - -public: - llvm::DenseMap declContexts; - - void record(const DeclContext *dc) { - if (dc) - declContexts.insert({dc, 0}); - } - - bool walkToDeclPre(Decl *D) override { - // catchForDebugging(D, "DictionaryBridging.swift", 694); - if (const auto *dc = dyn_cast(D)) - record(dc); - if (isa(D)) - return false; - if (auto *pd = dyn_cast(D)) - record(pd->getDefaultArgumentInitContext()); - else if (auto *pbd = dyn_cast(D)) - recordInitializers(pbd); - else if (auto *vd = dyn_cast(D)) { - vd->visitParsedAccessors([&](AccessorDecl *ad) { - ad->walk(*this); - }); - } - return ASTWalker::walkToDeclPre(D); - } - - std::pair walkToExprPre(Expr *E) override { - if (const auto *ce = dyn_cast(E)) - record(ce); - return ASTWalker::walkToExprPre(E); - } - -private: - void recordInitializers(PatternBindingDecl *pbd) { - for (auto idx : range(pbd->getNumPatternEntries())) - record(pbd->getInitContext(idx)); - } - - void catchForDebugging(Decl *D, const char *file, const unsigned line) { - auto &SM = D->getASTContext().SourceMgr; - auto loc = D->getStartLoc(); - if (!loc.isValid()) - return; - auto bufID = SM.findBufferContainingLoc(loc); - auto f = SM.getIdentifierForBuffer(bufID); - auto lin = SM.getLineAndColumnInBuffer(loc).first; - if (f.endswith(file) && lin == line) - if (isa(D)) - llvm::errs() << "*** catchForDebugging: " << lin << " ***\n"; - } -}; -} // end namespace - -llvm::DenseMap -ScopeCreator::findLocalizableDeclContextsInAST() const { - LocalizableDeclContextCollector collector; - sourceFileScope->SF->walk(collector); - // Walker omits the top - collector.record(sourceFileScope->SF); - return collector.declContexts; -} - -bool ASTSourceFileScope::crossCheckWithAST() { - return scopeCreator->containsAllDeclContextsFromAST(); -} - void ast_scope::simple_display(llvm::raw_ostream &out, const ScopeCreator *scopeCreator) { scopeCreator->print(out); From f8fb071f0fd0dd56f5d670696e453d0093d8ee9c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 24 Sep 2020 17:27:34 -0400 Subject: [PATCH 046/745] ASTScope: Remove ASTScopeImpl::getDeclContext() --- include/swift/AST/ASTScope.h | 28 ++---------------- lib/AST/ASTScope.cpp | 57 +++--------------------------------- lib/AST/ASTScopeLookup.cpp | 2 +- lib/AST/ASTScopePrinting.cpp | 4 --- 4 files changed, 7 insertions(+), 84 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 5e1dbe78dfdaf..80cc61ac08be3 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -280,7 +280,6 @@ class ASTScopeImpl { public: virtual NullablePtr getClosureIfClosureScope() const; virtual ASTContext &getASTContext() const; - virtual NullablePtr getDeclContext() const; virtual NullablePtr getDeclIfAny() const { return nullptr; }; virtual NullablePtr getStmtIfAny() const { return nullptr; }; virtual NullablePtr getExprIfAny() const { return nullptr; }; @@ -486,8 +485,6 @@ class ASTSourceFileScope final : public ASTScopeImpl { void printSpecifics(llvm::raw_ostream &out) const override; public: - NullablePtr getDeclContext() const override; - void buildFullyExpandedTree(); void buildEnoughOfTreeForTopLevelExpressionsButDontRequestGenericsOrExtendedNominals(); @@ -497,6 +494,8 @@ class ASTSourceFileScope final : public ASTScopeImpl { const SourceFile *getSourceFile() const override; NullablePtr addressForPrinting() const override { return SF; } + ASTContext &getASTContext() const override; + protected: ASTScopeImpl *expandSpecifically(ScopeCreator &scopeCreator) override; @@ -682,7 +681,6 @@ class GenericTypeOrExtensionScope : public ASTScopeImpl { // Returns the where clause scope, or the parent if none virtual ASTScopeImpl *createTrailingWhereClauseScope(ASTScopeImpl *parent, ScopeCreator &); - NullablePtr getDeclContext() const override; virtual NullablePtr getCorrespondingNominalTypeDecl() const { return nullptr; } @@ -827,7 +825,6 @@ class GenericParamScope final : public ASTScopeImpl { /// Actually holder is always a GenericContext, need to test if /// ProtocolDecl or SubscriptDecl but will refactor later. - NullablePtr getDeclContext() const override; NullablePtr getReferrent() const override; std::string getClassName() const override; SourceRange @@ -868,8 +865,6 @@ class AbstractFunctionDeclScope final : public ASTScopeImpl { void printSpecifics(llvm::raw_ostream &out) const override; public: - virtual NullablePtr getDeclContext() const override; - virtual NullablePtr getDeclIfAny() const override { return decl; } Decl *getDecl() const { return decl; } @@ -904,7 +899,6 @@ class ParameterListScope final : public ASTScopeImpl { std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; - virtual NullablePtr getDeclContext() const override; NullablePtr addressForPrinting() const override { return params; } }; @@ -927,9 +921,6 @@ class FunctionBodyScope : public ASTScopeImpl { public: SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; - virtual NullablePtr getDeclContext() const override { - return decl; - } virtual NullablePtr getDeclIfAny() const override { return decl; } Decl *getDecl() const { return decl; } @@ -957,7 +948,6 @@ class DefaultArgumentInitializerScope final : public ASTScopeImpl { std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; - virtual NullablePtr getDeclContext() const override; virtual NullablePtr getDeclIfAny() const override { return decl; } Decl *getDecl() const { return decl; } }; @@ -1000,7 +990,6 @@ class AttachedPropertyWrapperScope final : public ASTScopeImpl { SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; NullablePtr addressForPrinting() const override { return decl; } - virtual NullablePtr getDeclContext() const override; NullablePtr getDeclAttributeIfAny() const override { return attr; @@ -1099,7 +1088,6 @@ class PatternEntryInitializerScope final : public AbstractPatternEntryScope { std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; - virtual NullablePtr getDeclContext() const override; protected: bool lookupLocalsOrMembers(DeclConsumer) const override; @@ -1187,7 +1175,6 @@ class CaptureListScope final : public ASTScopeImpl { std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; - NullablePtr getDeclContext() const override; NullablePtr getExprIfAny() const override { return expr; } Expr *getExpr() const { return expr; } NullablePtr getReferrent() const override; @@ -1210,9 +1197,6 @@ class ClosureParametersScope final : public ASTScopeImpl { NullablePtr getClosureIfClosureScope() const override { return closureExpr; } - NullablePtr getDeclContext() const override { - return closureExpr; - } NullablePtr getExprIfAny() const override { return closureExpr; } Expr *getExpr() const { return closureExpr; } NullablePtr getReferrent() const override; @@ -1245,9 +1229,6 @@ class TopLevelCodeScope final : public ASTScopeImpl { std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; - virtual NullablePtr getDeclContext() const override { - return decl; - } virtual NullablePtr getDeclIfAny() const override { return decl; } Decl *getDecl() const { return decl; } NullablePtr getReferrent() const override; @@ -1334,9 +1315,6 @@ class SubscriptDeclScope final : public ASTScopeImpl { void printSpecifics(llvm::raw_ostream &out) const override; public: - virtual NullablePtr getDeclContext() const override { - return decl; - } virtual NullablePtr getDeclIfAny() const override { return decl; } Decl *getDecl() const { return decl; } NullablePtr getReferrent() const override; @@ -1356,7 +1334,6 @@ class EnumElementScope : public ASTScopeImpl { std::string getClassName() const override; ASTScopeImpl *expandSpecifically(ScopeCreator &) override; - NullablePtr getDeclContext() const override { return decl; } NullablePtr getDeclIfAny() const override { return decl; } Decl *getDecl() const { return decl; } @@ -1698,7 +1675,6 @@ class BraceStmtScope final : public AbstractStmtScope { std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; - virtual NullablePtr getDeclContext() const override; NullablePtr parentClosureIfAny() const; // public?? Stmt *getStmt() const override { return stmt; } diff --git a/lib/AST/ASTScope.cpp b/lib/AST/ASTScope.cpp index 5ed238fbb4927..82e109614025b 100644 --- a/lib/AST/ASTScope.cpp +++ b/lib/AST/ASTScope.cpp @@ -116,62 +116,9 @@ LabeledConditionalStmt *GuardStmtScope::getLabeledConditionalStmt() const { ASTContext &ASTScopeImpl::getASTContext() const { if (auto d = getDeclIfAny()) return d.get()->getASTContext(); - if (auto dc = getDeclContext()) - return dc.get()->getASTContext(); return getParent().get()->getASTContext(); } -#pragma mark getDeclContext - -NullablePtr ASTScopeImpl::getDeclContext() const { - return nullptr; -} - -NullablePtr ASTSourceFileScope::getDeclContext() const { - return NullablePtr(SF); -} - -NullablePtr GenericTypeOrExtensionScope::getDeclContext() const { - return getGenericContext(); -} - -NullablePtr GenericParamScope::getDeclContext() const { - return dyn_cast(holder); -} - -NullablePtr PatternEntryInitializerScope::getDeclContext() const { - return getPatternEntry().getInitContext(); -} - -NullablePtr BraceStmtScope::getDeclContext() const { - return getParent().get()->getDeclContext(); -} - -NullablePtr -DefaultArgumentInitializerScope::getDeclContext() const { - auto *dc = decl->getDefaultArgumentInitContext(); - ASTScopeAssert(dc, "If scope exists, this must exist"); - return dc; -} - -// When asked for a loc in an intializer in a capture list, the asked-for -// context is the closure. -NullablePtr CaptureListScope::getDeclContext() const { - return expr->getClosureBody(); -} - -NullablePtr AttachedPropertyWrapperScope::getDeclContext() const { - return decl->getParentPatternBinding()->getInitContext(0); -} - -NullablePtr AbstractFunctionDeclScope::getDeclContext() const { - return decl; -} - -NullablePtr ParameterListScope::getDeclContext() const { - return matchingContext; -} - #pragma mark getClassName std::string GenericTypeOrExtensionScope::getClassName() const { @@ -224,6 +171,10 @@ const SourceFile *ASTScopeImpl::getSourceFile() const { const SourceFile *ASTSourceFileScope::getSourceFile() const { return SF; } +ASTContext &ASTSourceFileScope::getASTContext() const { + return SF->getASTContext(); +} + SourceRange ExtensionScope::getBraces() const { return decl->getBraces(); } SourceRange NominalTypeScope::getBraces() const { return decl->getBraces(); } diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index 17d986431e691..e848e6c12c52c 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -248,7 +248,7 @@ bool GenericTypeOrExtensionWhereOrBodyPortion::lookupMembersOf( auto nt = scope->getCorrespondingNominalTypeDecl().getPtrOrNull(); if (!nt) return false; - return consumer.lookInMembers(scope->getDeclContext().get(), nt); + return consumer.lookInMembers(scope->getGenericContext(), nt); } bool GenericTypeOrExtensionWherePortion::lookupMembersOf( diff --git a/lib/AST/ASTScopePrinting.cpp b/lib/AST/ASTScopePrinting.cpp index 5e45c8a95699d..f68e4f582628e 100644 --- a/lib/AST/ASTScopePrinting.cpp +++ b/lib/AST/ASTScopePrinting.cpp @@ -56,10 +56,6 @@ void ASTScopeImpl::dumpOneScopeMapLocation( auto *locScope = findInnermostEnclosingScope(loc, &llvm::errs()); locScope->print(llvm::errs(), 0, false, false); - // Dump the AST context, too. - if (auto *dc = locScope->getDeclContext().getPtrOrNull()) - dc->printContext(llvm::errs()); - namelookup::ASTScopeDeclGatherer gatherer; // Print the local bindings introduced by this scope. locScope->lookupLocalsOrMembers(gatherer); From e646ef2cbe18e7a61056fe2f1a2176cc36259261 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 24 Sep 2020 23:32:08 -0600 Subject: [PATCH 047/745] Add incrementalExternalDepend as an Incremental NodeKind --- include/swift/Basic/ReferenceDependencyKeys.h | 7 +++++-- lib/AST/FineGrainedDependencies.cpp | 5 +++++ lib/AST/FrontendSourceFileDepGraphFactory.cpp | 3 +++ lib/FrontendTool/FrontendTool.cpp | 2 +- tools/swift-dependency-tool/swift-dependency-tool.cpp | 2 ++ unittests/Driver/UnitTestSourceFileDepGraphFactory.cpp | 1 + 6 files changed, 17 insertions(+), 3 deletions(-) diff --git a/include/swift/Basic/ReferenceDependencyKeys.h b/include/swift/Basic/ReferenceDependencyKeys.h index 3d7749df035a5..22a68f8ace002 100644 --- a/include/swift/Basic/ReferenceDependencyKeys.h +++ b/include/swift/Basic/ReferenceDependencyKeys.h @@ -53,6 +53,7 @@ enum class NodeKind { dynamicLookup, externalDepend, sourceFileProvide, + incrementalExternalDepend, /// For iterating through the NodeKinds. kindCount }; @@ -60,8 +61,10 @@ enum class NodeKind { /// Used for printing out NodeKinds to dot files, and dumping nodes for /// debugging. const std::string NodeKindNames[]{ - "topLevel", "nominal", "potentialMember", "member", - "dynamicLookup", "externalDepend", "sourceFileProvide"}; + "topLevel", "nominal", + "potentialMember", "member", + "dynamicLookup", "externalDepend", + "sourceFileProvide", "incrementalExternalDepend"}; } // end namespace fine_grained_dependencies } // end namespace swift diff --git a/lib/AST/FineGrainedDependencies.cpp b/lib/AST/FineGrainedDependencies.cpp index 5543d03c7f4a5..d12e07ac40360 100644 --- a/lib/AST/FineGrainedDependencies.cpp +++ b/lib/AST/FineGrainedDependencies.cpp @@ -231,6 +231,7 @@ std::string DependencyKey::humanReadableName() const { switch (kind) { case NodeKind::member: return demangleTypeAsContext(context) + "." + name; + case NodeKind::incrementalExternalDepend: case NodeKind::externalDepend: case NodeKind::sourceFileProvide: return llvm::sys::path::filename(name).str(); @@ -261,9 +262,12 @@ raw_ostream &fine_grained_dependencies::operator<<(raw_ostream &out, bool DependencyKey::verify() const { assert((getKind() != NodeKind::externalDepend || isInterface()) && "All external dependencies must be interfaces."); + assert((getKind() != NodeKind::incrementalExternalDepend || isInterface()) && + "All incremental external dependencies must be interfaces."); switch (getKind()) { case NodeKind::topLevel: case NodeKind::dynamicLookup: + case NodeKind::incrementalExternalDepend: case NodeKind::externalDepend: case NodeKind::sourceFileProvide: assert(context.empty() && !name.empty() && "Must only have a name"); @@ -294,6 +298,7 @@ void DependencyKey::verifyNodeKindNames() { CHECK_NAME(potentialMember) CHECK_NAME(member) CHECK_NAME(dynamicLookup) + CHECK_NAME(incrementalExternalDepend) CHECK_NAME(externalDepend) CHECK_NAME(sourceFileProvide) case NodeKind::kindCount: diff --git a/lib/AST/FrontendSourceFileDepGraphFactory.cpp b/lib/AST/FrontendSourceFileDepGraphFactory.cpp index 6439266e908f5..c89e1e0318e98 100644 --- a/lib/AST/FrontendSourceFileDepGraphFactory.cpp +++ b/lib/AST/FrontendSourceFileDepGraphFactory.cpp @@ -594,6 +594,9 @@ class UsedDeclEnumerator { } void enumerateExternalUses() { + for (StringRef s : depTracker.getIncrementalDependencies()) + enumerateUse("", s); + for (StringRef s : depTracker.getDependencies()) enumerateUse("", s); } diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 19973d3328947..39231330e0c30 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -2302,7 +2302,7 @@ static bool performCompileStepsPostSILGen(CompilerInstance &Instance, auto *Mod = MSF.get(); fine_grained_dependencies::withReferenceDependencies( Mod, *Instance.getDependencyTracker(), Mod->getModuleFilename(), - alsoEmitDotFile, [](SourceFileDepGraph &&g) { + alsoEmitDotFile, [&](SourceFileDepGraph &&g) { serialize(MSF, serializationOpts, SM.get(), &g); return false; }); diff --git a/tools/swift-dependency-tool/swift-dependency-tool.cpp b/tools/swift-dependency-tool/swift-dependency-tool.cpp index c5b6096387c40..82a6e6c7b1054 100644 --- a/tools/swift-dependency-tool/swift-dependency-tool.cpp +++ b/tools/swift-dependency-tool/swift-dependency-tool.cpp @@ -93,6 +93,8 @@ void ScalarEnumerationTraits:: io.enumCase(value, "member", NodeKind::member); io.enumCase(value, "dynamicLookup", NodeKind::dynamicLookup); io.enumCase(value, "externalDepend", NodeKind::externalDepend); + io.enumCase(value, "incrementalExternalDepend", + NodeKind::incrementalExternalDepend); io.enumCase(value, "sourceFileProvide", NodeKind::sourceFileProvide); } diff --git a/unittests/Driver/UnitTestSourceFileDepGraphFactory.cpp b/unittests/Driver/UnitTestSourceFileDepGraphFactory.cpp index 71c9794a5d0d4..34047395b1411 100644 --- a/unittests/Driver/UnitTestSourceFileDepGraphFactory.cpp +++ b/unittests/Driver/UnitTestSourceFileDepGraphFactory.cpp @@ -147,6 +147,7 @@ UnitTestSourceFileDepGraphFactory::singleNameIsContext(const NodeKind kind) { return true; case NodeKind::topLevel: case NodeKind::dynamicLookup: + case NodeKind::incrementalExternalDepend: case NodeKind::externalDepend: case NodeKind::sourceFileProvide: return false; From 3ba33b93eb2e72e46e6ec6464a51e3d38d455660 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 25 Sep 2020 00:22:39 -0600 Subject: [PATCH 048/745] Install Faux Cross-Module Incremental Infrastructure In Driver Treat any incremental external depends like normal external depends. This will eventually become the fallback behavior for cross-module incremental builds. --- .../Driver/FineGrainedDependencyDriverGraph.h | 14 +++++ lib/Driver/Compilation.cpp | 44 +++++++++++++++ .../FineGrainedDependencyDriverGraph.cpp | 55 ++++++++++++++++++- 3 files changed, 111 insertions(+), 2 deletions(-) diff --git a/include/swift/Driver/FineGrainedDependencyDriverGraph.h b/include/swift/Driver/FineGrainedDependencyDriverGraph.h index 16f9a70dea984..b43bbb740591d 100644 --- a/include/swift/Driver/FineGrainedDependencyDriverGraph.h +++ b/include/swift/Driver/FineGrainedDependencyDriverGraph.h @@ -168,6 +168,7 @@ class ModuleDepGraph { // Supports requests from the driver to getExternalDependencies. std::unordered_set externalDependencies; + std::unordered_set incrementalExternalDependencies; /// Keyed by swiftdeps filename, so we can get back to Jobs. std::unordered_map jobsBySwiftDeps; @@ -512,15 +513,28 @@ class ModuleDepGraph { std::vector findExternallyDependentUntracedJobs(StringRef externalDependency); + /// Find jobs that were previously not known to need compilation but that + /// depend on \c incrementalExternalDependency. + /// + /// This code path should only act as a fallback to the status-quo behavior. + /// Otherwise it acts to pessimize the behavior of cross-module incremental + /// builds. + std::vector + findIncrementalExternallyDependentUntracedJobs(StringRef externalDependency); + //============================================================================ // MARK: ModuleDepGraph - External dependencies //============================================================================ public: std::vector getExternalDependencies() const; + std::vector getIncrementalExternalDependencies() const; void forEachUntracedJobDirectlyDependentOnExternalSwiftDeps( StringRef externalDependency, function_ref fn); + void forEachUntracedJobDirectlyDependentOnExternalIncrementalSwiftDeps( + StringRef externalDependency, function_ref fn); + //============================================================================ // MARK: ModuleDepGraph - verification //============================================================================ diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index f2cc64f05fa51..183892b3abbe2 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -935,6 +935,10 @@ namespace driver { for (const auto cmd : collectExternallyDependentJobsFromDependencyGraph(forRanges)) jobsToSchedule.insert(cmd); + for (const auto cmd : + collectIncrementalExternallyDependentJobsFromDependencyGraph( + forRanges)) + jobsToSchedule.insert(cmd); return jobsToSchedule; } @@ -1109,6 +1113,21 @@ namespace driver { } } + void forEachOutOfDateIncrementalExternalDependency( + const bool forRanges, + function_ref consumeExternalSwiftDeps) { + for (StringRef dependency : + getIncrementalExternalDependencies(forRanges)) { + // If the dependency has been modified since the oldest built file, + // or if we can't stat it for some reason (perhaps it's been + // deleted?), trigger rebuilds through the dependency graph. + llvm::sys::fs::file_status depStatus; + if (llvm::sys::fs::status(dependency, depStatus) || + Comp.getLastBuildTime() < depStatus.getLastModificationTime()) + consumeExternalSwiftDeps(dependency); + } + } + CommandSet collectCascadedJobsFromDependencyGraph( const CommandSet &InitialCascadingCommands, const bool forRanges) { CommandSet CascadedJobs; @@ -1141,6 +1160,25 @@ namespace driver { return ExternallyDependentJobs; } + SmallVector + collectIncrementalExternallyDependentJobsFromDependencyGraph( + const bool forRanges) { + SmallVector ExternallyDependentJobs; + // Check all cross-module dependencies as well. + forEachOutOfDateIncrementalExternalDependency( + forRanges, [&](StringRef dependency) { + // If the dependency has been modified since the oldest built file, + // or if we can't stat it for some reason (perhaps it's been + // deleted?), trigger rebuilds through the dependency graph. + for (const Job *marked : + markExternalInDepGraph(dependency, forRanges)) + ExternallyDependentJobs.push_back(marked); + }); + noteBuildingJobs(ExternallyDependentJobs, forRanges, + "because of external dependencies"); + return ExternallyDependentJobs; + } + /// Insert all jobs in \p Cmds (of descriptive name \p Kind) to the \c /// TaskQueue, and clear \p Cmds. template @@ -1594,6 +1632,12 @@ namespace driver { return getFineGrainedDepGraph(forRanges).getExternalDependencies(); } + std::vector + getIncrementalExternalDependencies(const bool forRanges) const { + return getFineGrainedDepGraph(forRanges) + .getIncrementalExternalDependencies(); + } + std::vector markExternalInDepGraph(StringRef externalDependency, const bool forRanges) { diff --git a/lib/Driver/FineGrainedDependencyDriverGraph.cpp b/lib/Driver/FineGrainedDependencyDriverGraph.cpp index 6111b3690a7e1..0de440b7a2c65 100644 --- a/lib/Driver/FineGrainedDependencyDriverGraph.cpp +++ b/lib/Driver/FineGrainedDependencyDriverGraph.cpp @@ -195,6 +195,12 @@ std::vector ModuleDepGraph::getExternalDependencies() const { externalDependencies.end()); } +std::vector +ModuleDepGraph::getIncrementalExternalDependencies() const { + return std::vector(incrementalExternalDependencies.begin(), + incrementalExternalDependencies.end()); +} + // Add every (swiftdeps) use of the external dependency to foundJobs. // Can return duplicates, but it doesn't break anything, and they will be // canonicalized later. @@ -216,6 +222,26 @@ std::vector ModuleDepGraph::findExternallyDependentUntracedJobs( return foundJobs; } +std::vector +ModuleDepGraph::findIncrementalExternallyDependentUntracedJobs( + StringRef externalDependency) { + FrontendStatsTracer tracer(stats, + "fine-grained-dependencies-" + "findIncrementalExternallyDependentUntracedJobs"); + std::vector foundJobs; + forEachUntracedJobDirectlyDependentOnExternalIncrementalSwiftDeps( + externalDependency, [&](const Job *job) { + foundJobs.push_back(job); + for (const Job *marked : findJobsToRecompileWhenWholeJobChanges(job)) { + // findJobsToRecompileWhenWholeJobChanges is reflexive + // Don't return job twice. + if (marked != job) + foundJobs.push_back(marked); + } + }); + return foundJobs; +} + void ModuleDepGraph::forEachUntracedJobDirectlyDependentOnExternalSwiftDeps( StringRef externalSwiftDeps, function_ref fn) { // TODO move nameForDep into key @@ -228,6 +254,19 @@ void ModuleDepGraph::forEachUntracedJobDirectlyDependentOnExternalSwiftDeps( } } +void ModuleDepGraph:: + forEachUntracedJobDirectlyDependentOnExternalIncrementalSwiftDeps( + StringRef externalSwiftDeps, function_ref fn) { + // TODO move nameForDep into key + // These nodes will depend on the *interface* of the external Decl. + DependencyKey key(NodeKind::incrementalExternalDepend, DeclAspect::interface, + "", externalSwiftDeps.str()); + for (const ModuleDepGraphNode *useNode : usesByDef[key]) { + if (!useNode->getHasBeenTraced()) + fn(getJob(useNode->getSwiftDepsOfProvides())); + } +} + //============================================================================== // MARK: Integrating SourceFileDepGraph into ModuleDepGraph //============================================================================== @@ -387,9 +426,14 @@ bool ModuleDepGraph::recordWhatUseDependsUpon( sourceFileUseNode, [&](const SourceFileDepGraphNode *def) { const bool isNewUse = usesByDef[def->getKey()].insert(moduleUseNode).second; - if (isNewUse && def->getKey().getKind() == NodeKind::externalDepend) { + if (isNewUse) { StringRef externalSwiftDeps = def->getKey().getName(); - externalDependencies.insert(externalSwiftDeps.str()); + if (def->getKey().getKind() == NodeKind::externalDepend) { + externalDependencies.insert(externalSwiftDeps.str()); + } else if (def->getKey().getKind() == + NodeKind::incrementalExternalDepend) { + incrementalExternalDependencies.insert(externalSwiftDeps.str()); + } useHasNewExternalDependency = true; } }); @@ -641,6 +685,9 @@ void ModuleDepGraph::verifyExternalDependencyUniqueness( assert((key.getKind() != NodeKind::externalDepend || externalDependencies.count(key.getName().str()) == 1) && "Ensure each external dependency is tracked exactly once"); + assert((key.getKind() != NodeKind::incrementalExternalDepend || + incrementalExternalDependencies.count(key.getName().str()) == 1) && + "Ensure each incremental external dependency is tracked exactly once"); } void ModuleDepGraph::verifyCanFindEachJob() const { @@ -725,6 +772,10 @@ void ModuleDepGraph::printOneNodeOfPath(raw_ostream &out, out << filename << " depends on " << key.aspectName() << " of module '" << key.humanReadableName() << "'"; break; + case NodeKind::incrementalExternalDepend: + out << filename << " depends on " << key.aspectName() + << " of incremental module '" << key.humanReadableName() << "'"; + break; case NodeKind::sourceFileProvide: out << key.aspectName() << " of source file " << key.humanReadableName(); break; From 6b40a092b9c059873d0fd8713988aee5ca41db0e Mon Sep 17 00:00:00 2001 From: "Kuba (Brecka) Mracek" Date: Fri, 25 Sep 2020 07:29:03 -0700 Subject: [PATCH 049/745] Move the actual Obj-C parts of ReflectionMirror.mm into a separate file, make ReflectionMirror.mm a .cpp file (#34025) --- stdlib/public/runtime/CMakeLists.txt | 3 +- stdlib/public/runtime/Private.h | 7 ++++ ...flectionMirror.mm => ReflectionMirror.cpp} | 22 +--------- stdlib/public/runtime/ReflectionMirrorObjC.mm | 42 +++++++++++++++++++ 4 files changed, 53 insertions(+), 21 deletions(-) rename stdlib/public/runtime/{ReflectionMirror.mm => ReflectionMirror.cpp} (98%) create mode 100644 stdlib/public/runtime/ReflectionMirrorObjC.mm diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index 0b481bd2459b8..4b51b04fec37b 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -22,7 +22,7 @@ set(swift_runtime_objc_sources ErrorObject.mm SwiftObject.mm SwiftValue.mm - ReflectionMirror.mm + ReflectionMirrorObjC.mm ObjCRuntimeGetImageNameFromClass.mm) set(swift_runtime_sources @@ -63,6 +63,7 @@ set(swift_runtime_sources Portability.cpp ProtocolConformance.cpp RefCount.cpp + ReflectionMirror.cpp RuntimeInvocationsTracking.cpp SwiftDtoa.cpp) diff --git a/stdlib/public/runtime/Private.h b/stdlib/public/runtime/Private.h index 2f5e7232cc55d..b073de6997ac8 100644 --- a/stdlib/public/runtime/Private.h +++ b/stdlib/public/runtime/Private.h @@ -632,6 +632,13 @@ class TypeInfo { const Metadata *assocType, const ProtocolRequirement *reqBase, const ProtocolRequirement *assocConformance); + +#if SWIFT_OBJC_INTEROP + /// Returns a retained Quick Look representation object an Obj-C object. + SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL + id _quickLookObjectForPointer(void *value); +#endif + } // end namespace swift #endif /* SWIFT_RUNTIME_PRIVATE_H */ diff --git a/stdlib/public/runtime/ReflectionMirror.mm b/stdlib/public/runtime/ReflectionMirror.cpp similarity index 98% rename from stdlib/public/runtime/ReflectionMirror.mm rename to stdlib/public/runtime/ReflectionMirror.cpp index 9a7b41ee4bb21..5fbad4fb3c136 100644 --- a/stdlib/public/runtime/ReflectionMirror.mm +++ b/stdlib/public/runtime/ReflectionMirror.cpp @@ -34,9 +34,6 @@ #if SWIFT_OBJC_INTEROP #include "swift/Runtime/ObjCBridge.h" #include "SwiftObject.h" -#include -#include -#include #endif #if defined(_WIN32) @@ -80,13 +77,6 @@ int asprintf(char **strp, const char *fmt, ...) { using namespace swift; -#if SWIFT_OBJC_INTEROP -// Declare the debugQuickLookObject selector. -@interface DeclareSelectors -- (id)debugQuickLookObject; -@end -#endif - namespace { class FieldType { @@ -751,16 +741,8 @@ AnyReturn subscript(intptr_t i, const char **outName, } #if SWIFT_OBJC_INTEROP - id quickLookObject() { - id object = [*reinterpret_cast(value) retain]; - if ([object respondsToSelector:@selector(debugQuickLookObject)]) { - id quickLookObject = [object debugQuickLookObject]; - [quickLookObject retain]; - [object release]; - return quickLookObject; - } - - return object; + id quickLookObject() override { + return _quickLookObjectForPointer(value); } #endif }; diff --git a/stdlib/public/runtime/ReflectionMirrorObjC.mm b/stdlib/public/runtime/ReflectionMirrorObjC.mm new file mode 100644 index 0000000000000..a9efbe107b35d --- /dev/null +++ b/stdlib/public/runtime/ReflectionMirrorObjC.mm @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/Config.h" + +#if SWIFT_OBJC_INTEROP + +#include "Private.h" +#include +#include +#include + +using namespace swift; + +// Declare the debugQuickLookObject selector. +@interface DeclareSelectors +- (id)debugQuickLookObject; +@end + +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL +id swift::_quickLookObjectForPointer(void *value) { + id object = [*reinterpret_cast(value) retain]; + if ([object respondsToSelector:@selector(debugQuickLookObject)]) { + id quickLookObject = [object debugQuickLookObject]; + [quickLookObject retain]; + [object release]; + return quickLookObject; + } + + return object; +} + +#endif From d913fc609f191a01bc131f37858b00b87d94a328 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Fri, 11 Sep 2020 13:29:27 -0700 Subject: [PATCH 050/745] [Build Script] Pass down a CMake path to swift-driver's build script --- .../products/swiftdriver.py | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/utils/swift_build_support/swift_build_support/products/swiftdriver.py b/utils/swift_build_support/swift_build_support/products/swiftdriver.py index 5eca23c888086..cead28701b74b 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftdriver.py +++ b/utils/swift_build_support/swift_build_support/products/swiftdriver.py @@ -10,9 +10,10 @@ # # ---------------------------------------------------------------------------- +import os + from . import cmark from . import foundation -from . import indexstoredb from . import libcxx from . import libdispatch from . import libicu @@ -20,7 +21,10 @@ from . import llvm from . import product from . import swift +from . import swiftpm from . import xctest +from .. import shell +from .. import targets class SwiftDriver(product.Product): @@ -51,24 +55,48 @@ def should_clean(self, host_target): return self.args.clean_swift_driver def clean(self, host_target): - indexstoredb.run_build_script_helper( - 'clean', host_target, self, self.args) + run_build_script_helper('clean', host_target, self, self.args) def build(self, host_target): - indexstoredb.run_build_script_helper( - 'build', host_target, self, self.args) + run_build_script_helper('build', host_target, self, self.args) def should_test(self, host_target): return self.args.test_swift_driver def test(self, host_target): - indexstoredb.run_build_script_helper( - 'test', host_target, self, self.args, - self.args.test_sourcekitlsp_sanitize_all) + run_build_script_helper('test', host_target, self, self.args) def should_install(self, host_target): return self.args.install_swift_driver def install(self, host_target): - indexstoredb.run_build_script_helper( - 'install', host_target, self, self.args) + run_build_script_helper('install', host_target, self, self.args) + + +def run_build_script_helper(action, host_target, product, args): + script_path = os.path.join( + product.source_dir, 'Utilities', 'build-script-helper.py') + + install_destdir = args.install_destdir + if swiftpm.SwiftPM.has_cross_compile_hosts(args): + install_destdir = swiftpm.SwiftPM.get_install_destdir(args, + host_target, + product.build_dir) + toolchain_path = targets.toolchain_path(install_destdir, + args.install_prefix) + is_release = product.is_release() + configuration = 'release' if is_release else 'debug' + helper_cmd = [ + script_path, + action, + '--package-path', product.source_dir, + '--build-path', product.build_dir, + '--configuration', configuration, + '--toolchain', toolchain_path, + '--ninja-bin', product.toolchain.ninja, + '--cmake-bin', product.toolchain.cmake, + ] + if args.verbose_build: + helper_cmd.append('--verbose') + + shell.call(helper_cmd) From 7917d723c3cb0b9b794819797bb5ac53c76d2b13 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 25 Sep 2020 11:36:11 -0600 Subject: [PATCH 051/745] Teach the Driver to Read Fine-Grained Dependency Graphs in Swiftdeps Files --- include/swift/AST/FineGrainedDependencies.h | 2 + .../swift/AST/FineGrainedDependencyFormat.h | 9 +- .../Driver/FineGrainedDependencyDriverGraph.h | 2 + lib/AST/FineGrainedDependencies.cpp | 9 ++ lib/AST/FineGrainedDependencyFormat.cpp | 104 ++++++++++++++++++ .../FineGrainedDependencyDriverGraph.cpp | 21 ++++ lib/Serialization/ModuleFormat.h | 12 +- 7 files changed, 154 insertions(+), 5 deletions(-) diff --git a/include/swift/AST/FineGrainedDependencies.h b/include/swift/AST/FineGrainedDependencies.h index 9fa481f430b5b..e701ad31cce8a 100644 --- a/include/swift/AST/FineGrainedDependencies.h +++ b/include/swift/AST/FineGrainedDependencies.h @@ -851,6 +851,8 @@ class SourceFileDepGraph { /// Read a swiftdeps file from \p buffer and return a SourceFileDepGraph if /// successful. Optional static loadFromBuffer(llvm::MemoryBuffer &); + Optional static loadFromSwiftModuleBuffer( + llvm::MemoryBuffer &); void verifySame(const SourceFileDepGraph &other) const; diff --git a/include/swift/AST/FineGrainedDependencyFormat.h b/include/swift/AST/FineGrainedDependencyFormat.h index d3b487a561628..9bfe743afef9f 100644 --- a/include/swift/AST/FineGrainedDependencyFormat.h +++ b/include/swift/AST/FineGrainedDependencyFormat.h @@ -48,6 +48,7 @@ using NodeKindField = BCFixed<3>; using DeclAspectField = BCFixed<1>; const unsigned RECORD_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID; +const unsigned INCREMENTAL_INFORMATION_BLOCK_ID = 196; /// The swiftdeps file format consists of a METADATA record, followed by zero or more /// IDENTIFIER_NODE records. @@ -113,10 +114,16 @@ namespace record_block { } /// Tries to read the dependency graph from the given buffer. -/// Returns true if there was an error. +/// Returns \c true if there was an error. bool readFineGrainedDependencyGraph(llvm::MemoryBuffer &buffer, SourceFileDepGraph &g); +/// Tries to read the dependency graph from the given buffer, assuming that it +/// is in the format of a swiftmodule file. +/// Returns \c true if there was an error. +bool readFineGrainedDependencyGraphFromSwiftModule(llvm::MemoryBuffer &buffer, + SourceFileDepGraph &g); + /// Tries to read the dependency graph from the given path name. /// Returns true if there was an error. bool readFineGrainedDependencyGraph(llvm::StringRef path, diff --git a/include/swift/Driver/FineGrainedDependencyDriverGraph.h b/include/swift/Driver/FineGrainedDependencyDriverGraph.h index b43bbb740591d..ba461842bb36e 100644 --- a/include/swift/Driver/FineGrainedDependencyDriverGraph.h +++ b/include/swift/Driver/FineGrainedDependencyDriverGraph.h @@ -336,6 +336,8 @@ class ModuleDepGraph { const SourceFileDepGraph &, DiagnosticEngine &); + Changes loadFromSwiftModuleBuffer(const driver::Job *, llvm::MemoryBuffer &, + DiagnosticEngine &); private: /// Read a SourceFileDepGraph belonging to \p job from \p buffer diff --git a/lib/AST/FineGrainedDependencies.cpp b/lib/AST/FineGrainedDependencies.cpp index d12e07ac40360..594b78931eaac 100644 --- a/lib/AST/FineGrainedDependencies.cpp +++ b/lib/AST/FineGrainedDependencies.cpp @@ -58,6 +58,15 @@ SourceFileDepGraph::loadFromBuffer(llvm::MemoryBuffer &buffer) { return Optional(std::move(fg)); } +Optional +SourceFileDepGraph::loadFromSwiftModuleBuffer(llvm::MemoryBuffer &buffer) { + SourceFileDepGraph fg; + if (swift::fine_grained_dependencies:: + readFineGrainedDependencyGraphFromSwiftModule(buffer, fg)) + return None; + return Optional(std::move(fg)); +} + //============================================================================== // MARK: SourceFileDepGraph access //============================================================================== diff --git a/lib/AST/FineGrainedDependencyFormat.cpp b/lib/AST/FineGrainedDependencyFormat.cpp index 291897804d5ab..2eacef0a22156 100644 --- a/lib/AST/FineGrainedDependencyFormat.cpp +++ b/lib/AST/FineGrainedDependencyFormat.cpp @@ -45,6 +45,7 @@ class Deserializer { public: Deserializer(llvm::MemoryBufferRef Data) : Cursor(Data) {} bool readFineGrainedDependencyGraph(SourceFileDepGraph &g); + bool readFineGrainedDependencyGraphFromSwiftModule(SourceFileDepGraph &g); }; } // end namespace @@ -485,3 +486,106 @@ bool swift::fine_grained_dependencies::writeFineGrainedDependencyGraphToPath( return false; }); } + +static bool checkModuleSignature(llvm::BitstreamCursor &cursor, + ArrayRef signature) { + for (unsigned char byte : signature) { + if (cursor.AtEndOfStream()) + return false; + if (llvm::Expected maybeRead = + cursor.Read(8)) { + if (maybeRead.get() != byte) + return false; + } else { + consumeError(maybeRead.takeError()); + return false; + } + } + return true; +} + +static bool enterTopLevelModuleBlock(llvm::BitstreamCursor &cursor, unsigned ID, + bool shouldReadBlockInfo = true) { + llvm::Expected maybeNext = cursor.advance(); + if (!maybeNext) { + consumeError(maybeNext.takeError()); + return false; + } + llvm::BitstreamEntry next = maybeNext.get(); + + if (next.Kind != llvm::BitstreamEntry::SubBlock) + return false; + + if (next.ID == RECORD_BLOCK_ID) { + if (shouldReadBlockInfo) { + if (!cursor.ReadBlockInfoBlock()) + return false; + } else { + if (cursor.SkipBlock()) + return false; + } + return enterTopLevelModuleBlock(cursor, ID, false); + } + + if (next.ID != ID) + return false; + + if (llvm::Error Err = cursor.EnterSubBlock(ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + return false; + } + + return true; +} + +bool swift::fine_grained_dependencies:: + readFineGrainedDependencyGraphFromSwiftModule(llvm::MemoryBuffer &buffer, + SourceFileDepGraph &g) { + Deserializer deserializer(buffer.getMemBufferRef()); + return deserializer.readFineGrainedDependencyGraphFromSwiftModule(g); +} + +bool Deserializer::readFineGrainedDependencyGraphFromSwiftModule( + SourceFileDepGraph &g) { + if (!checkModuleSignature(Cursor, {0xE2, 0x9C, 0xA8, 0x0E}) || + !enterTopLevelModuleBlock(Cursor, RECORD_BLOCK_ID, false)) { + return false; + } + + llvm::BitstreamEntry topLevelEntry; + + while (!Cursor.AtEndOfStream()) { + llvm::Expected maybeEntry = + Cursor.advance(llvm::BitstreamCursor::AF_DontPopBlockAtEnd); + if (!maybeEntry) { + consumeError(maybeEntry.takeError()); + return false; + } + topLevelEntry = maybeEntry.get(); + if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock) + break; + + switch (topLevelEntry.ID) { + case INCREMENTAL_INFORMATION_BLOCK_ID: { + if (llvm::Error Err = + Cursor.EnterSubBlock(INCREMENTAL_INFORMATION_BLOCK_ID)) { + consumeError(std::move(Err)); + return false; + } + readFineGrainedDependencyGraph(g); + break; + } + + default: + // Unknown top-level block, possibly for use by a future version of the + // module format. + if (Cursor.SkipBlock()) { + return false; + } + break; + } + } + + return false; +} diff --git a/lib/Driver/FineGrainedDependencyDriverGraph.cpp b/lib/Driver/FineGrainedDependencyDriverGraph.cpp index 0de440b7a2c65..dc2a9afcf929b 100644 --- a/lib/Driver/FineGrainedDependencyDriverGraph.cpp +++ b/lib/Driver/FineGrainedDependencyDriverGraph.cpp @@ -108,6 +108,27 @@ ModuleDepGraph::Changes ModuleDepGraph::loadFromSourceFileDepGraph( return changes; } +ModuleDepGraph::Changes ModuleDepGraph::loadFromSwiftModuleBuffer( + const Job *Cmd, llvm::MemoryBuffer &buffer, DiagnosticEngine &diags) { + FrontendStatsTracer tracer( + stats, "fine-grained-dependencies-loadFromSwiftModuleBuffer"); + PrettyStackTraceStringAction stackTrace( + "loading fine-grained dependency graph from swiftmodule", + buffer.getBufferIdentifier()); + + Optional sourceFileDepGraph = + SourceFileDepGraph::loadFromSwiftModuleBuffer(buffer); + if (!sourceFileDepGraph) + return None; + registerJob(Cmd); + auto changes = integrate(*sourceFileDepGraph, buffer.getBufferIdentifier()); + if (verifyFineGrainedDependencyGraphAfterEveryImport) + verify(); + if (emitFineGrainedDependencyDotFileAfterEveryImport) + emitDotFileForJob(diags, Cmd); + return changes; +} + bool ModuleDepGraph::haveAnyNodesBeenTraversedIn(const Job *cmd) const { std::string swiftDeps = getSwiftDeps(cmd).str(); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 0c37b5679e379..3497a404bcde4 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -20,10 +20,11 @@ #define SWIFT_SERIALIZATION_MODULEFORMAT_H #include "swift/AST/Decl.h" +#include "swift/AST/FineGrainedDependencyFormat.h" #include "swift/AST/Types.h" +#include "llvm/ADT/PointerEmbeddedInt.h" #include "llvm/Bitcode/RecordLayout.h" #include "llvm/Bitstream/BitCodes.h" -#include "llvm/ADT/PointerEmbeddedInt.h" namespace swift { namespace serialization { @@ -724,8 +725,10 @@ enum BlockID { /// This is part of a stable format and should not be renumbered. /// /// Though we strive to keep the format stable, breaking the format of - /// .swiftsourceinfo doesn't have consequences as serious as breaking the format - /// of .swiftdoc because .swiftsourceinfo file is for local development use only. + /// .swiftsourceinfo doesn't have consequences as serious as breaking the + /// format + /// of .swiftdoc because .swiftsourceinfo file is for local development use + /// only. /// /// \sa decl_locs_block DECL_LOCS_BLOCK_ID, @@ -733,7 +736,8 @@ enum BlockID { /// The incremental dependency information block. /// /// This is part of a stable format and should not be renumbered. - INCREMENTAL_INFORMATION_BLOCK_ID = 196, + INCREMENTAL_INFORMATION_BLOCK_ID = + fine_grained_dependencies::INCREMENTAL_INFORMATION_BLOCK_ID, }; /// The record types within the control block. From 69d04aad08da3245f87c6a2df35869126d48b55e Mon Sep 17 00:00:00 2001 From: Butta Date: Tue, 22 Sep 2020 14:45:47 +0530 Subject: [PATCH 052/745] [linux] remove absolute rpath of /usr/lib/swift/linux added to many shared libraries This was presumably added as a backup, in case the libraries in a toolchain couldn't be found, but will not work well, so take it out. --- cmake/modules/AddSwift.cmake | 2 +- stdlib/cmake/modules/AddSwiftStdlib.cmake | 2 +- tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index ec61d2abb836a..904e281a0eb1d 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -461,7 +461,7 @@ function(add_swift_host_library name) INSTALL_NAME_DIR "@rpath") elseif(SWIFT_HOST_VARIANT_SDK STREQUAL LINUX) set_target_properties(${name} PROPERTIES - INSTALL_RPATH "$ORIGIN:/usr/lib/swift/linux") + INSTALL_RPATH "$ORIGIN") elseif(SWIFT_HOST_VARIANT_SDK STREQUAL CYGWIN) set_target_properties(${name} PROPERTIES INSTALL_RPATH "$ORIGIN:/usr/lib/swift/cygwin") diff --git a/stdlib/cmake/modules/AddSwiftStdlib.cmake b/stdlib/cmake/modules/AddSwiftStdlib.cmake index 0131417cbc71a..cbf16b98f0657 100644 --- a/stdlib/cmake/modules/AddSwiftStdlib.cmake +++ b/stdlib/cmake/modules/AddSwiftStdlib.cmake @@ -998,7 +998,7 @@ function(_add_swift_target_library_single target name) elseif("${SWIFTLIB_SINGLE_SDK}" STREQUAL "LINUX") set_target_properties("${target}" PROPERTIES - INSTALL_RPATH "$ORIGIN:/usr/lib/swift/linux") + INSTALL_RPATH "$ORIGIN") elseif("${SWIFTLIB_SINGLE_SDK}" STREQUAL "CYGWIN") set_target_properties("${target}" PROPERTIES diff --git a/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake b/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake index caba3e1d7bd7c..d182a1d1f0f87 100644 --- a/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake +++ b/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake @@ -99,7 +99,7 @@ macro(add_sourcekit_library name) if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") if(SOURCEKITLIB_SHARED) set_target_properties(${name} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE) - set_target_properties(${name} PROPERTIES INSTALL_RPATH "$ORIGIN/../lib/swift/linux:/usr/lib/swift/linux") + set_target_properties(${name} PROPERTIES INSTALL_RPATH "$ORIGIN/../lib/swift/linux") endif() endif() From e35f0601443636cfa9ee75f1ede41e4cd09b9782 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 25 Sep 2020 11:45:58 -0600 Subject: [PATCH 053/745] Install Incremental External Dependency Integration Code An incremental build involving incremental external dependencies behaves as a hybrid between an external dependency and a normal swiftdeps-laden Swift file. In the simplest case, we will fall back to the behavior of a plain external dependency today. That is, we will check its timestamp, then schedule all jobs that involve these external dependencies if it is out of date. Where things get interesting is when cross-module incremental builds are enabled. In such a case, we know that a previous compiler has already emitted serialized swiftdeps information inside of a swiftmodule file. Moreover, we know that that swiftmodule file was loaded by the build of the current swift module. Finally, thanks to the previous stack of commits, we now know exactly how to extract this information from the swiftmodule file. To bring this all home, we unpack incremental dependency information from external dependencies, then integrate them into the current dependency graph - as though they were any other swiftdeps file. This neatly extends the single-module incremental logic to the multi-module case. --- lib/Driver/Compilation.cpp | 94 +++++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 26 deletions(-) diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index 183892b3abbe2..1b1e57753b784 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -1113,21 +1113,6 @@ namespace driver { } } - void forEachOutOfDateIncrementalExternalDependency( - const bool forRanges, - function_ref consumeExternalSwiftDeps) { - for (StringRef dependency : - getIncrementalExternalDependencies(forRanges)) { - // If the dependency has been modified since the oldest built file, - // or if we can't stat it for some reason (perhaps it's been - // deleted?), trigger rebuilds through the dependency graph. - llvm::sys::fs::file_status depStatus; - if (llvm::sys::fs::status(dependency, depStatus) || - Comp.getLastBuildTime() < depStatus.getLastModificationTime()) - consumeExternalSwiftDeps(dependency); - } - } - CommandSet collectCascadedJobsFromDependencyGraph( const CommandSet &InitialCascadingCommands, const bool forRanges) { CommandSet CascadedJobs; @@ -1164,18 +1149,68 @@ namespace driver { collectIncrementalExternallyDependentJobsFromDependencyGraph( const bool forRanges) { SmallVector ExternallyDependentJobs; - // Check all cross-module dependencies as well. - forEachOutOfDateIncrementalExternalDependency( - forRanges, [&](StringRef dependency) { - // If the dependency has been modified since the oldest built file, - // or if we can't stat it for some reason (perhaps it's been - // deleted?), trigger rebuilds through the dependency graph. - for (const Job *marked : - markExternalInDepGraph(dependency, forRanges)) - ExternallyDependentJobs.push_back(marked); - }); + auto fallbackToExternalBehavior = [&](StringRef external) { + for (const auto cmd : + markIncrementalExternalInDepGraph(external, forRanges)) { + ExternallyDependentJobs.push_back(cmd); + } + }; + + for (auto external : getFineGrainedDepGraph(forRanges) + .getIncrementalExternalDependencies()) { + llvm::sys::fs::file_status depStatus; + // Can't `stat` this dependency? Treat it as a plain external + // dependency and drop schedule all of its consuming jobs to run. + if (llvm::sys::fs::status(external, depStatus)) { + fallbackToExternalBehavior(external); + continue; + } + + // Is this module out of date? If not, just keep searching. + if (Comp.getLastBuildTime() >= depStatus.getLastModificationTime()) + continue; + + // Can we run a cross-module incremental build at all? If not, fallback. + if (!Comp.getEnableCrossModuleIncrementalBuild()) { + fallbackToExternalBehavior(external); + continue; + } + + // If loading the buffer fails, the user may have deleted this external + // dependency or it could have become corrupted. We need to + // pessimistically schedule a rebuild to get dependent jobs to drop + // this dependency from their swiftdeps files if possible. + auto buffer = llvm::MemoryBuffer::getFile(external); + if (!buffer) { + fallbackToExternalBehavior(external); + continue; + } + + // Cons up a fake `Job` to satisfy the incremental job tracing + // code's internal invariants. + Job fakeJob(Comp.getDerivedOutputFileMap(), external); + auto subChanges = + getFineGrainedDepGraph(forRanges).loadFromSwiftModuleBuffer( + &fakeJob, *buffer.get(), Comp.getDiags()); + + // If the incremental dependency graph failed to load, fall back to + // treating this as plain external job. + if (!subChanges.hasValue()) { + fallbackToExternalBehavior(external); + continue; + } + + for (auto *CMD : + getFineGrainedDepGraph(forRanges) + .findJobsToRecompileWhenNodesChange(subChanges.getValue())) { + if (CMD == &fakeJob) { + continue; + } + ExternallyDependentJobs.push_back(CMD); + } + } noteBuildingJobs(ExternallyDependentJobs, forRanges, - "because of external dependencies"); + "because of incremental external dependencies"); return ExternallyDependentJobs; } @@ -1645,6 +1680,13 @@ namespace driver { .findExternallyDependentUntracedJobs(externalDependency); } + std::vector + markIncrementalExternalInDepGraph(StringRef externalDependency, + const bool forRanges) { + return getFineGrainedDepGraph(forRanges) + .findIncrementalExternallyDependentUntracedJobs(externalDependency); + } + std::vector findJobsToRecompileWhenWholeJobChanges( const Job *Cmd, const bool forRanges) { return getFineGrainedDepGraph(forRanges) From f07c7d15feb4444c603e404f8e10274f275a7104 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 25 Sep 2020 11:17:19 -0700 Subject: [PATCH 054/745] [Type checker] Eliminate a use-after-free due to C++ temporaries. Found by Brent using ASan, thank you! --- lib/Sema/BuilderTransform.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index fdcf4ee263a92..a5a60d07c150c 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -1979,7 +1979,8 @@ void swift::printFunctionBuilderBuildFunction( componentTypeString = "<#Component#>"; // Render the code. - ExtraIndentStreamPrinter printer(out, stubIndent.getValueOr(std::string())); + std::string stubIndentStr = stubIndent.getValueOr(std::string()); + ExtraIndentStreamPrinter printer(out, stubIndentStr); // If we're supposed to provide a full stub, add a newline and the introducer // keywords. From 49d93c58a71eaa5690621bd2eec922d47a05f662 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Fri, 25 Sep 2020 11:49:07 -0700 Subject: [PATCH 055/745] Revert "[ownership] Move OME after CopyForwarding (#33106)" This reverts commit ef972eb34de9b540971ebfd4d6b2718ad32931cb. --- lib/SILOptimizer/PassManager/PassPipeline.cpp | 9 ++++----- test/SILOptimizer/copyforward_ossa.sil | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index f93042ad18bcb..fda5b5fe7f589 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -283,10 +283,6 @@ void addFunctionPasses(SILPassPipelinePlan &P, // splits up copy_addr. P.addCopyForwarding(); - // We earlier eliminated ownership if we are not compiling the stdlib. Now - // handle the stdlib functions. - P.addNonTransparentFunctionOwnershipModelEliminator(); - // Optimize copies from a temporary (an "l-value") to a destination. P.addTempLValueOpt(); @@ -493,7 +489,10 @@ static void addHighLevelFunctionPipeline(SILPassPipelinePlan &P) { // FIXME: update EagerSpecializer to be a function pass! P.addEagerSpecializer(); - // stdlib ownership model elimination is done within addFunctionPasses + // We earlier eliminated ownership if we are not compiling the stdlib. Now + // handle the stdlib functions. + P.addNonTransparentFunctionOwnershipModelEliminator(); + addFunctionPasses(P, OptimizationLevelKind::HighLevel); addHighLevelLoopOptPasses(P); diff --git a/test/SILOptimizer/copyforward_ossa.sil b/test/SILOptimizer/copyforward_ossa.sil index 11be2a5140875..9719fc0ec92cc 100644 --- a/test/SILOptimizer/copyforward_ossa.sil +++ b/test/SILOptimizer/copyforward_ossa.sil @@ -265,7 +265,7 @@ sil [ossa] @_TFSq4someU__fMGSqQ__FQ_GSqQ__ : $@convention(thin) <τ_0_0> (@in τ sil [ossa] @_TFsoi2neU__FTGSqQ__Vs26_OptionalNilComparisonType_Sb : $@convention(thin) <τ_0_0> (@in Optional<τ_0_0>, _OptionalNilComparisonType) -> Bool -// CHECK-LABEL: sil hidden [ossa] @option_init : +// CHECK-LABEL: sil hidden [ossa] @option_init // CHECK: alloc_stack // CHECK: alloc_stack // CHECK: alloc_stack From 77a76a842206ac320881d4759c2fbc08b0c609d7 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Fri, 25 Sep 2020 11:49:52 -0700 Subject: [PATCH 056/745] Revert "Merge pull request #33205 from meg-gupta/ometofunctionpass" This reverts commit 8dbac48c18c343de3150dda26b3a6d5d8d8714f6, reversing changes made to c22ba90700a7123365f01b1ea4776051e4af198c. --- include/swift/SIL/SILCloner.h | 7 -- include/swift/SIL/SILModule.h | 22 ----- .../SILOptimizer/PassManager/PassManager.h | 5 -- lib/SIL/IR/SILModule.cpp | 2 - .../Mandatory/OwnershipModelEliminator.cpp | 88 +++++++++---------- lib/SILOptimizer/PassManager/PassManager.cpp | 20 +---- test/DebugInfo/dbgvalue-insertpt.swift | 12 ++- test/IRGen/big_types_corner_cases.swift | 6 ++ test/SILOptimizer/prespecialize.swift | 6 ++ 9 files changed, 68 insertions(+), 100 deletions(-) diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 56d5d19903a30..c27d345ee8ae2 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1542,13 +1542,6 @@ template void SILCloner::visitUncheckedValueCastInst( UncheckedValueCastInst *Inst) { getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - if (!getBuilder().hasOwnership()) { - recordClonedInstruction(Inst, getBuilder().createUncheckedBitwiseCast( - getOpLocation(Inst->getLoc()), - getOpValue(Inst->getOperand()), - getOpType(Inst->getType()))); - return; - } recordClonedInstruction(Inst, getBuilder().createUncheckedValueCast( getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()), diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index 33f7b07ff875f..0610893425ba2 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -263,15 +263,6 @@ class SILModule { /// to ensure that the module is serialized only once. bool serialized; - /// Set if we have registered a deserialization notification handler for - /// lowering ownership in non transparent functions. - /// This gets set in NonTransparent OwnershipModelEliminator pass. - bool regDeserializationNotificationHandlerForNonTransparentFuncOME; - /// Set if we have registered a deserialization notification handler for - /// lowering ownership in transparent functions. - /// This gets set in OwnershipModelEliminator pass. - bool regDeserializationNotificationHandlerForAllFuncOME; - /// Action to be executed for serializing the SILModule. ActionCallback SerializeSILAction; @@ -310,19 +301,6 @@ class SILModule { deserializationNotificationHandlers.erase(handler); } - bool hasRegisteredDeserializationNotificationHandlerForNonTransparentFuncOME() { - return regDeserializationNotificationHandlerForNonTransparentFuncOME; - } - bool hasRegisteredDeserializationNotificationHandlerForAllFuncOME() { - return regDeserializationNotificationHandlerForAllFuncOME; - } - void setRegisteredDeserializationNotificationHandlerForNonTransparentFuncOME() { - regDeserializationNotificationHandlerForNonTransparentFuncOME = true; - } - void setRegisteredDeserializationNotificationHandlerForAllFuncOME() { - regDeserializationNotificationHandlerForAllFuncOME = true; - } - /// Add a delete notification handler \p Handler to the module context. void registerDeleteNotificationHandler(DeleteNotificationHandler* Handler); diff --git a/include/swift/SILOptimizer/PassManager/PassManager.h b/include/swift/SILOptimizer/PassManager/PassManager.h index 8c82ff0169dc0..b72969c96a01b 100644 --- a/include/swift/SILOptimizer/PassManager/PassManager.h +++ b/include/swift/SILOptimizer/PassManager/PassManager.h @@ -281,11 +281,6 @@ class SILPassManager { /// Run the passes in Transform from \p FromTransIdx to \p ToTransIdx. void runFunctionPasses(unsigned FromTransIdx, unsigned ToTransIdx); - /// Helper function to check if the function pass should be run mandatorily - /// All passes in mandatory pass pipeline and ownership model elimination are - /// mandatory function passes. - bool isMandatoryFunctionPass(SILFunctionTransform *); - /// A helper function that returns (based on SIL stage and debug /// options) whether we should continue running passes. bool continueTransforming(); diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 2966cb89e7ba7..c70e4865c3641 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -95,8 +95,6 @@ class SILModule::SerializationCallback final SILModule::SILModule(llvm::PointerUnion context, Lowering::TypeConverter &TC, const SILOptions &Options) : Stage(SILStage::Raw), Options(Options), serialized(false), - regDeserializationNotificationHandlerForNonTransparentFuncOME(false), - regDeserializationNotificationHandlerForAllFuncOME(false), SerializeSILAction(), Types(TC) { assert(!context.isNull()); if (auto *file = context.dyn_cast()) { diff --git a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp index 0e57fc67d8a6e..769ee6c2cef75 100644 --- a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp +++ b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp @@ -370,7 +370,7 @@ static void prepareSILFunctionForOptimization(ModuleDecl *, SILFunction *F) { namespace { -struct OwnershipModelEliminator : SILFunctionTransform { +struct OwnershipModelEliminator : SILModuleTransform { bool SkipTransparent; bool SkipStdlibModule; @@ -379,11 +379,10 @@ struct OwnershipModelEliminator : SILFunctionTransform { void run() override { if (DumpBefore.size()) { - getFunction()->dump(DumpBefore.c_str()); + getModule()->dump(DumpBefore.c_str()); } - auto *F = getFunction(); - auto &Mod = getFunction()->getModule(); + auto &Mod = *getModule(); // If we are supposed to skip the stdlib module and we are in the stdlib // module bail. @@ -391,38 +390,42 @@ struct OwnershipModelEliminator : SILFunctionTransform { return; } - if (!F->hasOwnership()) - return; - - // If we were asked to not strip ownership from transparent functions in - // /our/ module, return. - if (SkipTransparent && F->isTransparent()) - return; - - // Verify here to make sure ownership is correct before we strip. - { - // Add a pretty stack trace entry to tell users who see a verification - // failure triggered by this verification check that they need to re-run - // with -sil-verify-all to actually find the pass that introduced the - // verification error. - // - // DISCUSSION: This occurs due to the crash from the verification - // failure happening in the pass itself. This causes us to dump the - // SILFunction and emit a msg that this pass (OME) is the culprit. This - // is generally correct for most passes, but not for OME since we are - // verifying before we have even modified the function to ensure that - // all ownership invariants have been respected before we lower - // ownership from the function. - llvm::PrettyStackTraceString silVerifyAllMsgOnFailure( - "Found verification error when verifying before lowering " - "ownership. Please re-run with -sil-verify-all to identify the " - "actual pass that introduced the verification error."); - F->verify(); - } + for (auto &F : Mod) { + // If F does not have ownership, skip it. We have no further work to do. + if (!F.hasOwnership()) + continue; + + // If we were asked to not strip ownership from transparent functions in + // /our/ module, continue. + if (SkipTransparent && F.isTransparent()) + continue; + + // Verify here to make sure ownership is correct before we strip. + { + // Add a pretty stack trace entry to tell users who see a verification + // failure triggered by this verification check that they need to re-run + // with -sil-verify-all to actually find the pass that introduced the + // verification error. + // + // DISCUSSION: This occurs due to the crash from the verification + // failure happening in the pass itself. This causes us to dump the + // SILFunction and emit a msg that this pass (OME) is the culprit. This + // is generally correct for most passes, but not for OME since we are + // verifying before we have even modified the function to ensure that + // all ownership invariants have been respected before we lower + // ownership from the function. + llvm::PrettyStackTraceString silVerifyAllMsgOnFailure( + "Found verification error when verifying before lowering " + "ownership. Please re-run with -sil-verify-all to identify the " + "actual pass that introduced the verification error."); + F.verify(); + } - if (stripOwnership(*F)) { - auto InvalidKind = SILAnalysis::InvalidationKind::BranchesAndInstructions; - invalidateAnalysis(InvalidKind); + if (stripOwnership(F)) { + auto InvalidKind = + SILAnalysis::InvalidationKind::BranchesAndInstructions; + invalidateAnalysis(&F, InvalidKind); + } } // If we were asked to strip transparent, we are at the beginning of the @@ -432,19 +435,12 @@ struct OwnershipModelEliminator : SILFunctionTransform { FunctionBodyDeserializationNotificationHandler; std::unique_ptr ptr; if (SkipTransparent) { - if (!Mod.hasRegisteredDeserializationNotificationHandlerForNonTransparentFuncOME()) { - ptr.reset(new NotificationHandlerTy( - prepareNonTransparentSILFunctionForOptimization)); - Mod.registerDeserializationNotificationHandler(std::move(ptr)); - Mod.setRegisteredDeserializationNotificationHandlerForNonTransparentFuncOME(); - } + ptr.reset(new NotificationHandlerTy( + prepareNonTransparentSILFunctionForOptimization)); } else { - if (!Mod.hasRegisteredDeserializationNotificationHandlerForAllFuncOME()) { - ptr.reset(new NotificationHandlerTy(prepareSILFunctionForOptimization)); - Mod.registerDeserializationNotificationHandler(std::move(ptr)); - Mod.setRegisteredDeserializationNotificationHandlerForAllFuncOME(); - } + ptr.reset(new NotificationHandlerTy(prepareSILFunctionForOptimization)); } + Mod.registerDeserializationNotificationHandler(std::move(ptr)); } }; diff --git a/lib/SILOptimizer/PassManager/PassManager.cpp b/lib/SILOptimizer/PassManager/PassManager.cpp index f315eec53f65d..e6bee2886a953 100644 --- a/lib/SILOptimizer/PassManager/PassManager.cpp +++ b/lib/SILOptimizer/PassManager/PassManager.cpp @@ -383,24 +383,11 @@ void SILPassManager::dumpPassInfo(const char *Title, unsigned TransIdx, llvm::dbgs() << '\n'; } -bool SILPassManager::isMandatoryFunctionPass(SILFunctionTransform *sft) { - return isMandatory || sft->getPassKind() == - PassKind::NonTransparentFunctionOwnershipModelEliminator || - sft->getPassKind() == PassKind::OwnershipModelEliminator || - sft->getPassKind() == - PassKind::NonStdlibNonTransparentFunctionOwnershipModelEliminator; -} - void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { assert(analysesUnlocked() && "Expected all analyses to be unlocked!"); auto *SFT = cast(Transformations[TransIdx]); - - if (!F->shouldOptimize() && !isMandatoryFunctionPass(SFT)) { - return; - } - SFT->injectPassManager(this); SFT->injectFunction(F); @@ -408,10 +395,9 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { DebugPrintEnabler DebugPrint(NumPassesRun); // If nothing changed since the last run of this pass, we can skip this - // pass if it is not mandatory + // pass. CompletedPasses &completedPasses = CompletedPassesMap[F]; - if (!isMandatoryFunctionPass(SFT) && - completedPasses.test((size_t)SFT->getPassKind()) && + if (completedPasses.test((size_t)SFT->getPassKind()) && !SILDisableSkippingPasses) { if (SILPrintPassName) dumpPassInfo("(Skip)", TransIdx, F); @@ -527,7 +513,7 @@ runFunctionPasses(unsigned FromTransIdx, unsigned ToTransIdx) { // Only include functions that are definitions, and which have not // been intentionally excluded from optimization. - if (F.isDefinition()) + if (F.isDefinition() && (isMandatory || F.shouldOptimize())) FunctionWorklist.push_back(*I); } diff --git a/test/DebugInfo/dbgvalue-insertpt.swift b/test/DebugInfo/dbgvalue-insertpt.swift index 469f1223f4a93..0d670e4bb7dba 100644 --- a/test/DebugInfo/dbgvalue-insertpt.swift +++ b/test/DebugInfo/dbgvalue-insertpt.swift @@ -1,9 +1,19 @@ -// RUN: %target-swift-frontend -g -emit-ir -Xllvm '-sil-inline-never-functions=next' %s | %FileCheck %s +// RUN: %target-swift-frontend -g -emit-ir %s | %FileCheck %s // FIXME: This test should be testing a non-shadow-copied value instead. for i in 0 ..< 3 { // CHECK: %[[ALLOCA:[0-9]+]] = alloca %TSiSg // CHECK: %i.debug = alloca i{{32|64}} // CHECK-NEXT: call void @llvm.dbg.declare(metadata i{{32|64}}* %i.debug, // CHECK-SAME: metadata ![[I:[0-9]+]], + // CHECK: call swiftcc{{.*}} @{{.*}}next{{.*}} + // CHECK: %[[LD:[0-9]+]] = load i{{32|64}}, i{{32|64}}* + // CHECK: br i1 {{%.*}}, label %[[FAIL:.*]], label %[[SUCCESS:.*]], + // + // CHECK: [[SUCCESS]]: + // CHECK: br label %[[NEXT_BB:.*]], + // + // CHECK: [[NEXT_BB]]: + // CHECK: %[[PHI_VAL:.*]] = phi i{{32|64}} [ %[[LD]], %[[SUCCESS]] ] + // CHECK: store i{{32|64}} %[[PHI_VAL]], i{{32|64}}* %i.debug // CHECK: ![[I]] = !DILocalVariable(name: "i", } diff --git a/test/IRGen/big_types_corner_cases.swift b/test/IRGen/big_types_corner_cases.swift index 7acfaa13c601d..f956b23f7effe 100644 --- a/test/IRGen/big_types_corner_cases.swift +++ b/test/IRGen/big_types_corner_cases.swift @@ -204,6 +204,12 @@ public func testGetFunc() { // CHECK: [[CALL1:%.*]] = call {{.*}} @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$sSayy22big_types_corner_cases9BigStructVcSgGMD" // CHECK: [[CALL2:%.*]] = call i8** @"$sSayy22big_types_corner_cases9BigStructVcSgGSayxGSlsWl // CHECK: call swiftcc void @"$sSlsE10firstIndex5where0B0QzSgSb7ElementQzKXE_tKF"(%TSq.{{.*}}* noalias nocapture sret %{{[0-9]+}}, i8* bitcast ({{.*}}* @"$s22big_types_corner_cases9BigStruct{{.*}}_TRTA{{(\.ptrauth)?}}" to i8*), %swift.opaque* %{{[0-9]+}}, %swift.type* %{{[0-9]+}}, i8** [[CALL2]] + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} hidden swiftcc void @"$s22big_types_corner_cases7TestBigC5test2yyF"(%T22big_types_corner_cases7TestBigC* swiftself %0) +// CHECK: [[CALL1:%.*]] = call {{.*}} @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$sSaySS2ID_y22big_types_corner_cases9BigStructVcSg7handlertGMD" +// CHECK: [[CALL2:%.*]] = call i8** @"$sSaySS2ID_y22big_types_corner_cases9BigStructVcSg7handlertGSayxGSlsWl" +// CHECK: call swiftcc void @"$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF"(%Ts16IndexingIteratorV{{.*}}* noalias nocapture sret {{.*}}, %swift.type* [[CALL1]], i8** [[CALL2]], %swift.opaque* noalias nocapture swiftself {{.*}}) +// CHECK: ret void class TestBig { typealias Handler = (BigStruct) -> Void diff --git a/test/SILOptimizer/prespecialize.swift b/test/SILOptimizer/prespecialize.swift index 1cbc165230253..f9c27e9e02cb9 100644 --- a/test/SILOptimizer/prespecialize.swift +++ b/test/SILOptimizer/prespecialize.swift @@ -7,6 +7,12 @@ // CHECK-LABEL: sil [noinline] @$s13prespecialize4test_4sizeySaySiGz_SitF // +// function_ref specialized Collection.makeIterator() -> IndexingIterator +// CHECK: function_ref @$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyFSnySiG_Tg5 +// +// function_ref specialized IndexingIterator.next() -> A.Element? +// CHECK: function_ref @$ss16IndexingIteratorV4next7ElementQzSgyFSnySiG_Tg5 +// // Look for generic specialization of Swift.Array.subscript.getter : (Swift.Int) -> A // CHECK: function_ref @$sSayxSicigSi_Tg5 // CHECK: return From 70a6d2c7c524c1c9576ba1f0d31f53dfa0191b4a Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Sun, 13 Sep 2020 14:41:03 +0300 Subject: [PATCH 057/745] WinSDK: extract version into a separate submodule Currently winver.h gets included into `WinSDK.WinSock2`, however its usages might not be related to sockets, and it requires linking against `Version.Lib` --- stdlib/public/Platform/winsdk.modulemap | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/stdlib/public/Platform/winsdk.modulemap b/stdlib/public/Platform/winsdk.modulemap index 8d7d464d27ffd..3e82eca6ec172 100644 --- a/stdlib/public/Platform/winsdk.modulemap +++ b/stdlib/public/Platform/winsdk.modulemap @@ -127,6 +127,14 @@ module WinSDK [system] { header "timezoneapi.h" export * } + + // api-ms-win-core-version-l1-1-0.dll + module version { + header "winver.h" + export * + + link "Version.Lib" + } } module AuthZ { From 14e6772dc04cf778ba393bebc21a0a8b8ada3603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Tue, 22 Sep 2020 17:38:52 -0700 Subject: [PATCH 058/745] Consider inherited platform unavailability to silence diagnostics rdar://68597591 --- lib/Sema/TypeCheckAvailability.cpp | 5 +++- test/attr/attr_availability_maccatalyst.swift | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index b8784d9e62978..f99a9415f551c 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -1566,7 +1566,10 @@ static bool isInsideCompatibleUnavailableDeclaration( auto IsUnavailable = [platform](const Decl *D) { auto EnclosingUnavailable = D->getAttrs().getUnavailable(D->getASTContext()); - return EnclosingUnavailable && EnclosingUnavailable->Platform == platform; + return EnclosingUnavailable && + (EnclosingUnavailable->Platform == platform || + inheritsAvailabilityFromPlatform(platform, + EnclosingUnavailable->Platform)); }; return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, IsUnavailable); diff --git a/test/attr/attr_availability_maccatalyst.swift b/test/attr/attr_availability_maccatalyst.swift index 07fd5229471d1..f69cc8aea1b3d 100644 --- a/test/attr/attr_availability_maccatalyst.swift +++ b/test/attr/attr_availability_maccatalyst.swift @@ -140,3 +140,27 @@ protocol P: Builtin.AnyObject { } extension X: P {} + +// Test platform inheritance for iOS unavailability. +// rdar://68597591 + +@available(iOS, unavailable) +public struct UnavailableOniOS { } // expected-note 2 {{'UnavailableOniOS' has been explicitly marked unavailable here}} + +@available(iOS, unavailable) +func unavailableOniOS(_ p: UnavailableOniOS) { } // ok + +func functionUsingAnUnavailableType(_ p: UnavailableOniOS) { } // expected-error {{'UnavailableOniOS' is unavailable in iOS}} + +public extension UnavailableOniOS { } // expected-error {{'UnavailableOniOS' is unavailable in iOS}} + +@available(iOS, unavailable) +public extension UnavailableOniOS { // ok + func someMethod(_ p: UnavailableOniOS) { } +} + +@available(iOS, unavailable) +@available(macCatalyst, introduced: 13.0) +public struct AvailableOnMacCatalyst { } + +public extension AvailableOnMacCatalyst { } // ok From 29ce77209ce349a4c22740101c0d9e4e3b4ae15d Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 24 Sep 2020 18:34:03 -0400 Subject: [PATCH 059/745] AST: Convert ConstructorDecl::getDelegatingOrChainedInitKind() into a request This method had a messy contract: - Setting the diags parameter to nullptr inhibited caching - The initExpr out parameter could only used if no result had yet been cached Let's instead use the request evaluator here. --- include/swift/AST/ASTTypeIDZone.def | 3 + include/swift/AST/ASTTypeIDs.h | 3 + include/swift/AST/Decl.h | 71 ++++---- include/swift/AST/Evaluator.h | 7 + include/swift/AST/Expr.h | 1 + include/swift/AST/TypeCheckRequests.h | 23 +++ include/swift/AST/TypeCheckerTypeIDZone.def | 3 + lib/AST/Decl.cpp | 175 ++------------------ lib/AST/Expr.cpp | 5 + lib/AST/TypeCheckRequests.cpp | 18 ++ lib/SILGen/SILGenConstructor.cpp | 8 +- lib/Sema/CSDiagnostics.cpp | 4 +- lib/Sema/TypeCheckDecl.cpp | 137 +++++++++++++++ lib/Sema/TypeCheckStmt.cpp | 18 +- 14 files changed, 260 insertions(+), 216 deletions(-) diff --git a/include/swift/AST/ASTTypeIDZone.def b/include/swift/AST/ASTTypeIDZone.def index 0bffc3a56697b..6bbe1a459ceb5 100644 --- a/include/swift/AST/ASTTypeIDZone.def +++ b/include/swift/AST/ASTTypeIDZone.def @@ -17,6 +17,8 @@ SWIFT_TYPEID(ActorIsolation) SWIFT_TYPEID(AncestryFlags) +SWIFT_TYPEID(BodyInitKind) +SWIFT_TYPEID(BodyInitKindAndExpr) SWIFT_TYPEID(CtorInitializerKind) SWIFT_TYPEID(FunctionBuilderBodyPreCheck) SWIFT_TYPEID(GenericSignature) @@ -35,6 +37,7 @@ SWIFT_TYPEID(TypePair) SWIFT_TYPEID(TypeWitnessAndDecl) SWIFT_TYPEID(Witness) SWIFT_TYPEID_NAMED(AbstractFunctionDecl *, AbstractFunctionDecl) +SWIFT_TYPEID_NAMED(ApplyExpr *, ApplyExpr) SWIFT_TYPEID_NAMED(ClosureExpr *, ClosureExpr) SWIFT_TYPEID_NAMED(CodeCompletionCallbacksFactory *, CodeCompletionCallbacksFactory) diff --git a/include/swift/AST/ASTTypeIDs.h b/include/swift/AST/ASTTypeIDs.h index e59255ec7ec28..8bbb7366f965d 100644 --- a/include/swift/AST/ASTTypeIDs.h +++ b/include/swift/AST/ASTTypeIDs.h @@ -24,6 +24,9 @@ namespace swift { class AbstractFunctionDecl; class ActorIsolation; +class ApplyExpr; +enum class BodyInitKind; +struct BodyInitKindAndExpr; class BraceStmt; class ClosureExpr; class CodeCompletionCallbacksFactory; diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index fbb34a351e924..050d4e5fd8a1e 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -451,14 +451,7 @@ class alignas(1 << DeclAlignInBits) Decl { /// Whether we have computed the above. IsTransparentComputed : 1); - SWIFT_INLINE_BITFIELD(ConstructorDecl, AbstractFunctionDecl, 3+1+1, - /// The body initialization kind (+1), or zero if not yet computed. - /// - /// This value is cached but is not serialized, because it is a property - /// of the definition of the constructor that is useful only to semantic - /// analysis and SIL generation. - ComputedBodyInitKind : 3, - + SWIFT_INLINE_BITFIELD(ConstructorDecl, AbstractFunctionDecl, 1+1, /// Whether this constructor can fail, by building an Optional type. Failable : 1, @@ -6770,6 +6763,37 @@ enum class CtorInitializerKind { Factory }; +/// Specifies the kind of initialization call performed within the body +/// of the constructor, e.g., self.init or super.init. +enum class BodyInitKind { + /// There are no calls to self.init or super.init. + None, + /// There is a call to self.init, which delegates to another (peer) + /// initializer. + Delegating, + /// There is a call to super.init, which chains to a superclass initializer. + Chained, + /// There are no calls to self.init or super.init explicitly in the body of + /// the constructor, but a 'super.init' call will be implicitly added + /// by semantic analysis. + ImplicitChained +}; + +struct BodyInitKindAndExpr { + BodyInitKind initKind; + ApplyExpr *initExpr; + + BodyInitKindAndExpr() : initKind(BodyInitKind::None), initExpr(nullptr) {} + + BodyInitKindAndExpr(BodyInitKind initKind, ApplyExpr *initExpr) + : initKind(initKind), initExpr(initExpr) {} + + friend bool operator==(BodyInitKindAndExpr lhs, BodyInitKindAndExpr rhs) { + return (lhs.initKind == rhs.initKind && + lhs.initExpr == rhs.initExpr); + } +}; + /// ConstructorDecl - Declares a constructor for a type. For example: /// /// \code @@ -6818,38 +6842,11 @@ class ConstructorDecl : public AbstractFunctionDecl { ParamDecl **getImplicitSelfDeclStorage() { return &SelfDecl; } - /// Specifies the kind of initialization call performed within the body - /// of the constructor, e.g., self.init or super.init. - enum class BodyInitKind { - /// There are no calls to self.init or super.init. - None, - /// There is a call to self.init, which delegates to another (peer) - /// initializer. - Delegating, - /// There is a call to super.init, which chains to a superclass initializer. - Chained, - /// There are no calls to self.init or super.init explicitly in the body of - /// the constructor, but a 'super.init' call will be implicitly added - /// by semantic analysis. - ImplicitChained - }; - /// Determine whether the body of this constructor contains any delegating /// or superclass initializations (\c self.init or \c super.init, /// respectively) within its body. - /// - /// \param diags If non-null, this check will ensure that the constructor - /// body is consistent in its use of delegation vs. chaining and emit any - /// diagnostics through the given diagnostic engine. - /// - /// \param init If non-null and there is an explicit \c self.init or - /// \c super.init within the body, will be set to point at that - /// initializer. - BodyInitKind getDelegatingOrChainedInitKind(DiagnosticEngine *diags, - ApplyExpr **init = nullptr) const; - void clearCachedDelegatingOrChainedInitKind() { - Bits.ConstructorDecl.ComputedBodyInitKind = 0; - } + BodyInitKindAndExpr getDelegatingOrChainedInitKind() const; + void clearCachedDelegatingOrChainedInitKind(); /// Whether this constructor is required. bool isRequired() const { diff --git a/include/swift/AST/Evaluator.h b/include/swift/AST/Evaluator.h index 212682d0bc121..ed7b6a92aed6a 100644 --- a/include/swift/AST/Evaluator.h +++ b/include/swift/AST/Evaluator.h @@ -322,6 +322,13 @@ class Evaluator { cache.insert({AnyRequest(request), std::move(output)}); } + /// Do not introduce new callers of this function. + template::type* = nullptr> + void clearCachedOutput(const Request &request) { + cache.erase(AnyRequest(request)); + } + /// Clear the cache stored within this evaluator. /// /// Note that this does not clear the caches of requests that use external diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 2bf600146c65f..b747589cb5078 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -5793,6 +5793,7 @@ Expr *packSingleArgument( void simple_display(llvm::raw_ostream &out, const ClosureExpr *CE); void simple_display(llvm::raw_ostream &out, const DefaultArgumentExpr *expr); +void simple_display(llvm::raw_ostream &out, const Expr *expr); SourceLoc extractNearestSourceLoc(const DefaultArgumentExpr *expr); diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 4b6108ccf8a01..ed84c49bbfa8e 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -211,6 +211,29 @@ class InitKindRequest : CtorInitializerKind evaluate(Evaluator &evaluator, ConstructorDecl *decl) const; +public: +// Caching. + bool isCached() const { return true; } +}; + +void simple_display(llvm::raw_ostream &out, BodyInitKind initKind); +void simple_display(llvm::raw_ostream &out, BodyInitKindAndExpr initKindAndExpr); + +/// Computes the kind of initializer call (self.init or super.init) performed +/// in the body of a \c ConstructorDecl +class BodyInitKindRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + BodyInitKindAndExpr evaluate(Evaluator &evaluator, ConstructorDecl *decl) const; + public: // Caching. bool isCached() const { return true; } diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index f18b77cdc9241..ea2a99e3ca459 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -121,6 +121,9 @@ SWIFT_REQUEST(TypeChecker, InheritsSuperclassInitializersRequest, bool(ClassDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, InitKindRequest, CtorInitializerKind(ConstructorDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, BodyInitKindRequest, + BodyInitKindAndExpr(ConstructorDecl *), Cached, + NoLocationInfo) SWIFT_REQUEST(TypeChecker, InterfaceTypeRequest, Type(ValueDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, IsAccessorTransparentRequest, bool(AccessorDecl *), diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 05a1f253c27f3..a330b63faedcb 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5463,8 +5463,8 @@ bool VarDecl::isSettable(const DeclContext *UseDC, // If this is a convenience initializer (i.e. one that calls // self.init), then let properties are never mutable in it. They are // only mutable in designated initializers. - if (CD->getDelegatingOrChainedInitKind(nullptr) == - ConstructorDecl::BodyInitKind::Delegating) + auto initKindAndExpr = CD->getDelegatingOrChainedInitKind(); + if (initKindAndExpr.initKind == BodyInitKind::Delegating) return false; return true; @@ -7522,7 +7522,6 @@ ConstructorDecl::ConstructorDecl(DeclName Name, SourceLoc ConstructorLoc, if (BodyParams) setParameters(BodyParams); - Bits.ConstructorDecl.ComputedBodyInitKind = 0; Bits.ConstructorDecl.HasStubImplementation = 0; Bits.ConstructorDecl.Failable = Failable; @@ -7750,169 +7749,17 @@ CtorInitializerKind ConstructorDecl::getInitKind() const { CtorInitializerKind::Designated); } -ConstructorDecl::BodyInitKind -ConstructorDecl::getDelegatingOrChainedInitKind(DiagnosticEngine *diags, - ApplyExpr **init) const { +BodyInitKindAndExpr +ConstructorDecl::getDelegatingOrChainedInitKind() const { + return evaluateOrDefault(getASTContext().evaluator, + BodyInitKindRequest{const_cast(this)}, + BodyInitKindAndExpr()); assert(hasBody() && "Constructor does not have a definition"); +} - if (init) - *init = nullptr; - - // If we already computed the result, return it. - if (Bits.ConstructorDecl.ComputedBodyInitKind) { - auto Kind = static_cast( - Bits.ConstructorDecl.ComputedBodyInitKind - 1); - assert((Kind == BodyInitKind::None || !init) && - "can't return cached result with the init expr"); - return Kind; - } - - - struct FindReferenceToInitializer : ASTWalker { - const ConstructorDecl *Decl; - BodyInitKind Kind = BodyInitKind::None; - ApplyExpr *InitExpr = nullptr; - DiagnosticEngine *Diags; - - FindReferenceToInitializer(const ConstructorDecl *decl, - DiagnosticEngine *diags) - : Decl(decl), Diags(diags) { } - - bool walkToDeclPre(class Decl *D) override { - // Don't walk into further nominal decls. - return !isa(D); - } - - std::pair walkToExprPre(Expr *E) override { - // Don't walk into closures. - if (isa(E)) - return { false, E }; - - // Look for calls of a constructor on self or super. - auto apply = dyn_cast(E); - if (!apply) - return { true, E }; - - auto Callee = apply->getSemanticFn(); - - Expr *arg; - - if (isa(Callee)) { - arg = apply->getArg(); - } else if (auto *CRE = dyn_cast(Callee)) { - arg = CRE->getArg(); - } else if (auto *dotExpr = dyn_cast(Callee)) { - if (dotExpr->getName().getBaseName() != DeclBaseName::createConstructor()) - return { true, E }; - - arg = dotExpr->getBase(); - } else { - // Not a constructor call. - return { true, E }; - } - - // Look for a base of 'self' or 'super'. - BodyInitKind myKind; - if (arg->isSuperExpr()) - myKind = BodyInitKind::Chained; - else if (arg->isSelfExprOf(Decl, /*sameBase*/true)) - myKind = BodyInitKind::Delegating; - else { - // We're constructing something else. - return { true, E }; - } - - if (Kind == BodyInitKind::None) { - Kind = myKind; - - // If we're not emitting diagnostics, we're done. - if (!Diags) - return { false, nullptr }; - - InitExpr = apply; - return { true, E }; - } - - assert(Diags && "Failed to abort traversal early"); - - // If the kind changed, complain. - if (Kind != myKind) { - // The kind changed. Complain. - Diags->diagnose(E->getLoc(), diag::init_delegates_and_chains); - Diags->diagnose(InitExpr->getLoc(), diag::init_delegation_or_chain, - Kind == BodyInitKind::Chained); - } - - return { true, E }; - } - }; - - FindReferenceToInitializer finder(this, diags); - getBody()->walk(finder); - - // get the kind out of the finder. - auto Kind = finder.Kind; - - auto *NTD = getDeclContext()->getSelfNominalTypeDecl(); - - // Protocol extension and enum initializers are always delegating. - if (Kind == BodyInitKind::None) { - if (isa(NTD) || isa(NTD)) { - Kind = BodyInitKind::Delegating; - } - } - - // Struct initializers that cannot see the layout of the struct type are - // always delegating. This occurs if the struct type is not fixed layout, - // and the constructor is either inlinable or defined in another module. - if (Kind == BodyInitKind::None && isa(NTD)) { - // Note: This is specifically not using isFormallyResilient. We relax this - // rule for structs in non-resilient modules so that they can have inlinable - // constructors, as long as those constructors don't reference private - // declarations. - if (NTD->isResilient() && - getResilienceExpansion() == ResilienceExpansion::Minimal) { - Kind = BodyInitKind::Delegating; - - } else if (isa(getDeclContext())) { - const ModuleDecl *containingModule = getParentModule(); - // Prior to Swift 5, cross-module initializers were permitted to be - // non-delegating. However, if the struct isn't fixed-layout, we have to - // be delegating because, well, we don't know the layout. - // A dynamic replacement is permitted to be non-delegating. - if (NTD->isResilient() || - (containingModule->getASTContext().isSwiftVersionAtLeast(5) && - !getAttrs().getAttribute())) { - if (containingModule != NTD->getParentModule()) - Kind = BodyInitKind::Delegating; - } - } - } - - // If we didn't find any delegating or chained initializers, check whether - // the initializer was explicitly marked 'convenience'. - if (Kind == BodyInitKind::None && getAttrs().hasAttribute()) - Kind = BodyInitKind::Delegating; - - // If we still don't know, check whether we have a class with a superclass: it - // gets an implicit chained initializer. - if (Kind == BodyInitKind::None) { - if (auto classDecl = getDeclContext()->getSelfClassDecl()) { - if (classDecl->hasSuperclass()) - Kind = BodyInitKind::ImplicitChained; - } - } - - // Cache the result if it is trustworthy. - if (diags) { - auto *mutableThis = const_cast(this); - mutableThis->Bits.ConstructorDecl.ComputedBodyInitKind = - static_cast(Kind) + 1; - if (init) - *init = finder.InitExpr; - } - - return Kind; +void ConstructorDecl::clearCachedDelegatingOrChainedInitKind() { + getASTContext().evaluator.clearCachedOutput( + BodyInitKindRequest{const_cast(this)}); } SourceRange DestructorDecl::getSourceRange() const { diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index e91138730e8b9..36707fa48d2d7 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -2463,6 +2463,11 @@ void swift::simple_display(llvm::raw_ostream &out, simple_display(out, expr->getDefaultArgsOwner().getDecl()); } +void swift::simple_display(llvm::raw_ostream &out, + const Expr *expr) { + out << "expression"; +} + SourceLoc swift::extractNearestSourceLoc(const DefaultArgumentExpr *expr) { return expr->getLoc(); } diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 112ab9c89d1f7..0b6c3eb544d1c 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1485,3 +1485,21 @@ void swift::simple_display( break; } } + +void swift::simple_display( + llvm::raw_ostream &out, BodyInitKind initKind) { + switch (initKind) { + case BodyInitKind::None: out << "none"; return; + case BodyInitKind::Delegating: out << "delegating"; return; + case BodyInitKind::Chained: out << "chained"; return; + case BodyInitKind::ImplicitChained: out << "implicit_chained"; return; + } + llvm_unreachable("Bad body init kind"); +} + +void swift::simple_display( + llvm::raw_ostream &out, BodyInitKindAndExpr initKindAndExpr) { + simple_display(out, initKindAndExpr.initKind); + out << " "; + simple_display(out, initKindAndExpr.initExpr); +} diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index dbbb66b81f35e..162ee955f83a7 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -303,8 +303,8 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) { return emitImplicitValueConstructor(*this, ctor); // True if this constructor delegates to a peer constructor with self.init(). - bool isDelegating = ctor->getDelegatingOrChainedInitKind(nullptr) == - ConstructorDecl::BodyInitKind::Delegating; + bool isDelegating = ctor->getDelegatingOrChainedInitKind().initKind == + BodyInitKind::Delegating; // Get the 'self' decl and type. VarDecl *selfDecl = ctor->getImplicitSelfDecl(); @@ -637,8 +637,8 @@ void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) { // True if this constructor delegates to a peer constructor with self.init(). bool isDelegating = false; if (!ctor->hasStubImplementation()) { - isDelegating = ctor->getDelegatingOrChainedInitKind(nullptr) == - ConstructorDecl::BodyInitKind::Delegating; + isDelegating = ctor->getDelegatingOrChainedInitKind().initKind == + BodyInitKind::Delegating; } // Set up the 'self' argument. If this class has a superclass, we set up diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index ff16379d72d5a..ae732cff956bf 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1428,8 +1428,8 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() { if (auto *ctor = dyn_cast(getDC())) { if (auto *baseRef = dyn_cast(member->getBase())) { if (baseRef->getDecl() == ctor->getImplicitSelfDecl() && - ctor->getDelegatingOrChainedInitKind(nullptr) == - ConstructorDecl::BodyInitKind::Delegating) { + ctor->getDelegatingOrChainedInitKind().initKind == + BodyInitKind::Delegating) { emitDiagnosticAt(loc, diag::assignment_let_property_delegating_init, member->getName()); if (auto overload = getOverloadChoiceIfAvailable( diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index c0b80def44141..cf2f2f702fc7a 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -472,6 +472,143 @@ InitKindRequest::evaluate(Evaluator &evaluator, ConstructorDecl *decl) const { return CtorInitializerKind::Designated; } +BodyInitKindAndExpr +BodyInitKindRequest::evaluate(Evaluator &evaluator, + ConstructorDecl *decl) const { + + struct FindReferenceToInitializer : ASTWalker { + const ConstructorDecl *Decl; + BodyInitKind Kind = BodyInitKind::None; + ApplyExpr *InitExpr = nullptr; + ASTContext &ctx; + + FindReferenceToInitializer(const ConstructorDecl *decl, + ASTContext &ctx) + : Decl(decl), ctx(ctx) { } + + bool walkToDeclPre(class Decl *D) override { + // Don't walk into further nominal decls. + return !isa(D); + } + + std::pair walkToExprPre(Expr *E) override { + // Don't walk into closures. + if (isa(E)) + return { false, E }; + + // Look for calls of a constructor on self or super. + auto apply = dyn_cast(E); + if (!apply) + return { true, E }; + + auto Callee = apply->getSemanticFn(); + + Expr *arg; + + if (isa(Callee)) { + arg = apply->getArg(); + } else if (auto *CRE = dyn_cast(Callee)) { + arg = CRE->getArg(); + } else if (auto *dotExpr = dyn_cast(Callee)) { + if (dotExpr->getName().getBaseName() != DeclBaseName::createConstructor()) + return { true, E }; + + arg = dotExpr->getBase(); + } else { + // Not a constructor call. + return { true, E }; + } + + // Look for a base of 'self' or 'super'. + BodyInitKind myKind; + if (arg->isSuperExpr()) + myKind = BodyInitKind::Chained; + else if (arg->isSelfExprOf(Decl, /*sameBase*/true)) + myKind = BodyInitKind::Delegating; + else { + // We're constructing something else. + return { true, E }; + } + + if (Kind == BodyInitKind::None) { + Kind = myKind; + + InitExpr = apply; + return { true, E }; + } + + // If the kind changed, complain. + if (Kind != myKind) { + // The kind changed. Complain. + ctx.Diags.diagnose(E->getLoc(), diag::init_delegates_and_chains); + ctx.Diags.diagnose(InitExpr->getLoc(), diag::init_delegation_or_chain, + Kind == BodyInitKind::Chained); + } + + return { true, E }; + } + }; + + auto &ctx = decl->getASTContext(); + FindReferenceToInitializer finder(decl, ctx); + decl->getBody()->walk(finder); + + // get the kind out of the finder. + auto Kind = finder.Kind; + + auto *NTD = decl->getDeclContext()->getSelfNominalTypeDecl(); + + // Protocol extension and enum initializers are always delegating. + if (Kind == BodyInitKind::None) { + if (isa(NTD) || isa(NTD)) { + Kind = BodyInitKind::Delegating; + } + } + + // Struct initializers that cannot see the layout of the struct type are + // always delegating. This occurs if the struct type is not fixed layout, + // and the constructor is either inlinable or defined in another module. + if (Kind == BodyInitKind::None && isa(NTD)) { + // Note: This is specifically not using isFormallyResilient. We relax this + // rule for structs in non-resilient modules so that they can have inlinable + // constructors, as long as those constructors don't reference private + // declarations. + if (NTD->isResilient() && + decl->getResilienceExpansion() == ResilienceExpansion::Minimal) { + Kind = BodyInitKind::Delegating; + + } else if (isa(decl->getDeclContext())) { + // Prior to Swift 5, cross-module initializers were permitted to be + // non-delegating. However, if the struct isn't fixed-layout, we have to + // be delegating because, well, we don't know the layout. + // A dynamic replacement is permitted to be non-delegating. + if (NTD->isResilient() || + (ctx.isSwiftVersionAtLeast(5) && + !decl->getAttrs().getAttribute())) { + if (decl->getParentModule() != NTD->getParentModule()) + Kind = BodyInitKind::Delegating; + } + } + } + + // If we didn't find any delegating or chained initializers, check whether + // the initializer was explicitly marked 'convenience'. + if (Kind == BodyInitKind::None && + decl->getAttrs().hasAttribute()) + Kind = BodyInitKind::Delegating; + + // If we still don't know, check whether we have a class with a superclass: it + // gets an implicit chained initializer. + if (Kind == BodyInitKind::None) { + if (auto classDecl = decl->getDeclContext()->getSelfClassDecl()) { + if (classDecl->hasSuperclass()) + Kind = BodyInitKind::ImplicitChained; + } + } + + return BodyInitKindAndExpr(Kind, finder.InitExpr); +} + bool ProtocolRequiresClassRequest::evaluate(Evaluator &evaluator, ProtocolDecl *decl) const { diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index c9abcefaaa9d4..06860ed87c7ca 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1704,30 +1704,30 @@ static void checkClassConstructorBody(ClassDecl *classDecl, ASTContext &ctx = classDecl->getASTContext(); bool wantSuperInitCall = false; bool isDelegating = false; - ApplyExpr *initExpr = nullptr; - switch (ctor->getDelegatingOrChainedInitKind(&ctx.Diags, &initExpr)) { - case ConstructorDecl::BodyInitKind::Delegating: + auto initKindAndExpr = ctor->getDelegatingOrChainedInitKind(); + switch (initKindAndExpr.initKind) { + case BodyInitKind::Delegating: isDelegating = true; wantSuperInitCall = false; break; - case ConstructorDecl::BodyInitKind::Chained: - checkSuperInit(ctor, initExpr, false); + case BodyInitKind::Chained: + checkSuperInit(ctor, initKindAndExpr.initExpr, false); /// A convenience initializer cannot chain to a superclass constructor. if (ctor->isConvenienceInit()) { - ctx.Diags.diagnose(initExpr->getLoc(), + ctx.Diags.diagnose(initKindAndExpr.initExpr->getLoc(), diag::delegating_convenience_super_init, ctor->getDeclContext()->getDeclaredInterfaceType()); } LLVM_FALLTHROUGH; - case ConstructorDecl::BodyInitKind::None: + case BodyInitKind::None: wantSuperInitCall = false; break; - case ConstructorDecl::BodyInitKind::ImplicitChained: + case BodyInitKind::ImplicitChained: wantSuperInitCall = true; break; } @@ -1743,7 +1743,7 @@ static void checkClassConstructorBody(ClassDecl *classDecl, .fixItInsert(ctor->getLoc(), "convenience "); } - ctx.Diags.diagnose(initExpr->getLoc(), diag::delegation_here); + ctx.Diags.diagnose(initKindAndExpr.initExpr->getLoc(), diag::delegation_here); } // An inlinable constructor in a class must always be delegating, From 6b98f8f3b5b9c786912d59f7775996292801c88e Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 25 Sep 2020 15:42:17 -0400 Subject: [PATCH 060/745] ASTScope: Add a new lookupSingleLocalDecl() entry point This is used in a few places that used to expect parsed but not yet type-checked code to contain DeclRefExprs that reference local bindings. Instead, we can call lookupSingleLocalDecl() with an UnresolvedDeclRefExpr instead. --- include/swift/AST/NameLookup.h | 8 +++++ lib/AST/UnqualifiedLookup.cpp | 57 +++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 5715546dc3383..6f5af437fcc9d 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -684,6 +684,14 @@ class ASTScope { static void unqualifiedLookup(SourceFile *, SourceLoc, namelookup::AbstractASTScopeDeclConsumer &); + /// Lookup that only finds local declarations and does not trigger + /// interface type computation. + static void lookupLocalDecls(SourceFile *, DeclName, SourceLoc, + SmallVectorImpl &); + + /// Returns the result if there is exactly one, nullptr otherwise. + static ValueDecl *lookupSingleLocalDecl(SourceFile *, DeclName, SourceLoc); + /// Entry point to record the visible statement labels from the given /// point. /// diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 2423bf48bd094..99aa0297fa9a7 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -778,4 +778,59 @@ unsigned UnqualifiedLookupFactory::lookupCounter = 0; // set to ~0 when not debugging const unsigned UnqualifiedLookupFactory::targetLookup = ~0; -#endif // NDEBUG +namespace { + +class ASTScopeDeclConsumerForLocalLookup + : public AbstractASTScopeDeclConsumer { + SmallVectorImpl &results; + DeclName name; + +public: + ASTScopeDeclConsumerForLocalLookup( + SmallVectorImpl &results, DeclName name) + : results(results), name(name) {} + + bool consume(ArrayRef values, DeclVisibilityKind vis, + NullablePtr baseDC) override { + for (auto *value: values) { + if (!value->getName().matchesRef(name)) + continue; + + results.push_back(value); + } + + return !results.empty(); + } + + bool lookInMembers(DeclContext *const, + NominalTypeDecl *const) override { + return true; + } + +#ifndef NDEBUG + void startingNextLookupStep() override {} + void finishingLookup(std::string) const override {} + bool isTargetLookup() const override { return false; } +#endif +}; + +} + +/// Lookup that only finds local declarations and does not trigger +/// interface type computation. +void ASTScope::lookupLocalDecls(SourceFile *sf, DeclName name, SourceLoc loc, + SmallVectorImpl &results) { + ASTScopeDeclConsumerForLocalLookup consumer(results, name); + ASTScope::unqualifiedLookup(sf, loc, consumer); +} + +ValueDecl *ASTScope::lookupSingleLocalDecl(SourceFile *sf, DeclName name, + SourceLoc loc) { + SmallVector result; + ASTScope::lookupLocalDecls(sf, name, loc, result); + if (result.size() != 1) + return nullptr; + return result[0]; +} + +#endif // NDEBUG \ No newline at end of file From dcafd585c133319189291b06e213165b6d28b49b Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 23 Sep 2020 22:21:02 -0400 Subject: [PATCH 061/745] Sema: Use ASTScope::lookupSingleLocalDecl() in MissingOptionalUnwrapFailure --- lib/Sema/CSDiagnostics.cpp | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index ae732cff956bf..0bf4c3dd6c121 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1195,7 +1195,12 @@ void MissingOptionalUnwrapFailure::offerForceUnwrapFixIt( } } +// FIXME: This walks a partially-type checked function body, which +// is not guaranteed to yield consistent results. We should come up +// with another way of performing this analysis, for example by moving +// it to a post-type checking pass in MiscDiagnostics. class VarDeclMultipleReferencesChecker : public ASTWalker { + DeclContext *DC; VarDecl *varDecl; int count; @@ -1204,11 +1209,30 @@ class VarDeclMultipleReferencesChecker : public ASTWalker { if (DRE->getDecl() == varDecl) ++count; } + + // FIXME: We can see UnresolvedDeclRefExprs here because we have + // not yet run preCheckExpression() on the entire function body + // yet. + // + // We could consider pre-checking more eagerly. + if (auto *UDRE = dyn_cast(E)) { + auto name = UDRE->getName(); + auto loc = UDRE->getLoc(); + if (name.isSimpleName(varDecl->getName()) && loc.isValid()) { + auto *otherDecl = + ASTScope::lookupSingleLocalDecl(DC->getParentSourceFile(), + name.getFullName(), loc); + if (otherDecl == varDecl) + ++count; + } + } + return { true, E }; } public: - VarDeclMultipleReferencesChecker(VarDecl *varDecl) : varDecl(varDecl),count(0) {} + VarDeclMultipleReferencesChecker(DeclContext *DC, VarDecl *varDecl) + : DC(DC), varDecl(varDecl),count(0) {} int referencesCount() { return count; } }; @@ -1267,12 +1291,10 @@ bool MissingOptionalUnwrapFailure::diagnoseAsError() { if (auto varDecl = dyn_cast(declRef->getDecl())) { bool singleUse = false; AbstractFunctionDecl *AFD = nullptr; - if (auto contextDecl = varDecl->getDeclContext()->getAsDecl()) { - if ((AFD = dyn_cast(contextDecl))) { - auto checker = VarDeclMultipleReferencesChecker(varDecl); - AFD->getBody()->walk(checker); - singleUse = checker.referencesCount() == 1; - } + if ((AFD = dyn_cast(varDecl->getDeclContext()))) { + auto checker = VarDeclMultipleReferencesChecker(getDC(), varDecl); + AFD->getBody()->walk(checker); + singleUse = checker.referencesCount() == 1; } PatternBindingDecl *binding = varDecl->getParentPatternBinding(); From 8ec878bb43cb69570cbec8fd4a60326018c55690 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 23 Sep 2020 23:23:17 -0400 Subject: [PATCH 062/745] Sema: Use ASTScope::lookupSingleLocalDecl() in CSGen's CollectVarRefs pass When parse-time lookup is disabled, we have to resolve UnresolvedDeclRefExprs inside the closure body, since there's no guarantee that preCheckExpression() has run yet. --- lib/Sema/CSGen.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index c69b28ca8efe7..48c6008d4ba09 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2552,6 +2552,24 @@ namespace { } } + // FIXME: We can see UnresolvedDeclRefExprs here because we have + // not yet run preCheckExpression() on the entire closure body + // yet. + // + // We could consider pre-checking more eagerly. + if (auto *declRef = dyn_cast(expr)) { + auto name = declRef->getName(); + auto loc = declRef->getLoc(); + if (name.isSimpleName() && loc.isValid()) { + auto *varDecl = dyn_cast_or_null( + ASTScope::lookupSingleLocalDecl(cs.DC->getParentSourceFile(), + name.getFullName(), loc)); + if (varDecl) + if (auto varType = cs.getTypeIfAvailable(varDecl)) + varType->getTypeVariables(varRefs); + } + } + return { true, expr }; } } collectVarRefs(CS); From ddc0cbd2d4ea1182428c0a88b88a5db2cd1ca3e6 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 25 Sep 2020 16:14:46 -0400 Subject: [PATCH 063/745] Sema: Use ASTScope::lookupSingleLocalDecl() in BodyInitKindRequest --- lib/Sema/TypeCheckDecl.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index cf2f2f702fc7a..3a1f7aaf921af 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -520,16 +520,33 @@ BodyInitKindRequest::evaluate(Evaluator &evaluator, } // Look for a base of 'self' or 'super'. - BodyInitKind myKind; + arg = arg->getSemanticsProvidingExpr(); + + auto myKind = BodyInitKind::None; if (arg->isSuperExpr()) myKind = BodyInitKind::Chained; else if (arg->isSelfExprOf(Decl, /*sameBase*/true)) myKind = BodyInitKind::Delegating; - else { - // We're constructing something else. - return { true, E }; + else if (auto *declRef = dyn_cast(arg)) { + // FIXME: We can see UnresolvedDeclRefExprs here because we have + // not yet run preCheckExpression() on the entire function body + // yet. + // + // We could consider pre-checking more eagerly. + auto name = declRef->getName(); + auto loc = declRef->getLoc(); + if (name.isSimpleName(ctx.Id_self)) { + auto *otherSelfDecl = + ASTScope::lookupSingleLocalDecl(Decl->getParentSourceFile(), + name.getFullName(), loc); + if (otherSelfDecl == Decl->getImplicitSelfDecl()) + myKind = BodyInitKind::Delegating; + } } + if (myKind == BodyInitKind::None) + return { true, E }; + if (Kind == BodyInitKind::None) { Kind = myKind; From 6a82f242ac540174111fb346e5d0c47946fd9d53 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 8 Sep 2020 16:26:51 -0400 Subject: [PATCH 064/745] Sema: Don't trigger ImplInfoRequest from TypeChecker::buildRefExpr() It's too early to do that here, because we may be building a reference to a ParamDecl that is not yet known to be inout, because the constraint solver has not run yet. Instead, always compute the access semantics in CSApply. --- lib/Sema/CSApply.cpp | 13 +++++++------ lib/Sema/TypeCheckExpr.cpp | 8 ++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index a6a75d32b73c8..5a3f6463147f5 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -482,8 +482,7 @@ namespace { public: /// Build a reference to the given declaration. Expr *buildDeclRef(SelectedOverload overload, DeclNameLoc loc, - ConstraintLocatorBuilder locator, bool implicit, - AccessSemantics semantics) { + ConstraintLocatorBuilder locator, bool implicit) { auto choice = overload.choice; assert(choice.getKind() != OverloadChoiceKind::DeclViaDynamic); auto *decl = choice.getDecl(); @@ -492,6 +491,9 @@ namespace { // Determine the declaration selected for this overloaded reference. auto &ctx = cs.getASTContext(); + auto semantics = decl->getAccessSemanticsFromContext(cs.DC, + /*isAccessOnSelf*/false); + // If this is a member of a nominal type, build a reference to the // member with an implied base type. if (decl->getDeclContext()->isTypeContext() && isa(decl)) { @@ -2686,7 +2688,7 @@ namespace { // Find the overload choice used for this declaration reference. auto selected = solution.getOverloadChoice(locator); return buildDeclRef(selected, expr->getNameLoc(), locator, - expr->isImplicit(), expr->getAccessSemantics()); + expr->isImplicit()); } Expr *visitSuperRefExpr(SuperRefExpr *expr) { @@ -2716,7 +2718,7 @@ namespace { auto selected = solution.getOverloadChoice(locator); return buildDeclRef(selected, expr->getNameLoc(), locator, - expr->isImplicit(), AccessSemantics::Ordinary); + expr->isImplicit()); } Expr *visitUnresolvedDeclRefExpr(UnresolvedDeclRefExpr *expr) { @@ -3036,8 +3038,7 @@ namespace { diagnoseDeprecatedConditionalConformanceOuterAccess( UDE, selected.choice.getDecl()); - return buildDeclRef(selected, nameLoc, memberLocator, implicit, - AccessSemantics::Ordinary); + return buildDeclRef(selected, nameLoc, memberLocator, implicit); } switch (selected.choice.getKind()) { diff --git a/lib/Sema/TypeCheckExpr.cpp b/lib/Sema/TypeCheckExpr.cpp index 7e8135083ea34..f2c289403cad7 100644 --- a/lib/Sema/TypeCheckExpr.cpp +++ b/lib/Sema/TypeCheckExpr.cpp @@ -601,10 +601,10 @@ Expr *TypeChecker::buildRefExpr(ArrayRef Decls, assert(!Decls.empty() && "Must have at least one declaration"); auto &Context = UseDC->getASTContext(); - if (Decls.size() == 1 && !isa(Decls[0]->getDeclContext())) { - auto semantics = Decls[0]->getAccessSemanticsFromContext(UseDC, - /*isAccessOnSelf*/false); - return new (Context) DeclRefExpr(Decls[0], NameLoc, Implicit, semantics); + + if (Decls.size() == 1) { + return new (Context) DeclRefExpr(Decls[0], NameLoc, Implicit, + AccessSemantics::Ordinary); } Decls = Context.AllocateCopy(Decls); From 1fb69a7d43f4c3aa58d88309d54537bce61f3d56 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 24 Sep 2020 22:39:49 -0700 Subject: [PATCH 065/745] [Diagnostics] Diagnose cases when it's impossible to infer type for `nil` Detect and diagnose situations when it's invalid to use `nil` literal without more context e.g. `_ = nil`, `nil as? Int`, or `_ = nil!`. --- lib/Sema/CSDiagnostics.cpp | 47 ++++++++++++++++++++++++++++++++++++++ lib/Sema/CSDiagnostics.h | 17 ++++++++++++++ lib/Sema/CSFix.cpp | 3 ++- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index ff16379d72d5a..dcc2ed3ce43ce 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -6945,3 +6945,50 @@ bool InvalidEmptyKeyPathFailure::diagnoseAsError() { emitDiagnostic(diag::expr_swift_keypath_empty); return true; } + +bool MissingContextualTypeForNil::diagnoseAsError() { + auto *expr = castToExpr(getAnchor()); + + // If this is a standalone `nil` literal expression e.g. + // `_ = nil`, let's diagnose it here because solver can't + // attempt any types for it. + auto *parentExpr = findParentExpr(expr); + + while (parentExpr && isa(parentExpr)) + parentExpr = findParentExpr(parentExpr); + + // In cases like `_ = nil?` AST would have `nil` + // wrapped in `BindOptionalExpr`. + if (parentExpr && isa(parentExpr)) + parentExpr = findParentExpr(parentExpr); + + if (parentExpr) { + // `_ = nil as? ...` + if (isa(parentExpr)) { + emitDiagnostic(diag::conditional_cast_from_nil); + return true; + } + + // `_ = nil!` + if (isa(parentExpr)) { + emitDiagnostic(diag::cannot_force_unwrap_nil_literal); + return true; + } + + // `_ = nil?` + if (isa(parentExpr)) { + emitDiagnostic(diag::unresolved_nil_literal); + return true; + } + // `_ = nil` + if (auto *assignment = dyn_cast(parentExpr)) { + if (isa(assignment->getDest())) { + emitDiagnostic(diag::unresolved_nil_literal); + return true; + } + } + } + + emitDiagnostic(diag::unresolved_nil_literal); + return true; +} diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 980a8a51b753b..c684aa2096959 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -2268,6 +2268,23 @@ class InvalidEmptyKeyPathFailure final : public FailureDiagnostic { bool diagnoseAsError() override; }; +/// Diagnose situations where there is no context to determine a +/// type of `nil` literal e.g. +/// +/// \code +/// let _ = nil +/// let _ = try nil +/// let _ = nil! +/// \endcode +class MissingContextualTypeForNil final : public FailureDiagnostic { +public: + MissingContextualTypeForNil(const Solution &solution, + ConstraintLocator *locator) + : FailureDiagnostic(solution, locator) {} + + bool diagnoseAsError() override; +}; + } // end namespace constraints } // end namespace swift diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 602cf3b22886e..cb1c2c621f989 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -1606,7 +1606,8 @@ IgnoreInvalidFunctionBuilderBody *IgnoreInvalidFunctionBuilderBody::create( bool SpecifyContextualTypeForNil::diagnose(const Solution &solution, bool asNote) const { - return false; + MissingContextualTypeForNil failure(solution, getLocator()); + return failure.diagnose(asNote); } SpecifyContextualTypeForNil * From 7d6a11021030731e4e617bb5a8a995d20dc65087 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 25 Sep 2020 11:53:43 -0700 Subject: [PATCH 066/745] [CSSimplify] Allow `optional object` constraint to look through holes If right-hand side (optional type) has been determined to be a hole, let's establish that optional object of a hole is a hole and continue searching for a solution. --- lib/Sema/CSSimplify.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 104964b5ead2a..1b667acf2c8ab 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -5893,6 +5893,12 @@ ConstraintSystem::simplifyOptionalObjectConstraint( } + if (optTy->isHole()) { + if (auto *typeVar = second->getAs()) + recordPotentialHole(typeVar); + return SolutionKind::Solved; + } + Type objectTy = optTy->getOptionalObjectType(); // If the base type is not optional, let's attempt a fix (if possible) // and assume that `!` is just not there. @@ -10072,7 +10078,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::SpecifyKeyPathRootType: case FixKind::SpecifyLabelToAssociateTrailingClosure: case FixKind::AllowKeyPathWithoutComponents: - case FixKind::IgnoreInvalidFunctionBuilderBody: { + case FixKind::IgnoreInvalidFunctionBuilderBody: + case FixKind::SpecifyContextualTypeForNil: { return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } From 1b5ce2b88f6c1adde6a30cb7b0b15955628a7fde Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 25 Sep 2020 11:55:48 -0700 Subject: [PATCH 067/745] [CSBindings] Start recording `SpecifyContextualTypeForNil` fix when `nil` is bound to a hole --- lib/Sema/CSBindings.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 7db71f632008a..5d1b193bf90e0 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -368,6 +368,15 @@ void ConstraintSystem::PotentialBindings::finalize( PotentiallyIncomplete = true; } + // Delay resolution of the `nil` literal to a hole until + // the very end to give it a change to be bound to some + // other type, just like code completion expression which + // relies solely on contextual information. + if (locator->directlyAt()) { + FullyBound = true; + PotentiallyIncomplete = true; + } + addPotentialBinding(PotentialBinding::forHole(TypeVar, locator)); } @@ -1268,6 +1277,8 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { return true; fix = SpecifyKeyPathRootType::create(cs, dstLocator); + } else if (dstLocator->directlyAt()) { + fix = SpecifyContextualTypeForNil::create(cs, dstLocator); } if (fix && cs.recordFix(fix)) From e30bdacd5792737cb1e31a9ba884b1bf6e513028 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 25 Sep 2020 12:06:55 -0700 Subject: [PATCH 068/745] [CSBindings] Adjust impact of an event when `nil` is bound to a hole --- lib/Sema/CSBindings.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 5d1b193bf90e0..f43d31d5f26dd 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -1239,6 +1239,8 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { cs.increaseScore(SK_Hole); ConstraintFix *fix = nullptr; + unsigned fixImpact = 1; + if (auto *GP = TypeVar->getImpl().getGenericParameter()) { // If it is represetative for a key path root, let's emit a more // specific diagnostic. @@ -1279,9 +1281,12 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { fix = SpecifyKeyPathRootType::create(cs, dstLocator); } else if (dstLocator->directlyAt()) { fix = SpecifyContextualTypeForNil::create(cs, dstLocator); + // This is a dramatic event, it means that there is absolutely + // no contextual information to resolve type of `nil`. + fixImpact = 10; } - if (fix && cs.recordFix(fix)) + if (fix && cs.recordFix(fix, fixImpact)) return true; } } From a2b3b545230df44734077af68f59adde65fc3067 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 25 Sep 2020 14:55:16 -0700 Subject: [PATCH 069/745] [CSGen] Rework constraint generation for `nil` to avoid failing --- lib/Sema/CSGen.cpp | 95 ++++--------------- test/Constraints/function_builder_diags.swift | 1 + 2 files changed, 19 insertions(+), 77 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index c69b28ca8efe7..8a1874d2f6b46 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1015,80 +1015,13 @@ namespace { } Type visitNilLiteralExpr(NilLiteralExpr *expr) { - auto &DE = CS.getASTContext().Diags; - // If this is a standalone `nil` literal expression e.g. - // `_ = nil`, let's diagnose it here because solver can't - // attempt any types for it. - auto *parentExpr = CS.getParentExpr(expr); - bool hasContextualType = bool(CS.getContextualType(expr)); - - while (parentExpr) { - if (!isa(parentExpr)) - break; - - // If there is a parent, use it, otherwise we need - // to check whether the last parent node in the chain - // had a contextual type associated with it because - // in situations like: - // - // \code - // func foo() -> Int? { - // return (nil) - // } - // \endcode - // - // parentheses around `nil` are significant. - if (auto *nextParent = CS.getParentExpr(parentExpr)) { - parentExpr = nextParent; - } else { - hasContextualType |= bool(CS.getContextualType(parentExpr)); - // Since current expression is an identity expr - // and there are no more parents, let's pretend - // that `nil` don't have a parent since parens - // are not semantically significant for further checks. - parentExpr = nullptr; - } - } - - // In cases like `_ = nil?` AST would have `nil` - // wrapped in `BindOptionalExpr`. - if (parentExpr && isa(parentExpr)) - parentExpr = CS.getParentExpr(parentExpr); - - if (parentExpr) { - // `_ = nil as? ...` - if (isa(parentExpr)) { - DE.diagnose(expr->getLoc(), diag::conditional_cast_from_nil); - return Type(); - } - - // `_ = nil!` - if (isa(parentExpr)) { - DE.diagnose(expr->getLoc(), diag::cannot_force_unwrap_nil_literal); - return Type(); - } - - // `_ = nil?` - if (isa(parentExpr)) { - DE.diagnose(expr->getLoc(), diag::unresolved_nil_literal); - return Type(); - } + auto literalTy = visitLiteralExpr(expr); + // Allow `nil` to be a hole so we can diagnose it via a fix + // if it turns out that there is no contextual information. + if (auto *typeVar = literalTy->getAs()) + CS.recordPotentialHole(typeVar); - // `_ = nil` - if (auto *assignment = dyn_cast(parentExpr)) { - if (isa(assignment->getDest())) { - DE.diagnose(expr->getLoc(), diag::unresolved_nil_literal); - return Type(); - } - } - } - - if (!parentExpr && !hasContextualType) { - DE.diagnose(expr->getLoc(), diag::unresolved_nil_literal); - return Type(); - } - - return visitLiteralExpr(expr); + return literalTy; } Type visitFloatLiteralExpr(FloatLiteralExpr *expr) { @@ -3024,11 +2957,19 @@ namespace { TVO_PrefersSubtypeBinding | TVO_CanBindToLValue | TVO_CanBindToNoEscape); - + + auto *valueExpr = expr->getSubExpr(); + // It's invalid to force unwrap `nil` literal e.g. `_ = nil!` or + // `_ = (try nil)!` and similar constructs. + if (auto *nilLiteral = dyn_cast( + valueExpr->getSemanticsProvidingExpr())) { + CS.recordFix(SpecifyContextualTypeForNil::create( + CS, CS.getConstraintLocator(nilLiteral))); + } + // The result is the object type of the optional subexpression. - CS.addConstraint(ConstraintKind::OptionalObject, - CS.getType(expr->getSubExpr()), objectTy, - locator); + CS.addConstraint(ConstraintKind::OptionalObject, CS.getType(valueExpr), + objectTy, locator); return objectTy; } diff --git a/test/Constraints/function_builder_diags.swift b/test/Constraints/function_builder_diags.swift index dd66006a93503..d2389a8cc8b58 100644 --- a/test/Constraints/function_builder_diags.swift +++ b/test/Constraints/function_builder_diags.swift @@ -440,6 +440,7 @@ struct TestConstraintGenerationErrors { @TupleBuilder var nilWithoutContext: String { let a = nil // expected-error {{'nil' requires a contextual type}} + "" } func buildTupleClosure() { From a1f4a43483c5c57e7879e40749dee3b4a3ec01ff Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Fri, 25 Sep 2020 17:01:25 -0500 Subject: [PATCH 070/745] [ownership] Teach the ownership verifier how to verify that guaranteed yielded values are only used within the coroutine's lifetime. I think validating this was an oversight from the bringup of coroutines. I discovered this while writing test cases for coroutine lifetime extension. I realized it was possible to write a test case that should have triggered this but was not. I added some tests to make sure that we continue to flag this in the future. rdar://69597888 --- lib/SIL/Verifier/SILOwnershipVerifier.cpp | 71 +++++++++++++++---- .../begin_apply_use_after_end_apply.sil | 67 +++++++++++++++++ 2 files changed, 123 insertions(+), 15 deletions(-) create mode 100644 test/SIL/ownership-verifier/begin_apply_use_after_end_apply.sil diff --git a/lib/SIL/Verifier/SILOwnershipVerifier.cpp b/lib/SIL/Verifier/SILOwnershipVerifier.cpp index 8490e003d920a..ef6c73efe433e 100644 --- a/lib/SIL/Verifier/SILOwnershipVerifier.cpp +++ b/lib/SIL/Verifier/SILOwnershipVerifier.cpp @@ -13,6 +13,7 @@ #define DEBUG_TYPE "sil-ownership-verifier" #include "LinearLifetimeCheckerPrivate.h" + #include "swift/AST/ASTContext.h" #include "swift/AST/AnyFunctionRef.h" #include "swift/AST/Decl.h" @@ -32,16 +33,20 @@ #include "swift/SIL/SILBuiltinVisitor.h" #include "swift/SIL/SILDebugScope.h" #include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/SILOpenedArchetypesTracker.h" #include "swift/SIL/SILVTable.h" #include "swift/SIL/SILVisitor.h" #include "swift/SIL/TypeLowering.h" + #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/PostOrderIterator.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" + #include using namespace swift; @@ -130,10 +135,11 @@ class SILValueOwnershipChecker { bool gatherNonGuaranteedUsers(SmallVectorImpl &lifetimeEndingUsers, SmallVectorImpl ®ularUsers); - bool checkValueWithoutLifetimeEndingUses(); + bool checkValueWithoutLifetimeEndingUses(ArrayRef regularUsers); bool checkFunctionArgWithoutLifetimeEndingUses(SILFunctionArgument *arg); - bool checkYieldWithoutLifetimeEndingUses(BeginApplyResult *yield); + bool checkYieldWithoutLifetimeEndingUses(BeginApplyResult *yield, + ArrayRef regularUsers); bool isGuaranteedFunctionArgWithLifetimeEndingUses( SILFunctionArgument *arg, @@ -501,25 +507,56 @@ bool SILValueOwnershipChecker::checkFunctionArgWithoutLifetimeEndingUses( } bool SILValueOwnershipChecker::checkYieldWithoutLifetimeEndingUses( - BeginApplyResult *yield) { + BeginApplyResult *yield, ArrayRef regularUses) { switch (yield->getOwnershipKind()) { - case ValueOwnershipKind::Guaranteed: case ValueOwnershipKind::Unowned: case ValueOwnershipKind::None: return true; case ValueOwnershipKind::Owned: + if (deadEndBlocks.isDeadEnd(yield->getParent()->getParent())) + return true; + + return !errorBuilder.handleMalformedSIL([&] { + llvm::errs() << "Owned yield without life ending uses!\n" + << "Value: " << *yield << '\n'; + }); + case ValueOwnershipKind::Guaranteed: + // NOTE: If we returned false here, we would catch any error caught below as + // an out of lifetime use of the yielded value. That being said, that would + // be confusing from a code perspective since we would be validating + // something that did not have a /real/ lifetime ending use (one could + // consider the end_apply to be a pseudo-lifetime ending uses) along a code + // path that is explicitly trying to do that. break; } - if (deadEndBlocks.isDeadEnd(yield->getParent()->getParent())) + // If we have a guaranteed value, make sure that all uses are before our + // end_yield. + SmallVector coroutineEndUses; + for (auto *use : yield->getParent()->getTokenResult()->getUses()) { + coroutineEndUses.push_back(use); + } + + assert(visitedBlocks.empty()); + LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); + auto linearLifetimeResult = + checker.checkValue(yield, coroutineEndUses, regularUses, errorBuilder); + if (linearLifetimeResult.getFoundError()) { + // We return true here even if we find an error since we want to only emit + // this error for the value rather than continue and go down the "has + // consuming use" path. This is to work around any confusion that maybe + // caused by end_apply/abort_apply acting as a pseudo-ending lifetime use. + result = true; return true; + } - return !errorBuilder.handleMalformedSIL([&] { - llvm::errs() << "Owned yield without life ending uses!\n" - << "Value: " << *yield << '\n'; - }); + // Otherwise, we do not set result to have a value and return since all of our + // guaranteed value's uses are appropriate. + return true; } -bool SILValueOwnershipChecker::checkValueWithoutLifetimeEndingUses() { + +bool SILValueOwnershipChecker::checkValueWithoutLifetimeEndingUses( + ArrayRef regularUses) { LLVM_DEBUG(llvm::dbgs() << "No lifetime ending users?! Bailing early.\n"); if (auto *arg = dyn_cast(value)) { if (checkFunctionArgWithoutLifetimeEndingUses(arg)) { @@ -528,9 +565,7 @@ bool SILValueOwnershipChecker::checkValueWithoutLifetimeEndingUses() { } if (auto *yield = dyn_cast(value)) { - if (checkYieldWithoutLifetimeEndingUses(yield)) { - return true; - } + return checkYieldWithoutLifetimeEndingUses(yield, regularUses); } // Check if we are a guaranteed subobject. In such a case, we should never @@ -622,6 +657,7 @@ bool SILValueOwnershipChecker::checkUses() { // 1. A trivial typed value. // 2. An address type value. // 3. A guaranteed function argument. + // 4. A yielded guaranteed value. // // In the first two cases, it is easy to see that there is nothing further to // do but return false. @@ -630,8 +666,13 @@ bool SILValueOwnershipChecker::checkUses() { // more. Specifically, we should have /no/ lifetime ending uses of a // guaranteed function argument, since a guaranteed function argument should // outlive the current function always. - if (lifetimeEndingUsers.empty() && checkValueWithoutLifetimeEndingUses()) { - return false; + // + // In the case of a yielded guaranteed value, we need to validate that all + // regular uses of the value are within the co + if (lifetimeEndingUsers.empty()) { + if (checkValueWithoutLifetimeEndingUses(regularUsers)) + return false; + return true; } LLVM_DEBUG(llvm::dbgs() << " Found lifetime ending users! Performing " diff --git a/test/SIL/ownership-verifier/begin_apply_use_after_end_apply.sil b/test/SIL/ownership-verifier/begin_apply_use_after_end_apply.sil new file mode 100644 index 0000000000000..898c39decd9c0 --- /dev/null +++ b/test/SIL/ownership-verifier/begin_apply_use_after_end_apply.sil @@ -0,0 +1,67 @@ +// RUN: %target-sil-opt -sil-ownership-verifier-enable-testing -ownership-verifier-textual-error-dumper -enable-sil-verify-all=0 -o /dev/null %s 2>&1 | %FileCheck %s +// REQUIRES: asserts + +sil_stage canonical + +import Builtin + +class Klass {} + +sil @guaranteed_yield_coroutine : $@yield_once @convention(thin) () -> @yields @guaranteed Klass +sil @owned_yield_coroutine : $@yield_once @convention(thin) () -> @yields @owned Klass + +sil @use_klass : $@convention(thin) (@guaranteed Klass) -> () + +// CHECK-LABEL: Error#: 0. Begin Error in Function: 'guaranteed_coroutine_caller' +// CHECK: Found outside of lifetime use?! +// CHECK: Value: (**%3**, %4) = begin_apply %0() : $@yield_once @convention(thin) () -> @yields @guaranteed Klass // user: %6 +// CHECK: Consuming User: end_apply %4 // id: %5 +// CHECK: Non Consuming User: %6 = apply %2(%3) : $@convention(thin) (@guaranteed Klass) -> () +// CHECK: Block: bb0 +// CHECK: Error#: 0. End Error in Function: 'guaranteed_coroutine_caller' + +// CHECK-LABEL: Error#: 1. Begin Error in Function: 'guaranteed_coroutine_caller' +// CHECK: Owned yield without life ending uses! +// CHECK: Value: (**%7**, %8) = begin_apply %1() : $@yield_once @convention(thin) () -> @yields @owned Klass // user: %10 +// CHECK: Error#: 1. End Error in Function: 'guaranteed_coroutine_caller' + +// CHECK-LABEL: Error#: 2. Begin Error in Function: 'guaranteed_coroutine_caller' +// CHECK: Found outside of lifetime use?! +// CHECK: Value: (**%11**, %12) = begin_apply %1() : $@yield_once @convention(thin) () -> @yields @owned Klass // users: %15, %13 +// CHECK: Consuming User: destroy_value %11 : $Klass // id: %13 +// CHECK: Non Consuming User: %15 = apply %2(%11) : $@convention(thin) (@guaranteed Klass) -> () +// CHECK: Block: bb0 +// CHECK: Error#: 2. End Error in Function: 'guaranteed_coroutine_caller' + +// CHECK-LABEL: Error#: 3. Begin Error in Function: 'guaranteed_coroutine_caller' +// CHECK: Owned yield without life ending uses! +// CHECK: Value: (**%16**, %17) = begin_apply %1() : $@yield_once @convention(thin) () -> @yields @owned Klass // user: %18 +// CHECK: Error#: 3. End Error in Function: 'guaranteed_coroutine_caller' + +sil [ossa] @guaranteed_coroutine_caller : $@convention(thin) () -> () { +bb0: + %0 = function_ref @guaranteed_yield_coroutine : $@yield_once @convention(thin) () -> @yields @guaranteed Klass + %1 = function_ref @owned_yield_coroutine : $@yield_once @convention(thin) () -> @yields @owned Klass + + %user_func = function_ref @use_klass : $@convention(thin) (@guaranteed Klass) -> () + + (%0a, %0b) = begin_apply %0() : $@yield_once @convention(thin) () -> @yields @guaranteed Klass + end_apply %0b + apply %user_func(%0a) : $@convention(thin) (@guaranteed Klass) -> () + + (%val1, %tok1) = begin_apply %1() : $@yield_once @convention(thin) () -> @yields @owned Klass + end_apply %tok1 + apply %user_func(%val1) : $@convention(thin) (@guaranteed Klass) -> () + + (%val2, %tok2) = begin_apply %1() : $@yield_once @convention(thin) () -> @yields @owned Klass + destroy_value %val2 : $Klass + end_apply %tok2 + apply %user_func(%val2) : $@convention(thin) (@guaranteed Klass) -> () + + (%val3, %tok3) = begin_apply %1() : $@yield_once @convention(thin) () -> @yields @owned Klass + apply %user_func(%val3) : $@convention(thin) (@guaranteed Klass) -> () + end_apply %tok3 + + %9999 = tuple() + return %9999 : $() +} \ No newline at end of file From f96d6d348679f75313ac97a0e8451a34e3ec10fa Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 25 Sep 2020 15:56:18 -0700 Subject: [PATCH 071/745] [Diagnostics] Remove obsolete special case for `_ = nil` --- lib/Sema/CSDiagnostics.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index dcc2ed3ce43ce..ca1c9ef9a5493 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -6980,13 +6980,6 @@ bool MissingContextualTypeForNil::diagnoseAsError() { emitDiagnostic(diag::unresolved_nil_literal); return true; } - // `_ = nil` - if (auto *assignment = dyn_cast(parentExpr)) { - if (isa(assignment->getDest())) { - emitDiagnostic(diag::unresolved_nil_literal); - return true; - } - } } emitDiagnostic(diag::unresolved_nil_literal); From 4d61b710da706e5904dc8ff538ac66ed7474e042 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 25 Sep 2020 18:09:37 -0600 Subject: [PATCH 072/745] [Gardening] Rename Incremental/PrivateDependencies to Incremental/Dependencies --- .../Inputs/InterestingType.swift | 0 .../private-function-fine.swift | 0 .../private-function-return-type-fine.swift | 0 .../private-protocol-conformer-ext-fine.swift | 0 .../private-protocol-conformer-fine.swift | 0 .../private-struct-member-fine.swift | 0 .../private-subscript-fine.swift | 0 .../private-typealias-fine.swift | 0 .../{PrivateDependencies => Dependencies}/private-var-fine.swift | 0 .../reference-dependencies-consistency-fine.swift | 0 .../reference-dependencies-dynamic-lookup-fine.swift | 0 .../reference-dependencies-errors.swift | 0 .../reference-dependencies-fine.swift | 0 .../reference-dependencies-members-fine.swift | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename test/Incremental/{PrivateDependencies => Dependencies}/Inputs/InterestingType.swift (100%) rename test/Incremental/{PrivateDependencies => Dependencies}/private-function-fine.swift (100%) rename test/Incremental/{PrivateDependencies => Dependencies}/private-function-return-type-fine.swift (100%) rename test/Incremental/{PrivateDependencies => Dependencies}/private-protocol-conformer-ext-fine.swift (100%) rename test/Incremental/{PrivateDependencies => Dependencies}/private-protocol-conformer-fine.swift (100%) rename test/Incremental/{PrivateDependencies => Dependencies}/private-struct-member-fine.swift (100%) rename test/Incremental/{PrivateDependencies => Dependencies}/private-subscript-fine.swift (100%) rename test/Incremental/{PrivateDependencies => Dependencies}/private-typealias-fine.swift (100%) rename test/Incremental/{PrivateDependencies => Dependencies}/private-var-fine.swift (100%) rename test/Incremental/{PrivateDependencies => Dependencies}/reference-dependencies-consistency-fine.swift (100%) rename test/Incremental/{PrivateDependencies => Dependencies}/reference-dependencies-dynamic-lookup-fine.swift (100%) rename test/Incremental/{PrivateDependencies => Dependencies}/reference-dependencies-errors.swift (100%) rename test/Incremental/{PrivateDependencies => Dependencies}/reference-dependencies-fine.swift (100%) rename test/Incremental/{PrivateDependencies => Dependencies}/reference-dependencies-members-fine.swift (100%) diff --git a/test/Incremental/PrivateDependencies/Inputs/InterestingType.swift b/test/Incremental/Dependencies/Inputs/InterestingType.swift similarity index 100% rename from test/Incremental/PrivateDependencies/Inputs/InterestingType.swift rename to test/Incremental/Dependencies/Inputs/InterestingType.swift diff --git a/test/Incremental/PrivateDependencies/private-function-fine.swift b/test/Incremental/Dependencies/private-function-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/private-function-fine.swift rename to test/Incremental/Dependencies/private-function-fine.swift diff --git a/test/Incremental/PrivateDependencies/private-function-return-type-fine.swift b/test/Incremental/Dependencies/private-function-return-type-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/private-function-return-type-fine.swift rename to test/Incremental/Dependencies/private-function-return-type-fine.swift diff --git a/test/Incremental/PrivateDependencies/private-protocol-conformer-ext-fine.swift b/test/Incremental/Dependencies/private-protocol-conformer-ext-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/private-protocol-conformer-ext-fine.swift rename to test/Incremental/Dependencies/private-protocol-conformer-ext-fine.swift diff --git a/test/Incremental/PrivateDependencies/private-protocol-conformer-fine.swift b/test/Incremental/Dependencies/private-protocol-conformer-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/private-protocol-conformer-fine.swift rename to test/Incremental/Dependencies/private-protocol-conformer-fine.swift diff --git a/test/Incremental/PrivateDependencies/private-struct-member-fine.swift b/test/Incremental/Dependencies/private-struct-member-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/private-struct-member-fine.swift rename to test/Incremental/Dependencies/private-struct-member-fine.swift diff --git a/test/Incremental/PrivateDependencies/private-subscript-fine.swift b/test/Incremental/Dependencies/private-subscript-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/private-subscript-fine.swift rename to test/Incremental/Dependencies/private-subscript-fine.swift diff --git a/test/Incremental/PrivateDependencies/private-typealias-fine.swift b/test/Incremental/Dependencies/private-typealias-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/private-typealias-fine.swift rename to test/Incremental/Dependencies/private-typealias-fine.swift diff --git a/test/Incremental/PrivateDependencies/private-var-fine.swift b/test/Incremental/Dependencies/private-var-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/private-var-fine.swift rename to test/Incremental/Dependencies/private-var-fine.swift diff --git a/test/Incremental/PrivateDependencies/reference-dependencies-consistency-fine.swift b/test/Incremental/Dependencies/reference-dependencies-consistency-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/reference-dependencies-consistency-fine.swift rename to test/Incremental/Dependencies/reference-dependencies-consistency-fine.swift diff --git a/test/Incremental/PrivateDependencies/reference-dependencies-dynamic-lookup-fine.swift b/test/Incremental/Dependencies/reference-dependencies-dynamic-lookup-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/reference-dependencies-dynamic-lookup-fine.swift rename to test/Incremental/Dependencies/reference-dependencies-dynamic-lookup-fine.swift diff --git a/test/Incremental/PrivateDependencies/reference-dependencies-errors.swift b/test/Incremental/Dependencies/reference-dependencies-errors.swift similarity index 100% rename from test/Incremental/PrivateDependencies/reference-dependencies-errors.swift rename to test/Incremental/Dependencies/reference-dependencies-errors.swift diff --git a/test/Incremental/PrivateDependencies/reference-dependencies-fine.swift b/test/Incremental/Dependencies/reference-dependencies-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/reference-dependencies-fine.swift rename to test/Incremental/Dependencies/reference-dependencies-fine.swift diff --git a/test/Incremental/PrivateDependencies/reference-dependencies-members-fine.swift b/test/Incremental/Dependencies/reference-dependencies-members-fine.swift similarity index 100% rename from test/Incremental/PrivateDependencies/reference-dependencies-members-fine.swift rename to test/Incremental/Dependencies/reference-dependencies-members-fine.swift From d5febdb7916def418644543a06b0f9a74bb634be Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 25 Sep 2020 22:50:07 -0400 Subject: [PATCH 073/745] Sema: Fix diagnoseSelfAssignment() with computed properties This check did not handle MemberRefExprs with an InOutExpr base, giving it inconsistent behavior: - With a class, we would diagnose self-assignment of computed properties - With a struct, accesses to computed properties would build a MemberRefExpr with an InOutExpr base, so self-assignments were *not* diagnosed. Let's tweak the check to consistently diagnose self-assignments to stored properties only, instead of the emergent behavior as described above. --- lib/Sema/CSDiagnostics.cpp | 24 +++++---- test/Sema/diag_self_assign.swift | 88 +++++++++++++++++++++++++++++--- 2 files changed, 94 insertions(+), 18 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index ff16379d72d5a..ebd7a85d054e6 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1479,31 +1479,31 @@ bool RValueTreatedAsLValueFailure::diagnoseAsNote() { return true; } -static Decl *findSimpleReferencedDecl(const Expr *E) { +static VarDecl *findSimpleReferencedVarDecl(const Expr *E) { if (auto *LE = dyn_cast(E)) E = LE->getSubExpr(); if (auto *DRE = dyn_cast(E)) - return DRE->getDecl(); + return dyn_cast(DRE->getDecl()); return nullptr; } -static std::pair findReferencedDecl(const Expr *E) { +static std::pair findReferencedVarDecl(const Expr *E) { E = E->getValueProvidingExpr(); if (auto *LE = dyn_cast(E)) - return findReferencedDecl(LE->getSubExpr()); + return findReferencedVarDecl(LE->getSubExpr()); if (auto *AE = dyn_cast(E)) - return findReferencedDecl(AE->getDest()); + return findReferencedVarDecl(AE->getDest()); - if (auto *D = findSimpleReferencedDecl(E)) + if (auto *D = findSimpleReferencedVarDecl(E)) return std::make_pair(nullptr, D); if (auto *MRE = dyn_cast(E)) { - if (auto *BaseDecl = findSimpleReferencedDecl(MRE->getBase())) - return std::make_pair(BaseDecl, MRE->getMember().getDecl()); + if (auto *BaseDecl = findSimpleReferencedVarDecl(MRE->getBase())) + return std::make_pair(BaseDecl, cast(MRE->getMember().getDecl())); } return std::make_pair(nullptr, nullptr); @@ -1517,10 +1517,12 @@ bool TypeChecker::diagnoseSelfAssignment(const Expr *expr) { auto *dstExpr = assignExpr->getDest(); auto *srcExpr = assignExpr->getSrc(); - auto dstDecl = findReferencedDecl(dstExpr); - auto srcDecl = findReferencedDecl(srcExpr); + auto dstDecl = findReferencedVarDecl(dstExpr); + auto srcDecl = findReferencedVarDecl(srcExpr); - if (dstDecl.second && dstDecl == srcDecl) { + if (dstDecl.second && + dstDecl.second->hasStorage() && + dstDecl == srcDecl) { auto &DE = dstDecl.second->getASTContext().Diags; DE.diagnose(expr->getLoc(), dstDecl.first ? diag::self_assignment_prop : diag::self_assignment_var) diff --git a/test/Sema/diag_self_assign.swift b/test/Sema/diag_self_assign.swift index 46fe930fe195c..3ff09c23a17fe 100644 --- a/test/Sema/diag_self_assign.swift +++ b/test/Sema/diag_self_assign.swift @@ -21,6 +21,24 @@ class SA1 { } } +struct SA1a { + var foo: Int = 0 + init(fooi: Int) { + var foo = fooi + foo = foo // expected-error {{assigning a variable to itself}} + self.foo = self.foo // expected-error {{assigning a property to itself}} + foo = self.foo // no-error + self.foo = foo // no-error + } + mutating func f(fooi: Int) { + var foo = fooi + foo = foo // expected-error {{assigning a variable to itself}} + self.foo = self.foo // expected-error {{assigning a property to itself}} + foo = self.foo // no-error + self.foo = foo // no-error + } +} + class SA2 { var foo: Int { get { @@ -31,14 +49,37 @@ class SA2 { init(fooi: Int) { var foo = fooi foo = foo // expected-error {{assigning a variable to itself}} - self.foo = self.foo // expected-error {{assigning a property to itself}} + self.foo = self.foo // no-error foo = self.foo // no-error self.foo = foo // no-error } func f(fooi: Int) { var foo = fooi foo = foo // expected-error {{assigning a variable to itself}} - self.foo = self.foo // expected-error {{assigning a property to itself}} + self.foo = self.foo // no-error + foo = self.foo // no-error + self.foo = foo // no-error + } +} + +struct SA2a { + var foo: Int { + get { + return 0 + } + set {} + } + init(fooi: Int) { + var foo = fooi + foo = foo // expected-error {{assigning a variable to itself}} + self.foo = self.foo // no-error + foo = self.foo // no-error + self.foo = foo // no-error + } + mutating func f(fooi: Int) { + var foo = fooi + foo = foo // expected-error {{assigning a variable to itself}} + self.foo = self.foo // no-error foo = self.foo // no-error self.foo = foo // no-error } @@ -50,10 +91,24 @@ class SA3 { return foo // expected-warning {{attempting to access 'foo' within its own getter}} expected-note{{access 'self' explicitly to silence this warning}} {{14-14=self.}} } set { - foo = foo // expected-error {{assigning a property to itself}} expected-warning {{attempting to modify 'foo' within its own setter}} expected-note{{access 'self' explicitly to silence this warning}} {{7-7=self.}} expected-warning{{setter argument 'newValue' was never used, but the property was accessed}} expected-note{{did you mean to use 'newValue' instead of accessing the property's current value?}} - self.foo = self.foo // expected-error {{assigning a property to itself}} - foo = self.foo // expected-error {{assigning a property to itself}} expected-warning {{attempting to modify 'foo' within its own setter}} expected-note{{access 'self' explicitly to silence this warning}} {{7-7=self.}} - self.foo = foo // expected-error {{assigning a property to itself}} + foo = foo // expected-warning {{attempting to modify 'foo' within its own setter}} expected-note{{access 'self' explicitly to silence this warning}} {{7-7=self.}} expected-warning{{setter argument 'newValue' was never used, but the property was accessed}} expected-note{{did you mean to use 'newValue' instead of accessing the property's current value?}} + self.foo = self.foo // no-error + foo = self.foo // expected-warning {{attempting to modify 'foo' within its own setter}} expected-note{{access 'self' explicitly to silence this warning}} {{7-7=self.}} + self.foo = foo + } + } +} + +struct SA3a { + var foo: Int { + get { + return foo // expected-warning {{attempting to access 'foo' within its own getter}} expected-note{{access 'self' explicitly to silence this warning}} {{14-14=self.}} + } + set { + foo = foo // expected-warning {{attempting to modify 'foo' within its own setter}} expected-note{{access 'self' explicitly to silence this warning}} {{7-7=self.}} expected-warning{{setter argument 'newValue' was never used, but the property was accessed}} expected-note{{did you mean to use 'newValue' instead of accessing the property's current value?}} + self.foo = self.foo // no-error + foo = self.foo // expected-warning {{attempting to modify 'foo' within its own setter}} expected-note{{access 'self' explicitly to silence this warning}} {{7-7=self.}} + self.foo = foo } } } @@ -69,10 +124,29 @@ class SA4 { } } +struct SA4a { + var foo: Int { + get { + return foo // expected-warning {{attempting to access 'foo' within its own getter}} expected-note{{access 'self' explicitly to silence this warning}} {{14-14=self.}} + } + set(value) { + value = value // expected-error {{cannot assign to value: 'value' is a 'let' constant}} + } + } +} + class SA5 { var foo: Int = 0 } -func SA5_test(a: SA4, b: SA4) { +func SA5_test(a: SA5, b: SA5) { + a.foo = a.foo // expected-error {{assigning a property to itself}} + a.foo = b.foo +} + +struct SA5a { + var foo: Int = 0 +} +func SA5a_test(a: inout SA5, b: inout SA5) { a.foo = a.foo // expected-error {{assigning a property to itself}} a.foo = b.foo } From 1a860cc99ef8548aff51957f195d3c2ef89dbc62 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 25 Sep 2020 21:48:58 -0400 Subject: [PATCH 074/745] Sema: Simplify adjustSelfTypeForMember() a little bit to avoid a cycle We used to wrap the base expression in an InOutExpr when accessing a computed property. This was a vestigial remnant of differences in the SILGen code paths for stored vs computed property access. These days SILGen doesn't care and is perfectly happy to call getters and setters with an LValueType base as well. This allows us to remove the call to getAccessSemantics(), which for a 'didSet', had to kick off type checking of the body. Fixes . --- lib/AST/ASTVerifier.cpp | 34 ++++++++++++++++---------------- lib/Sema/CSApply.cpp | 34 +++++++++++--------------------- test/decl/var/didset_cycle.swift | 19 ++++++++++++++++++ 3 files changed, 48 insertions(+), 39 deletions(-) create mode 100644 test/decl/var/didset_cycle.swift diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index 78ce70008c8ea..6f502b96319c1 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -1828,30 +1828,30 @@ class Verifier : public ASTWalker { Out << "\n"; abort(); } - + + if (!isa(E->getMember().getDecl())) { + Out << "Member reference to a non-VarDecl\n"; + E->dump(Out); + Out << "\n"; + abort(); + } + + auto baseType = E->getBase()->getType(); + if (baseType->is()) { + Out << "Member reference to an inout type\n"; + E->dump(Out); + Out << "\n"; + abort(); + } + // The base of a member reference cannot be an existential type. - if (E->getBase()->getType()->getWithoutSpecifierType() - ->isExistentialType()) { + if (baseType->getWithoutSpecifierType()->isExistentialType()) { Out << "Member reference into an unopened existential type\n"; E->dump(Out); Out << "\n"; abort(); } - // The only time the base is allowed to be inout is if we are accessing - // a computed property or if the base is a protocol or existential. - if (auto *baseIOT = E->getBase()->getType()->getAs()) { - if (!baseIOT->getObjectType()->is()) { - auto *VD = dyn_cast(E->getMember().getDecl()); - if (!VD || !VD->requiresOpaqueAccessors()) { - Out << "member_ref_expr on value of inout type\n"; - E->dump(Out); - Out << "\n"; - abort(); - } - } - } - // FIXME: Check container/member types through substitutions. verifyCheckedBase(E); diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index a6a75d32b73c8..7d5cb92c3aa0f 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -6871,7 +6871,13 @@ static Type adjustSelfTypeForMember(Expr *baseExpr, Type baseTy, ValueDecl *member, AccessSemantics semantics, DeclContext *UseDC) { - auto baseObjectTy = baseTy->getWithoutSpecifierType(); + assert(!baseTy->is()); + + auto inOutTy = baseTy->getAs(); + if (!inOutTy) + return baseTy; + + auto baseObjectTy = inOutTy->getObjectType(); if (isa(member)) return baseObjectTy; @@ -6880,7 +6886,7 @@ static Type adjustSelfTypeForMember(Expr *baseExpr, // If 'self' is an inout type, turn the base type into an lvalue // type with the same qualifiers. if (func->isMutating()) - return InOutType::get(baseObjectTy); + return baseTy; // Otherwise, return the rvalue type. return baseObjectTy; @@ -6903,26 +6909,10 @@ static Type adjustSelfTypeForMember(Expr *baseExpr, !isNonMutatingSetterPWAssignInsideInit(baseExpr, member, UseDC)) return baseObjectTy; - // If we're calling an accessor, keep the base as an inout type, because the - // getter may be mutating. - auto strategy = SD->getAccessStrategy(semantics, - isSettableFromHere - ? AccessKind::ReadWrite - : AccessKind::Read, - UseDC->getParentModule(), - UseDC->getResilienceExpansion()); - if (baseTy->is() && strategy.getKind() != AccessStrategy::Storage) - return InOutType::get(baseObjectTy); - - // Accesses to non-function members in value types are done through an @lvalue - // type. - if (baseTy->is()) - return LValueType::get(baseObjectTy); - - // Accesses to members in values of reference type (classes, metatypes) are - // always done through a the reference to self. Accesses to value types with - // a non-mutable self are also done through the base type. - return baseTy; + if (isa(member)) + return baseTy; + + return LValueType::get(baseObjectTy); } Expr * diff --git a/test/decl/var/didset_cycle.swift b/test/decl/var/didset_cycle.swift new file mode 100644 index 0000000000000..42564bd804a5d --- /dev/null +++ b/test/decl/var/didset_cycle.swift @@ -0,0 +1,19 @@ +// RUN: %target-swift-frontend -typecheck %s + +func doSomething(_: Int) {} + +struct S { + var x: Int { + didSet { + doSomething(y) + doSomething(self.y) + } + } + + var y: Int { + didSet { + doSomething(x) + doSomething(self.x) + } + } +} From 71a281c68edae48d709ceda685cb9db7a521a589 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 25 Sep 2020 22:06:01 -0400 Subject: [PATCH 075/745] Sema: Rename coerceObjectArgumentToType() to coerceSelfArgumentToType() Also remove the unused AccessSemantics parameter. --- lib/Sema/CSApply.cpp | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 7d5cb92c3aa0f..c1603274fea49 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -1173,8 +1173,8 @@ namespace { if (cs.getType(base)->is()) selfParamTy = InOutType::get(selfTy); - base = coerceObjectArgumentToType( - base, selfParamTy, member, semantics, + base = coerceSelfArgumentToType( + base, selfParamTy, member, locator.withPathElement(ConstraintLocator::MemberRefBase)); } else { if (!isExistentialMetatype || openedExistential) { @@ -1587,7 +1587,7 @@ namespace { ArrayRef argLabels, ConstraintLocatorBuilder locator); - /// Coerce the given object argument (e.g., for the base of a + /// Coerce the given 'self' argument (e.g., for the base of a /// member expression) to the given type. /// /// \param expr The expression to coerce. @@ -1596,13 +1596,10 @@ namespace { /// /// \param member The member being accessed. /// - /// \param semantics The kind of access we've been asked to perform. - /// /// \param locator Locator used to describe where in this expression we are. - Expr *coerceObjectArgumentToType(Expr *expr, - Type baseTy, ValueDecl *member, - AccessSemantics semantics, - ConstraintLocatorBuilder locator); + Expr *coerceSelfArgumentToType(Expr *expr, + Type baseTy, ValueDecl *member, + ConstraintLocatorBuilder locator); private: /// Build a new subscript. @@ -1806,8 +1803,7 @@ namespace { // Handle dynamic lookup. if (choice.getKind() == OverloadChoiceKind::DeclViaDynamic || subscript->getAttrs().hasAttribute()) { - base = coerceObjectArgumentToType(base, baseTy, subscript, - AccessSemantics::Ordinary, locator); + base = coerceSelfArgumentToType(base, baseTy, subscript, locator); if (!base) return nullptr; @@ -1831,8 +1827,8 @@ namespace { auto containerTy = solution.simplifyType(openedBaseType); if (baseIsInstance) { - base = coerceObjectArgumentToType( - base, containerTy, subscript, AccessSemantics::Ordinary, + base = coerceSelfArgumentToType( + base, containerTy, subscript, locator.withPathElement(ConstraintLocator::MemberRefBase)); } else { base = coerceToType(base, @@ -6869,7 +6865,6 @@ static bool isNonMutatingSetterPWAssignInsideInit(Expr *baseExpr, /// the given member. static Type adjustSelfTypeForMember(Expr *baseExpr, Type baseTy, ValueDecl *member, - AccessSemantics semantics, DeclContext *UseDC) { assert(!baseTy->is()); @@ -6916,11 +6911,10 @@ static Type adjustSelfTypeForMember(Expr *baseExpr, } Expr * -ExprRewriter::coerceObjectArgumentToType(Expr *expr, - Type baseTy, ValueDecl *member, - AccessSemantics semantics, - ConstraintLocatorBuilder locator) { - Type toType = adjustSelfTypeForMember(expr, baseTy, member, semantics, dc); +ExprRewriter::coerceSelfArgumentToType(Expr *expr, + Type baseTy, ValueDecl *member, + ConstraintLocatorBuilder locator) { + Type toType = adjustSelfTypeForMember(expr, baseTy, member, dc); // If our expression already has the right type, we're done. Type fromType = cs.getType(expr); From 81f0f37accd31612d25825275b15a426073d27e0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 25 Sep 2020 22:45:08 -0700 Subject: [PATCH 076/745] [Concurrency] (De-)mangling for SIL @async function types. --- docs/ABI/Mangling.rst | 4 +++- include/swift/Demangling/TypeDecoder.h | 2 ++ include/swift/Reflection/TypeRefBuilder.h | 2 ++ lib/AST/ASTMangler.cpp | 5 ++++ lib/Demangling/Demangler.cpp | 5 ++++ lib/Demangling/OldDemangler.cpp | 3 +++ lib/Demangling/OldRemangler.cpp | 2 ++ lib/Demangling/Remangler.cpp | 1 + test/TypeDecoder/concurrency.swift | 28 +++++++++++++++++++++++ 9 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 test/TypeDecoder/concurrency.swift diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index 9e60d04a4f5e3..bc0b78d5c1edf 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -600,7 +600,7 @@ mangled in to disambiguate. impl-function-type ::= type* 'I' FUNC-ATTRIBUTES '_' impl-function-type ::= type* generic-signature 'I' FUNC-ATTRIBUTES '_' - FUNC-ATTRIBUTES ::= PATTERN-SUBS? INVOCATION-SUBS? PSEUDO-GENERIC? CALLEE-ESCAPE? DIFFERENTIABILITY-KIND? CALLEE-CONVENTION FUNC-REPRESENTATION? COROUTINE-KIND? (PARAM-CONVENTION PARAM-DIFFERENTIABILITY?)* RESULT-CONVENTION* ('Y' PARAM-CONVENTION)* ('z' RESULT-CONVENTION RESULT-DIFFERENTIABILITY?)? + FUNC-ATTRIBUTES ::= PATTERN-SUBS? INVOCATION-SUBS? PSEUDO-GENERIC? CALLEE-ESCAPE? DIFFERENTIABILITY-KIND? CALLEE-CONVENTION FUNC-REPRESENTATION? COROUTINE-KIND? ASYNC? (PARAM-CONVENTION PARAM-DIFFERENTIABILITY?)* RESULT-CONVENTION* ('Y' PARAM-CONVENTION)* ('z' RESULT-CONVENTION RESULT-DIFFERENTIABILITY?)? PATTERN-SUBS ::= 's' // has pattern substitutions INVOCATION-SUB ::= 'I' // has invocation substitutions @@ -627,6 +627,8 @@ mangled in to disambiguate. COROUTINE-KIND ::= 'A' // yield-once coroutine COROUTINE-KIND ::= 'G' // yield-many coroutine + ASYNC ::= 'H' // @async + PARAM-CONVENTION ::= 'i' // indirect in PARAM-CONVENTION ::= 'c' // indirect in constant PARAM-CONVENTION ::= 'l' // indirect inout diff --git a/include/swift/Demangling/TypeDecoder.h b/include/swift/Demangling/TypeDecoder.h index f7a5a2247c049..fb33edf878252 100644 --- a/include/swift/Demangling/TypeDecoder.h +++ b/include/swift/Demangling/TypeDecoder.h @@ -706,6 +706,8 @@ class TypeDecoder { } else if (text == "@convention(block)") { flags = flags.withRepresentation(ImplFunctionRepresentation::Block); + } else if (text == "@async") { + flags = flags.withAsync(); } } else if (child->getKind() == NodeKind::ImplDifferentiable) { flags = flags.withDifferentiabilityKind( diff --git a/include/swift/Reflection/TypeRefBuilder.h b/include/swift/Reflection/TypeRefBuilder.h index 982bcba2edb4c..2e411fee779ba 100644 --- a/include/swift/Reflection/TypeRefBuilder.h +++ b/include/swift/Reflection/TypeRefBuilder.h @@ -443,6 +443,8 @@ class TypeRefBuilder { break; } + funcFlags = funcFlags.withAsync(flags.isAsync()); + auto result = createTupleType({}, ""); return FunctionTypeRef::create(*this, {}, result, funcFlags); } diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 8da9843183986..48fb9337f6459 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -1689,6 +1689,11 @@ void ASTMangler::appendImplFunctionType(SILFunctionType *fn) { break; } + // Asynchronous functions. + if (fn->isAsync()) { + OpArgs.push_back('H'); + } + auto outerGenericSig = CurGenericSignature; CurGenericSignature = fn->getSubstGenericSignature(); diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index f49ba90261714..d6a06b003f6da 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -1829,6 +1829,11 @@ NodePointer Demangler::demangleImplFunctionType() { if (CoroAttr) type->addChild(createNode(Node::Kind::ImplFunctionAttribute, CoroAttr), *this); + if (nextIf('H')) { + type->addChild(createNode(Node::Kind::ImplFunctionAttribute, "@async"), + *this); + } + addChild(type, GenSig); int NumTypesToAdd = 0; diff --git a/lib/Demangling/OldDemangler.cpp b/lib/Demangling/OldDemangler.cpp index e66d21aaa85f9..575750012f1c7 100644 --- a/lib/Demangling/OldDemangler.cpp +++ b/lib/Demangling/OldDemangler.cpp @@ -2148,6 +2148,9 @@ class OldDemangler { return nullptr; } + if (Mangled.nextIf('H')) + addImplFunctionAttribute(type, "@async"); + // Enter a new generic context if this type is generic. // FIXME: replace with std::optional, when we have it. bool isPseudogeneric = false; diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index 7df26cbfad047..d402cc7bcbb0f 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -1255,6 +1255,8 @@ void Remangler::mangleImplFunctionAttribute(Node *node) { Buffer << "A"; } else if (text == "@yield_many") { Buffer << "G"; + } else if (text == "@async") { + Buffer << "H"; } else { unreachable("bad impl-function-attribute"); } diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index a61064fdfb1cb..0b0fdec171717 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -1537,6 +1537,7 @@ void Remangler::mangleImplFunctionType(Node *node) { .Case("@convention(witness_method)", 'W') .Case("@yield_once", 'A') .Case("@yield_many", 'G') + .Case("@async", 'H') .Default(0); assert(FuncAttr && "invalid impl function attribute"); Buffer << FuncAttr; diff --git a/test/TypeDecoder/concurrency.swift b/test/TypeDecoder/concurrency.swift new file mode 100644 index 0000000000000..db05054cfac2e --- /dev/null +++ b/test/TypeDecoder/concurrency.swift @@ -0,0 +1,28 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-build-swift -emit-executable %s -g -o %t/concurrency -emit-module -Xfrontend -enable-experimental-concurrency + +// RUN: sed -ne '/\/\/ *DEMANGLE-TYPE: /s/\/\/ *DEMANGLE-TYPE: *//p' < %s > %t/input +// RUN: %lldb-moduleimport-test-with-sdk %t/concurrency -type-from-mangled=%t/input | %FileCheck %s --check-prefix=CHECK-TYPE + +func blackHole(_: Any...) {} + +public var lookAtMeeee: [(Int) async -> Void] = [] + +func foo() { + do { + let x1 = [(Int) async -> Void]() + let x2 = [(Int) async throws -> Void]() + + blackHole(x1, x2) + } +} + +// DEMANGLE-TYPE: $sSayySiYcG +// CHECK-TYPE: Array<(Int) async -> ()> + +// DEMANGLE-TYPE: $sSayySiYKcG +// CHECK-TYPE: Array<(Int) async throws -> ()> + +// DEMANGLE-TYPE: $sIegH_D +// CHECK-TYPE: @async @callee_guaranteed () -> () From e022a952ca63e4702a79af4e3bedef49c4637ea9 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Sat, 26 Sep 2020 17:39:15 -0500 Subject: [PATCH 077/745] [build-toolchain] Add distcc like integration for using sccache. Disabled by default. --- utils/build-toolchain | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/utils/build-toolchain b/utils/build-toolchain index 7768339ec1537..99e86f434a66c 100755 --- a/utils/build-toolchain +++ b/utils/build-toolchain @@ -45,6 +45,7 @@ cd "$(dirname $0)/.." || exit # Set defaults DISTCC_FLAG= +SCCACHE_FLAG= DRY_RUN= BUNDLE_PREFIX= PRESET_FILE_FLAGS= @@ -82,6 +83,15 @@ while [ $# -ne 0 ]; do ;; --distcc) DISTCC_FLAG="--distcc" + ;; + --sccache) + SCCACHE=$(which sccache) + if [[ -z "${SCCACHE}" ]]; then + echo "Error! Asked to use sccache, but could not find sccache in PATH?!" + usage + exit 1 + fi + SCCACHE_FLAG="--cmake-c-launcher=${SCCACHE} --cmake-cxx-launcher=${SCCACHE}" ;; --preset-file) shift @@ -140,8 +150,10 @@ SYMBOLS_PACKAGE="${RESULT_DIR}/${SYM_ARCHIVE}" DRY_RUN="${DRY_RUN}" DISTCC_FLAG="${DISTCC_FLAG}" PRESET_FILE_FLAGS="${PRESET_FILE_FLAGS}" +SCCACHE_FLAG="${SCCACHE_FLAG}" ./utils/build-script ${DRY_RUN} ${DISTCC_FLAG} ${PRESET_FILE_FLAGS} \ + ${SCCACHE_FLAG} \ --preset="${PRESET_PREFIX}${SWIFT_PACKAGE}${PRESET_SUFFIX}" \ install_destdir="${SWIFT_INSTALL_DIR}" \ installable_package="${SWIFT_INSTALLABLE_PACKAGE}" \ From 3a47087cc197aab8970a55ee265d395bc4f6f002 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 25 Aug 2020 09:54:46 -0700 Subject: [PATCH 078/745] [Parser] Don't resolve decl references in the parser if the declaration has a custom attribute. --- lib/Parse/ParseExpr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index b61e21834871e..a985e7194d8fb 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -2255,7 +2255,7 @@ Expr *Parser::parseExprIdentifier() { } Expr *E; - if (D == nullptr) { + if (D == nullptr || D->getAttrs().hasAttribute()) { if (name.getBaseName().isEditorPlaceholder()) { IDSyntaxContext.setCreateSyntax(SyntaxKind::EditorPlaceholderExpr); return parseExprEditorPlaceholder(IdentTok, name.getBaseIdentifier()); From b03457ddf38f38f21ca4e00035752c2de3b5b6c0 Mon Sep 17 00:00:00 2001 From: Zoe Carver Date: Sat, 26 Sep 2020 20:19:06 -0700 Subject: [PATCH 079/745] [cxx-interop] Rename tests in Cxx/templates to show they are class templates. (#34086) Once we have tests for both class templates and function templates, it will be important that they are differentiated. --- ...s-template-eager-instantiation-problems.h} | 0 ... class-template-with-primitive-argument.h} | 0 ...tion.h => explicit-class-specialization.h} | 0 ...s.h => fully-pre-defined-class-template.h} | 2 +- .../Cxx/templates/Inputs/module.modulemap | 24 +++++++++---------- ...ion.h => not-pre-defined-class-template.h} | 0 ...=> partially-pre-defined-class-template.h} | 2 +- ...emplate-eager-instatiation-problems.swift} | 2 +- ...ss-template-with-primitive-argument.swift} | 2 +- ...ft => explicit-class-specialization.swift} | 2 +- ...=> fully-pre-defined-class-template.swift} | 4 ++-- ...ned-class-template-module-interface.swift} | 2 +- ...t => not-pre-defined-class-template.swift} | 2 +- ...ly-pre-defined-class-template-irgen.swift} | 4 ++-- ...y-pre-defined-class-template-silgen.swift} | 4 ++-- ...artially-pre-defined-class-template.swift} | 4 ++-- 16 files changed, 27 insertions(+), 27 deletions(-) rename test/Interop/Cxx/templates/Inputs/{eager-instantiation-problems.h => class-template-eager-instantiation-problems.h} (100%) rename test/Interop/Cxx/templates/Inputs/{decl-with-primitive-argument.h => class-template-with-primitive-argument.h} (100%) rename test/Interop/Cxx/templates/Inputs/{explicit-specialization.h => explicit-class-specialization.h} (100%) rename test/Interop/Cxx/templates/Inputs/{decl-with-definition-including-members.h => fully-pre-defined-class-template.h} (92%) rename test/Interop/Cxx/templates/Inputs/{decl-without-definition.h => not-pre-defined-class-template.h} (100%) rename test/Interop/Cxx/templates/Inputs/{decl-with-definition.h => partially-pre-defined-class-template.h} (90%) rename test/Interop/Cxx/templates/{eager-instatiation-problems.swift => class-template-eager-instatiation-problems.swift} (95%) rename test/Interop/Cxx/templates/{decl-with-primitive-argument.swift => class-template-with-primitive-argument.swift} (89%) rename test/Interop/Cxx/templates/{explicit-specialization.swift => explicit-class-specialization.swift} (95%) rename test/Interop/Cxx/templates/{decl-with-definition-including-members.swift => fully-pre-defined-class-template.swift} (76%) rename test/Interop/Cxx/templates/{decl-without-definition-module-interface.swift => not-pre-defined-class-template-module-interface.swift} (75%) rename test/Interop/Cxx/templates/{decl-without-definition.swift => not-pre-defined-class-template.swift} (92%) rename test/Interop/Cxx/templates/{decl-with-definition-irgen.swift => partially-pre-defined-class-template-irgen.swift} (95%) rename test/Interop/Cxx/templates/{decl-with-definition-silgen.swift => partially-pre-defined-class-template-silgen.swift} (92%) rename test/Interop/Cxx/templates/{decl-with-definition.swift => partially-pre-defined-class-template.swift} (76%) diff --git a/test/Interop/Cxx/templates/Inputs/eager-instantiation-problems.h b/test/Interop/Cxx/templates/Inputs/class-template-eager-instantiation-problems.h similarity index 100% rename from test/Interop/Cxx/templates/Inputs/eager-instantiation-problems.h rename to test/Interop/Cxx/templates/Inputs/class-template-eager-instantiation-problems.h diff --git a/test/Interop/Cxx/templates/Inputs/decl-with-primitive-argument.h b/test/Interop/Cxx/templates/Inputs/class-template-with-primitive-argument.h similarity index 100% rename from test/Interop/Cxx/templates/Inputs/decl-with-primitive-argument.h rename to test/Interop/Cxx/templates/Inputs/class-template-with-primitive-argument.h diff --git a/test/Interop/Cxx/templates/Inputs/explicit-specialization.h b/test/Interop/Cxx/templates/Inputs/explicit-class-specialization.h similarity index 100% rename from test/Interop/Cxx/templates/Inputs/explicit-specialization.h rename to test/Interop/Cxx/templates/Inputs/explicit-class-specialization.h diff --git a/test/Interop/Cxx/templates/Inputs/decl-with-definition-including-members.h b/test/Interop/Cxx/templates/Inputs/fully-pre-defined-class-template.h similarity index 92% rename from test/Interop/Cxx/templates/Inputs/decl-with-definition-including-members.h rename to test/Interop/Cxx/templates/Inputs/fully-pre-defined-class-template.h index 4c772c9dfaca0..e9bdca3698eee 100644 --- a/test/Interop/Cxx/templates/Inputs/decl-with-definition-including-members.h +++ b/test/Interop/Cxx/templates/Inputs/fully-pre-defined-class-template.h @@ -21,6 +21,6 @@ inline int forceInstantiation() { // because function above forced the instantiation. Its members are fully // instantiated, so nothing needs to be explicitly instantiated by the Swift // compiler. -typedef MagicWrapper FullyDefinedMagicallyWrappedInt; +typedef MagicWrapper FullyPreDefinedMagicallyWrappedInt; #endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_DECL_WITH_DEFINITION_INCLUDING_MEMBERS_H diff --git a/test/Interop/Cxx/templates/Inputs/module.modulemap b/test/Interop/Cxx/templates/Inputs/module.modulemap index af6d28e675a40..ca82db18fc25f 100644 --- a/test/Interop/Cxx/templates/Inputs/module.modulemap +++ b/test/Interop/Cxx/templates/Inputs/module.modulemap @@ -1,33 +1,33 @@ -module DeclWithPrimitiveArgument { - header "decl-with-primitive-argument.h" +module ClassTemplateWithPrimitiveArgument { + header "class-template-with-primitive-argument.h" } -module DeclWithoutDefinition { - header "decl-without-definition.h" +module NotPreDefinedClassTemplate { + header "not-pre-defined-class-template.h" } -module DeclWithDefinition { - header "decl-with-definition.h" +module PartiallyPreDefinedClassTemplate { + header "partially-pre-defined-class-template.h" } -module DeclWithDefinitionIncludingMembers { - header "decl-with-definition-including-members.h" +module FullyPreDefinedClassTemplate { + header "fully-pre-defined-class-template.h" } module CanonicalTypes { header "canonical-types.h" } -module ExplicitSpecialization { - header "explicit-specialization.h" +module ExplicitClassSpecialization { + header "explicit-class-specialization.h" } module UsingDirective { header "using-directive.h" } -module EagerInstantiationProblems { - header "eager-instantiation-problems.h" +module ClassTemplateEagerInstantiationProblems { + header "class-template-eager-instantiation-problems.h" } module Mangling { diff --git a/test/Interop/Cxx/templates/Inputs/decl-without-definition.h b/test/Interop/Cxx/templates/Inputs/not-pre-defined-class-template.h similarity index 100% rename from test/Interop/Cxx/templates/Inputs/decl-without-definition.h rename to test/Interop/Cxx/templates/Inputs/not-pre-defined-class-template.h diff --git a/test/Interop/Cxx/templates/Inputs/decl-with-definition.h b/test/Interop/Cxx/templates/Inputs/partially-pre-defined-class-template.h similarity index 90% rename from test/Interop/Cxx/templates/Inputs/decl-with-definition.h rename to test/Interop/Cxx/templates/Inputs/partially-pre-defined-class-template.h index d8fb7b56f676b..f84e15e96c905 100644 --- a/test/Interop/Cxx/templates/Inputs/decl-with-definition.h +++ b/test/Interop/Cxx/templates/Inputs/partially-pre-defined-class-template.h @@ -19,6 +19,6 @@ inline MagicWrapper forceInstantiation() { // The ClassTemplateSpecializationDecl node for MagicWrapper already has a definition // because function above forced the instantiation. Its members are not // instantiated though, the Swift compiler needs to instantiate them. -typedef MagicWrapper PartiallyDefinedMagicallyWrappedInt; +typedef MagicWrapper PartiallyPreDefinedMagicallyWrappedInt; #endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_DECL_WITH_DEFINITION_H diff --git a/test/Interop/Cxx/templates/eager-instatiation-problems.swift b/test/Interop/Cxx/templates/class-template-eager-instatiation-problems.swift similarity index 95% rename from test/Interop/Cxx/templates/eager-instatiation-problems.swift rename to test/Interop/Cxx/templates/class-template-eager-instatiation-problems.swift index f341c8c68c87c..979834e5dcb29 100644 --- a/test/Interop/Cxx/templates/eager-instatiation-problems.swift +++ b/test/Interop/Cxx/templates/class-template-eager-instatiation-problems.swift @@ -2,7 +2,7 @@ // // REQUIRES: executable_test -import EagerInstantiationProblems +import ClassTemplateEagerInstantiationProblems import StdlibUnittest var TemplatesTestSuite = TestSuite("TemplatesTestSuite") diff --git a/test/Interop/Cxx/templates/decl-with-primitive-argument.swift b/test/Interop/Cxx/templates/class-template-with-primitive-argument.swift similarity index 89% rename from test/Interop/Cxx/templates/decl-with-primitive-argument.swift rename to test/Interop/Cxx/templates/class-template-with-primitive-argument.swift index 951c49c0fb814..c6d7f7810756e 100644 --- a/test/Interop/Cxx/templates/decl-with-primitive-argument.swift +++ b/test/Interop/Cxx/templates/class-template-with-primitive-argument.swift @@ -2,7 +2,7 @@ // // REQUIRES: executable_test -import DeclWithPrimitiveArgument +import ClassTemplateWithPrimitiveArgument import StdlibUnittest var TemplatesTestSuite = TestSuite("TemplatesTestSuite") diff --git a/test/Interop/Cxx/templates/explicit-specialization.swift b/test/Interop/Cxx/templates/explicit-class-specialization.swift similarity index 95% rename from test/Interop/Cxx/templates/explicit-specialization.swift rename to test/Interop/Cxx/templates/explicit-class-specialization.swift index b8e02faefe85b..2d97862b42312 100644 --- a/test/Interop/Cxx/templates/explicit-specialization.swift +++ b/test/Interop/Cxx/templates/explicit-class-specialization.swift @@ -2,7 +2,7 @@ // // REQUIRES: executable_test -import ExplicitSpecialization +import ExplicitClassSpecialization import StdlibUnittest var TemplatesTestSuite = TestSuite("TemplatesTestSuite") diff --git a/test/Interop/Cxx/templates/decl-with-definition-including-members.swift b/test/Interop/Cxx/templates/fully-pre-defined-class-template.swift similarity index 76% rename from test/Interop/Cxx/templates/decl-with-definition-including-members.swift rename to test/Interop/Cxx/templates/fully-pre-defined-class-template.swift index 8c7dcf7013557..bf245c7da838a 100644 --- a/test/Interop/Cxx/templates/decl-with-definition-including-members.swift +++ b/test/Interop/Cxx/templates/fully-pre-defined-class-template.swift @@ -2,14 +2,14 @@ // // REQUIRES: executable_test -import DeclWithDefinitionIncludingMembers +import FullyPreDefinedClassTemplate import StdlibUnittest var TemplatesTestSuite = TestSuite("TemplatesTestSuite") TemplatesTestSuite.test("fully-defined") { let myInt = IntWrapper(value: 10) - var magicInt = FullyDefinedMagicallyWrappedInt(t: myInt) + var magicInt = FullyPreDefinedMagicallyWrappedInt(t: myInt) expectEqual(magicInt.getValuePlusArg(5), 15) } diff --git a/test/Interop/Cxx/templates/decl-without-definition-module-interface.swift b/test/Interop/Cxx/templates/not-pre-defined-class-template-module-interface.swift similarity index 75% rename from test/Interop/Cxx/templates/decl-without-definition-module-interface.swift rename to test/Interop/Cxx/templates/not-pre-defined-class-template-module-interface.swift index ca12841f96cb9..7fd6b7cc0efc1 100644 --- a/test/Interop/Cxx/templates/decl-without-definition-module-interface.swift +++ b/test/Interop/Cxx/templates/not-pre-defined-class-template-module-interface.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-ide-test -print-module -module-to-print=DeclWithoutDefinition -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s +// RUN: %target-swift-ide-test -print-module -module-to-print=NotPreDefinedClassTemplate -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s // CHECK: struct __CxxTemplateInst12MagicWrapperI10IntWrapperE { // CHECK: var t: IntWrapper diff --git a/test/Interop/Cxx/templates/decl-without-definition.swift b/test/Interop/Cxx/templates/not-pre-defined-class-template.swift similarity index 92% rename from test/Interop/Cxx/templates/decl-without-definition.swift rename to test/Interop/Cxx/templates/not-pre-defined-class-template.swift index 23eca83c31d85..e57936670ff4d 100644 --- a/test/Interop/Cxx/templates/decl-without-definition.swift +++ b/test/Interop/Cxx/templates/not-pre-defined-class-template.swift @@ -2,7 +2,7 @@ // // REQUIRES: executable_test -import DeclWithoutDefinition +import NotPreDefinedClassTemplate import StdlibUnittest var TemplatesTestSuite = TestSuite("TemplatesTestSuite") diff --git a/test/Interop/Cxx/templates/decl-with-definition-irgen.swift b/test/Interop/Cxx/templates/partially-pre-defined-class-template-irgen.swift similarity index 95% rename from test/Interop/Cxx/templates/decl-with-definition-irgen.swift rename to test/Interop/Cxx/templates/partially-pre-defined-class-template-irgen.swift index d69ece5914b55..1dc4ac92f6fee 100644 --- a/test/Interop/Cxx/templates/decl-with-definition-irgen.swift +++ b/test/Interop/Cxx/templates/partially-pre-defined-class-template-irgen.swift @@ -1,10 +1,10 @@ // RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s // REQUIRES: rdar67257133 -import DeclWithDefinition +import PartiallyPreDefinedClassTemplate public func getWrappedMagicInt() -> CInt { let myInt = IntWrapper(value: 7) - var magicInt = PartiallyDefinedMagicallyWrappedInt(t: myInt) + var magicInt = PartiallyPreDefinedMagicallyWrappedInt(t: myInt) return magicInt.getValuePlusArg(13) } diff --git a/test/Interop/Cxx/templates/decl-with-definition-silgen.swift b/test/Interop/Cxx/templates/partially-pre-defined-class-template-silgen.swift similarity index 92% rename from test/Interop/Cxx/templates/decl-with-definition-silgen.swift rename to test/Interop/Cxx/templates/partially-pre-defined-class-template-silgen.swift index a9481f6ac9d6c..a65be03154590 100644 --- a/test/Interop/Cxx/templates/decl-with-definition-silgen.swift +++ b/test/Interop/Cxx/templates/partially-pre-defined-class-template-silgen.swift @@ -1,10 +1,10 @@ // RUN: %target-swift-emit-sil %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s -import DeclWithDefinition +import PartiallyPreDefinedClassTemplate public func getWrappedMagicInt() -> CInt { let myInt = IntWrapper(value: 21) - var magicInt = PartiallyDefinedMagicallyWrappedInt(t: myInt) + var magicInt = PartiallyPreDefinedMagicallyWrappedInt(t: myInt) return magicInt.getValuePlusArg(32) } diff --git a/test/Interop/Cxx/templates/decl-with-definition.swift b/test/Interop/Cxx/templates/partially-pre-defined-class-template.swift similarity index 76% rename from test/Interop/Cxx/templates/decl-with-definition.swift rename to test/Interop/Cxx/templates/partially-pre-defined-class-template.swift index 3772cd4f7a99a..3ef00d778cd68 100644 --- a/test/Interop/Cxx/templates/decl-with-definition.swift +++ b/test/Interop/Cxx/templates/partially-pre-defined-class-template.swift @@ -2,14 +2,14 @@ // // REQUIRES: executable_test -import DeclWithDefinition +import PartiallyPreDefinedClassTemplate import StdlibUnittest var TemplatesTestSuite = TestSuite("TemplatesTestSuite") TemplatesTestSuite.test("has-partial-definition") { let myInt = IntWrapper(value: 32) - var magicInt = PartiallyDefinedMagicallyWrappedInt(t: myInt) + var magicInt = PartiallyPreDefinedMagicallyWrappedInt(t: myInt) expectEqual(magicInt.getValuePlusArg(5), 37) } From bec0aae7e5c983b62835d178e36a70703eb0794a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sat, 26 Sep 2020 21:54:28 -0700 Subject: [PATCH 080/745] [Function builders] Add support for function builders on stored struct properties. Implements SR-13188, which is part of the function builders proposal under review. --- lib/SILGen/SILGenConstructor.cpp | 19 ++++++- lib/Sema/CodeSynthesis.cpp | 22 +++++++ lib/Sema/TypeCheckAttr.cpp | 13 ++++- test/Constraints/function_builder.swift | 21 +++++++ test/SILGen/function_builder_memberwise.swift | 57 +++++++++++++++++++ 5 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 test/SILGen/function_builder_memberwise.swift diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 162ee955f83a7..d38ce95f27825 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -18,6 +18,7 @@ #include "SILGenFunctionBuilder.h" #include "Scope.h" #include "swift/AST/ASTMangler.h" +#include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PropertyWrappers.h" @@ -211,8 +212,24 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, "number of args does not match number of fields"); (void)eltEnd; FullExpr scope(SGF.Cleanups, field->getParentPatternBinding()); + + RValue arg = std::move(*elti); + + // If the stored property has an attached function builder and its + // type is not a function type, the argument is a noescape closure + // that needs to be called. + if (field->getFunctionBuilderType()) { + if (!field->getValueInterfaceType() + ->lookThroughAllOptionalTypes()->is()) { + auto resultTy = cast(arg.getType()).getResult(); + arg = SGF.emitMonomorphicApply( + Loc, std::move(arg).getAsSingleValue(SGF, Loc), { }, resultTy, + resultTy, ApplyOptions::None, None, None); + } + } + maybeEmitPropertyWrapperInitFromValue(SGF, Loc, field, subs, - std::move(*elti)) + std::move(arg)) .forwardInto(SGF, Loc, init.get()); ++elti; } else { diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 384cad9dcb45c..60676e4c241e6 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -261,6 +261,20 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, } } + Type functionBuilderType= var->getFunctionBuilderType(); + if (functionBuilderType) { + // If the variable's type is structurally a function type, use that + // type. Otherwise, form a non-escaping function type for the function + // parameter. + bool isStructuralFunctionType = + varInterfaceType->lookThroughAllOptionalTypes() + ->is(); + if (!isStructuralFunctionType) { + auto extInfo = ASTExtInfoBuilder().withNoEscape().build(); + varInterfaceType = FunctionType::get({ }, varInterfaceType, extInfo); + } + } + // Create the parameter. auto *arg = new (ctx) ParamDecl(SourceLoc(), Loc, @@ -273,6 +287,14 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, // Don't allow the parameter to accept temporary pointer conversions. arg->setNonEphemeralIfPossible(); + // Attach a function builder attribute if needed. + if (functionBuilderType) { + auto typeExpr = TypeExpr::createImplicit(functionBuilderType, ctx); + auto attr = CustomAttr::create( + ctx, SourceLoc(), typeExpr, /*implicit=*/true); + arg->getAttrs().add(attr); + } + maybeAddMemberwiseDefaultArg(arg, var, params.size(), ctx); params.push_back(arg); diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index c6d981e9cb911..549c2419bf5bc 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2987,8 +2987,19 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { } else if (auto storage = dyn_cast(D)) { decl = storage; - // Check whether this is a property without an explicit getter. + // Check whether this is a storage declaration that is not permitted + // to have a function builder attached. auto shouldDiagnose = [&]() -> bool { + // An uninitialized stored property in a struct can have a function + // builder attached. + if (auto var = dyn_cast(decl)) { + if (var->isInstanceMember() && + isa(var->getDeclContext()) && + !var->getParentInitializer()) { + return false; + } + } + auto getter = storage->getParsedAccessor(AccessorKind::Get); if (!getter) return true; diff --git a/test/Constraints/function_builder.swift b/test/Constraints/function_builder.swift index d30a3a932f7ce..c6e7cf21a5a05 100644 --- a/test/Constraints/function_builder.swift +++ b/test/Constraints/function_builder.swift @@ -767,3 +767,24 @@ do { } catch { fatalError("Threw something else?") } + +// CHECK: testStoredProperties +struct MyTupleStruct { + @TupleBuilder let first: () -> T + @TupleBuilder let second: U +} + +print("testStoredProperties") +let ts1 = MyTupleStruct { + 1 + "hello" + if true { + "conditional" + } +} second: { + 3.14159 + "blah" +} + +// CHECK: MyTupleStruct<(Int, String, Optional), (Double, String)>(first: (Function), second: (3.14159, "blah")) +print(ts1) diff --git a/test/SILGen/function_builder_memberwise.swift b/test/SILGen/function_builder_memberwise.swift new file mode 100644 index 0000000000000..c87930e35aa06 --- /dev/null +++ b/test/SILGen/function_builder_memberwise.swift @@ -0,0 +1,57 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +@_functionBuilder +struct TupleBuilder { + static func buildBlock(_ t1: T1) -> (T1) { + return (t1) + } + + static func buildBlock(_ t1: T1, _ t2: T2) -> (T1, T2) { + return (t1, t2) + } + + static func buildBlock(_ t1: T1, _ t2: T2, _ t3: T3) + -> (T1, T2, T3) { + return (t1, t2, t3) + } + + static func buildBlock(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4) + -> (T1, T2, T3, T4) { + return (t1, t2, t3, t4) + } + + static func buildBlock( + _ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4, _ t5: T5 + ) -> (T1, T2, T3, T4, T5) { + return (t1, t2, t3, t4, t5) + } + + static func buildOptional(_ value: T?) -> T? { return value } +} + +struct MyTupleStruct { + @TupleBuilder let first: () -> T + @TupleBuilder let second: U + // CHECK: init(@TupleBuilder first: @escaping () -> T, @TupleBuilder second: () -> U) +} + +// CHECK-LABEL: sil hidden [ossa] @$s27function_builder_memberwise13MyTupleStructV5first6secondACyxq_Gxyc_q_yXEtcfC : $@convention(method) (@owned @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for , @noescape @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for , @thin MyTupleStruct.Type) -> @out MyTupleStruct { +// CHECK: bb0([[SELF:%.*]] : $*MyTupleStruct, [[FIRST:%.*]] : @owned $@callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for , [[SECOND:%.*]] : $@noescape @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for , [[META:%.*]] : $@thin MyTupleStruct.Type): +// CHECK-NEXT: [[FIRST_ADDR:%.*]] = struct_element_addr [[SELF]] : $*MyTupleStruct, #MyTupleStruct.first +// CHECK-NEXT: store [[FIRST]] to [init] [[FIRST_ADDR]] : $*@callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for +// CHECK-NEXT: [[SECOND_ADDR:%.*]] = struct_element_addr [[SELF]] : $*MyTupleStruct, #MyTupleStruct.second +// CHECK-NEXT: [[CALL_RESULT:%.*]] = alloc_stack $U +// CHECK-NEXT: apply [[SECOND]]([[CALL_RESULT]]) : $@noescape @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for +// CHECK-NEXT: copy_addr [take] [[CALL_RESULT]] to [initialization] [[SECOND_ADDR]] : $*U +func trigger(cond: Bool) { + _ = MyTupleStruct { + 1 + "hello" + if cond { + "conditional" + } + } second: { + 3.14159 + "blah" + } +} From 9a24aaf7140af68cdc8101b73cdfc6a17b4ae9aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodr=C3=ADguez=20Troiti=C3=B1o?= Date: Sun, 27 Sep 2020 20:22:41 -0700 Subject: [PATCH 081/745] [android] Disable a test that needs LTO support. With Gold from the Android NDK setting up LTO is complicated, and the CI machines are not setup for it. Disable this test in platforms that do not use LLD and LTO (which is basically Android with older NDKs). --- test/Interpreter/llvm_link_time_opt.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Interpreter/llvm_link_time_opt.swift b/test/Interpreter/llvm_link_time_opt.swift index f60b37e1af02b..214ba7656050e 100644 --- a/test/Interpreter/llvm_link_time_opt.swift +++ b/test/Interpreter/llvm_link_time_opt.swift @@ -1,6 +1,8 @@ // UNSUPPORTED: OS=windows-msvc // static library is not well supported yet on Windows +// REQUIRES: lld_lto + // RUN: %empty-directory(%t) // RUN: %target-swiftc_driver -emit-library -static -lto=llvm-full -emit-module %S/Inputs/lto/module1.swift -working-directory %t // RUN: %target-swiftc_driver -lto=llvm-full %s -I%t -L%t -lmodule1 -module-name main -o %t/main From 692fdde4fee0a40e5de5e6fa470ca8fd817c7ad8 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Thu, 24 Sep 2020 16:17:26 -0700 Subject: [PATCH 082/745] Move explicit module frontend options out of the set of flags emitted into module interfaces. They never belonged there and were placed there by accident. --- include/swift/Option/FrontendOptions.td | 65 ++++++++++++------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 63c68b14c918e..bab1f03411bdb 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -175,40 +175,6 @@ def autolink_library : Separate<["-"], "autolink-library">, def disable_typo_correction : Flag<["-"], "disable-typo-correction">, HelpText<"Disable typo correction">; -} // end let Flags = [FrontendOption, NoDriverOption] - -def debug_crash_Group : OptionGroup<"">; -class DebugCrashOpt : Group; - - -// Flags that are saved into module interfaces -let Flags = [FrontendOption, NoDriverOption, HelpHidden, ModuleInterfaceOption] in { - -def enable_objc_interop : - Flag<["-"], "enable-objc-interop">, - HelpText<"Enable Objective-C interop code generation and config directives">; - -def disable_objc_interop : - Flag<["-"], "disable-objc-interop">, - HelpText<"Disable Objective-C interop code generation and config directives">; - -def enable_objc_attr_requires_foundation_module : - Flag<["-"], "enable-objc-attr-requires-foundation-module">, - HelpText<"Enable requiring uses of @objc to require importing the " - "Foundation module">; - -def disable_objc_attr_requires_foundation_module : - Flag<["-"], "disable-objc-attr-requires-foundation-module">, - HelpText<"Disable requiring uses of @objc to require importing the " - "Foundation module">; - -def enable_experimental_concurrency : - Flag<["-"], "enable-experimental-concurrency">, - HelpText<"Enable experimental concurrency model">; - -def enable_resilience : Flag<["-"], "enable-resilience">, - HelpText<"Deprecated, use -enable-library-evolution instead">; - def disable_implicit_swift_modules: Flag<["-"], "disable-implicit-swift-modules">, HelpText<"Disable building Swift modules explicitly by the compiler">; @@ -231,6 +197,37 @@ def batch_scan_input_file def import_prescan : Flag<["-"], "import-prescan">, HelpText<"When performing a dependency scan, only dentify all imports of the main Swift module sources">; +} // end let Flags = [FrontendOption, NoDriverOption] + +def debug_crash_Group : OptionGroup<"">; +class DebugCrashOpt : Group; + + +// Flags that are saved into module interfaces +let Flags = [FrontendOption, NoDriverOption, HelpHidden, ModuleInterfaceOption] in { + def enable_objc_interop : + Flag<["-"], "enable-objc-interop">, + HelpText<"Enable Objective-C interop code generation and config directives">; + + def disable_objc_interop : + Flag<["-"], "disable-objc-interop">, + HelpText<"Disable Objective-C interop code generation and config directives">; + + def enable_objc_attr_requires_foundation_module : + Flag<["-"], "enable-objc-attr-requires-foundation-module">, + HelpText<"Enable requiring uses of @objc to require importing the " + "Foundation module">; + + def disable_objc_attr_requires_foundation_module : + Flag<["-"], "disable-objc-attr-requires-foundation-module">, + HelpText<"Disable requiring uses of @objc to require importing the " + "Foundation module">; + +def enable_experimental_concurrency : + Flag<["-"], "enable-experimental-concurrency">, + HelpText<"Enable experimental concurrency model">; + def enable_resilience : Flag<["-"], "enable-resilience">, + HelpText<"Deprecated, use -enable-library-evolution instead">; } // HIDDEN FLAGS From 8e63a4b43f77d8530b58f84939447cffc9692e79 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Mon, 28 Sep 2020 12:10:10 -0700 Subject: [PATCH 083/745] [Serialization] Gate Clang type (de)serialization behind UseClangFunctionTypes. --- lib/Serialization/Deserialization.cpp | 3 +++ lib/Serialization/Serialization.cpp | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 96789352635da..78dd359d94238 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -5799,6 +5799,9 @@ class SwiftToClangBasicReader : llvm::Expected ModuleFile::getClangType(ClangTypeID TID) { + if (!getContext().LangOpts.UseClangFunctionTypes) + return nullptr; + if (TID == 0) return nullptr; diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 1d5bb74a1e28e..e577c12090c06 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -4140,7 +4140,10 @@ class Serializer::TypeSerializer : public TypeVisitor { using namespace decls_block; auto resultType = S.addTypeRef(fnTy->getResult()); - auto clangType = S.addClangTypeRef(fnTy->getClangTypeInfo().getType()); + auto clangType = + S.getASTContext().LangOpts.UseClangFunctionTypes + ? S.addClangTypeRef(fnTy->getClangTypeInfo().getType()) + : ClangTypeID(0); unsigned abbrCode = S.DeclTypeAbbrCodes[FunctionTypeLayout::Code]; FunctionTypeLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, From 88e256256b72fb6c2d4a25ba21caaa47d27d18e5 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Mon, 28 Sep 2020 14:29:58 -0700 Subject: [PATCH 084/745] [NFC] Tidy up test relying to Clang type serialization. --- test/Sema/clang_types_in_ast.swift | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/test/Sema/clang_types_in_ast.swift b/test/Sema/clang_types_in_ast.swift index e087c0e66151a..5842afa91ee90 100644 --- a/test/Sema/clang_types_in_ast.swift +++ b/test/Sema/clang_types_in_ast.swift @@ -3,12 +3,14 @@ // RUN: %target-swift-frontend %s -typecheck -DNOCRASH1 -use-clang-function-types // RUN: %target-swift-frontend %s -typecheck -DNOCRASH2 -sdk %clang-importer-sdk // RUN: %target-swift-frontend %s -typecheck -DNOCRASH2 -sdk %clang-importer-sdk -use-clang-function-types -// RUN: %target-swift-frontend %s -DAUXMODULE -module-name Foo -emit-module -o %t // rdar://problem/57644243 : We shouldn't crash if -use-clang-function-types is not enabled. +// RUN: %target-swift-frontend %s -DAUXMODULE -module-name Foo -emit-module -o %t // RUN: %target-swift-frontend %s -typecheck -DNOCRASH3 -I %t -// RUN: %target-swift-frontend %s -typecheck -DCRASH -I %t -use-clang-function-types +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -DAUXMODULE -module-name Foo -emit-module -o %t -use-clang-function-types +// RUN: %target-swift-frontend %s -typecheck -DNOCRASH3 -I %t -use-clang-function-types #if NOCRASH1 public func my_signal() -> Optional<@convention(c) (Int32) -> Void> { @@ -45,19 +47,3 @@ public func my_signal4() -> Optional<@convention(c) (Int32) -> Void> { return Foo.DUMMY_SIGNAL2 } #endif - -#if CRASH -import Foo -public func my_signal1() -> Optional<@convention(c) (Int32) -> ()> { - return Foo.DUMMY_SIGNAL1 -} -public func my_signal2() -> Optional<@convention(c) (Int32) -> Void> { - return Foo.DUMMY_SIGNAL1 -} -public func my_signal3() -> Optional<@convention(c) (Int32) -> ()> { - return Foo.DUMMY_SIGNAL2 -} -public func my_signal4() -> Optional<@convention(c) (Int32) -> Void> { - return Foo.DUMMY_SIGNAL2 -} -#endif From f5a03f54e11632be4994706f93527b758a8c204c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 28 Sep 2020 17:40:36 -0400 Subject: [PATCH 085/745] ASTScope: Fix linker error when building without asserts --- lib/AST/UnqualifiedLookup.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 99aa0297fa9a7..2b6e354ca0d4c 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -778,6 +778,8 @@ unsigned UnqualifiedLookupFactory::lookupCounter = 0; // set to ~0 when not debugging const unsigned UnqualifiedLookupFactory::targetLookup = ~0; +#endif // NDEBUG + namespace { class ASTScopeDeclConsumerForLocalLookup @@ -831,6 +833,4 @@ ValueDecl *ASTScope::lookupSingleLocalDecl(SourceFile *sf, DeclName name, if (result.size() != 1) return nullptr; return result[0]; -} - -#endif // NDEBUG \ No newline at end of file +} \ No newline at end of file From fe9d1ed595db13ef4ba69758187875dc99589ab6 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sat, 26 Sep 2020 00:07:42 -0400 Subject: [PATCH 086/745] Sema: Use ASTScope::lookupSingleLocalDecl() in VarDeclUsageChecker::handleIfConfig() --- lib/Sema/MiscDiagnostics.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 6e9c126f3599c..e032f594922b0 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -2282,6 +2282,8 @@ bool swift::computeFixitsForOverridenDeclaration( namespace { class VarDeclUsageChecker : public ASTWalker { + DeclContext *DC; + DiagnosticEngine &Diags; // Keep track of some information about a variable. enum { @@ -2318,7 +2320,8 @@ class VarDeclUsageChecker : public ASTWalker { void operator=(const VarDeclUsageChecker &) = delete; public: - VarDeclUsageChecker(DiagnosticEngine &Diags) : Diags(Diags) {} + VarDeclUsageChecker(DeclContext *DC, + DiagnosticEngine &Diags) : DC(DC), Diags(Diags) {} // After we have scanned the entire region, diagnose variables that could be // declared with a narrower usage kind. @@ -3140,7 +3143,10 @@ std::pair VarDeclUsageChecker::walkToExprPre(Expr *E) { void VarDeclUsageChecker::handleIfConfig(IfConfigDecl *ICD) { struct ConservativeDeclMarker : public ASTWalker { VarDeclUsageChecker &VDUC; - ConservativeDeclMarker(VarDeclUsageChecker &VDUC) : VDUC(VDUC) {} + SourceFile *SF; + + ConservativeDeclMarker(VarDeclUsageChecker &VDUC) + : VDUC(VDUC), SF(VDUC.DC->getParentSourceFile()) {} Expr *walkToExprPost(Expr *E) override { // If we see a bound reference to a decl in an inactive #if block, then @@ -3148,6 +3154,16 @@ void VarDeclUsageChecker::handleIfConfig(IfConfigDecl *ICD) { // unused" and "could be marked let" warnings for it. if (auto *DRE = dyn_cast(E)) VDUC.addMark(DRE->getDecl(), RK_Read|RK_Written); + else if (auto *declRef = dyn_cast(E)) { + auto name = declRef->getName(); + auto loc = declRef->getLoc(); + if (name.isSimpleName() && loc.isValid()) { + auto *varDecl = dyn_cast_or_null( + ASTScope::lookupSingleLocalDecl(SF, name.getFullName(), loc)); + if (varDecl) + VDUC.addMark(varDecl, RK_Read|RK_Written); + } + } return E; } }; @@ -3166,7 +3182,7 @@ void VarDeclUsageChecker::handleIfConfig(IfConfigDecl *ICD) { void swift:: performTopLevelDeclDiagnostics(TopLevelCodeDecl *TLCD) { auto &ctx = TLCD->getDeclContext()->getASTContext(); - VarDeclUsageChecker checker(ctx.Diags); + VarDeclUsageChecker checker(TLCD, ctx.Diags); TLCD->walk(checker); } @@ -3181,7 +3197,7 @@ void swift::performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD) { // be checked as part of their parent function or TopLevelCodeDecl. if (!AFD->getDeclContext()->isLocalContext()) { auto &ctx = AFD->getDeclContext()->getASTContext(); - VarDeclUsageChecker checker(ctx.Diags); + VarDeclUsageChecker checker(AFD, ctx.Diags); AFD->walk(checker); } From 025921c927e4764ac186f58c265b7f585928f279 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 28 Sep 2020 15:14:08 -0400 Subject: [PATCH 087/745] Parse: Move the lookup of 'self' for a SuperRefExpr to preCheckExpression() --- include/swift/AST/Expr.h | 1 + lib/Parse/ParseExpr.cpp | 31 ++-------------------------- lib/Sema/TypeCheckCaptures.cpp | 6 ++++-- lib/Sema/TypeCheckConstraints.cpp | 34 +++++++++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 31 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index b747589cb5078..3945003098bdd 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -1332,6 +1332,7 @@ class SuperRefExpr : public Expr { : Expr(ExprKind::SuperRef, Implicit, SuperTy), Self(Self), Loc(Loc) {} VarDecl *getSelf() const { return Self; } + void setSelf(VarDecl *self) { Self = self; } SourceLoc getSuperLoc() const { return Loc; } SourceRange getSourceRange() const { return Loc; } diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index b61e21834871e..cf221d429e34f 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -853,29 +853,6 @@ UnresolvedDeclRefExpr *Parser::parseExprOperator() { return new (Context) UnresolvedDeclRefExpr(name, refKind, DeclNameLoc(loc)); } -static VarDecl *getImplicitSelfDeclForSuperContext(Parser &P, - DeclContext *DC, - SourceLoc Loc) { - auto *methodContext = DC->getInnermostMethodContext(); - if (!methodContext) { - P.diagnose(Loc, diag::super_not_in_class_method); - return nullptr; - } - - // Do an actual lookup for 'self' in case it shows up in a capture list. - auto *methodSelf = methodContext->getImplicitSelfDecl(); - auto *lookupSelf = P.lookupInScope(DeclNameRef(P.Context.Id_self)); - if (lookupSelf && lookupSelf != methodSelf) { - // FIXME: This is the wrong diagnostic for if someone manually declares a - // variable named 'self' using backticks. - P.diagnose(Loc, diag::super_in_closure_with_capture); - P.diagnose(lookupSelf->getLoc(), diag::super_in_closure_with_capture_here); - return nullptr; - } - - return methodSelf; -} - /// parseExprSuper /// /// expr-super: @@ -903,12 +880,8 @@ ParserResult Parser::parseExprSuper() { return nullptr; } - VarDecl *selfDecl = - getImplicitSelfDeclForSuperContext(*this, CurDeclContext, superLoc); - if (!selfDecl) - return makeParserResult(new (Context) ErrorExpr(superLoc)); - - return makeParserResult(new (Context) SuperRefExpr(selfDecl, superLoc, + return makeParserResult(new (Context) SuperRefExpr(/*selfDecl=*/nullptr, + superLoc, /*Implicit=*/false)); } diff --git a/lib/Sema/TypeCheckCaptures.cpp b/lib/Sema/TypeCheckCaptures.cpp index c83ed135bcc9a..bb817f58b88fc 100644 --- a/lib/Sema/TypeCheckCaptures.cpp +++ b/lib/Sema/TypeCheckCaptures.cpp @@ -572,8 +572,10 @@ class FindCapturedVars : public ASTWalker { // When we see a reference to the 'super' expression, capture 'self' decl. if (auto *superE = dyn_cast(E)) { - if (CurDC->isChildContextOf(superE->getSelf()->getDeclContext())) - addCapture(CapturedValue(superE->getSelf(), 0, superE->getLoc())); + if (auto *selfDecl = superE->getSelf()) { + if (CurDC->isChildContextOf(selfDecl->getDeclContext())) + addCapture(CapturedValue(selfDecl, 0, superE->getLoc())); + } return { false, superE }; } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 5e3728222ca5b..e2ac8e0bcec5a 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -1114,6 +1114,29 @@ namespace { bool shouldWalkCaptureInitializerExpressions() override { return true; } + VarDecl *getImplicitSelfDeclForSuperContext(SourceLoc Loc) { + auto *methodContext = DC->getInnermostMethodContext(); + if (!methodContext) { + Ctx.Diags.diagnose(Loc, diag::super_not_in_class_method); + return nullptr; + } + + // Do an actual lookup for 'self' in case it shows up in a capture list. + auto *methodSelf = methodContext->getImplicitSelfDecl(); + auto *lookupSelf = ASTScope::lookupSingleLocalDecl(DC->getParentSourceFile(), + Ctx.Id_self, Loc); + if (lookupSelf && lookupSelf != methodSelf) { + // FIXME: This is the wrong diagnostic for if someone manually declares a + // variable named 'self' using backticks. + Ctx.Diags.diagnose(Loc, diag::super_in_closure_with_capture); + Ctx.Diags.diagnose(lookupSelf->getLoc(), + diag::super_in_closure_with_capture_here); + return nullptr; + } + + return methodSelf; + } + std::pair walkToExprPre(Expr *expr) override { // If this is a call, record the argument expression. if (auto call = dyn_cast(expr)) { @@ -1156,6 +1179,17 @@ namespace { return std::make_pair(recursive, expr); }; + // Resolve 'super' references. + if (auto *superRef = dyn_cast(expr)) { + auto loc = superRef->getLoc(); + auto *selfDecl = getImplicitSelfDeclForSuperContext(loc); + if (selfDecl == nullptr) + return finish(true, new (Ctx) ErrorExpr(loc)); + + superRef->setSelf(selfDecl); + return finish(true, superRef); + } + // For closures, type-check the patterns and result type as written, // but do not walk into the body. That will be type-checked after // we've determine the complete function type. From bd7763df3099bc5122eeca8d3b7aeea8d87b40a4 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 25 Sep 2020 15:56:01 -0400 Subject: [PATCH 088/745] AST: Remove unused UnqualifiedLookupFactory::resultsSizeBeforeLocalsPass This was read but never written to. I believe it was an artifact of the pre-ASTScope lookup logic. --- lib/AST/UnqualifiedLookup.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 99aa0297fa9a7..39460734b7446 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -144,9 +144,6 @@ namespace { static const unsigned targetLookup; #endif - public: // for exp debugging - unsigned resultsSizeBeforeLocalsPass = ~0; - public: // clang-format off UnqualifiedLookupFactory(DeclNameRef Name, @@ -708,16 +705,10 @@ void UnqualifiedLookupFactory::printScopes(raw_ostream &out) const { void UnqualifiedLookupFactory::printResults(raw_ostream &out) const { for (auto i : indices(Results)) { - if (i == resultsSizeBeforeLocalsPass) - out << "============== next pass ============\n"; out << i << ": "; Results[i].print(out); out << "\n"; } - if (resultsSizeBeforeLocalsPass == Results.size()) - out << "============== next pass ============\n"; - if (resultsSizeBeforeLocalsPass == ~0u) - out << "never tried locals\n\n"; } void UnqualifiedLookupFactory::print(raw_ostream &OS) const { From 225d87a9bd1373a8cd23a2bd6d85396e4dc08b05 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 28 Sep 2020 15:36:18 -0700 Subject: [PATCH 089/745] [CSBindings] NFC: Simplify `TypeVariableBinding::attempt` but extracting fix computation Add a separate method `fixForHole` on `TypeVariableBinding` responsible for determining whether fix is required and if so, what kind of fix to apply when a particular type variable is resolved to a hole. --- lib/Sema/CSBindings.cpp | 121 ++++++++++++++++++++++-------------- lib/Sema/ConstraintSystem.h | 5 ++ 2 files changed, 78 insertions(+), 48 deletions(-) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index f43d31d5f26dd..da8fbc4525aae 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -1217,6 +1217,77 @@ bool TypeVarBindingProducer::computeNext() { return true; } +Optional> +TypeVariableBinding::fixForHole(ConstraintSystem &cs) const { + auto *dstLocator = TypeVar->getImpl().getLocator(); + auto *srcLocator = Binding.getLocator(); + + unsigned defaultImpact = 1; + + if (auto *GP = TypeVar->getImpl().getGenericParameter()) { + // If it is represetative for a key path root, let's emit a more + // specific diagnostic. + auto *keyPathRoot = + cs.isRepresentativeFor(TypeVar, ConstraintLocator::KeyPathRoot); + if (keyPathRoot) { + ConstraintFix *fix = SpecifyKeyPathRootType::create( + cs, keyPathRoot->getImpl().getLocator()); + return std::make_pair(fix, defaultImpact); + } else { + auto path = dstLocator->getPath(); + // Drop `generic parameter` locator element so that all missing + // generic parameters related to the same path can be coalesced later. + ConstraintFix *fix = DefaultGenericArgument::create( + cs, GP, + cs.getConstraintLocator(dstLocator->getAnchor(), path.drop_back())); + return std::make_pair(fix, defaultImpact); + } + } + + if (TypeVar->getImpl().isClosureParameterType()) { + ConstraintFix *fix = SpecifyClosureParameterType::create(cs, dstLocator); + return std::make_pair(fix, defaultImpact); + } + + if (TypeVar->getImpl().isClosureResultType()) { + auto *closure = castToExpr(dstLocator->getAnchor()); + // If the whole body is being ignored due to a pre-check failure, + // let's not record a fix about result type since there is + // just not enough context to infer it without a body. + if (cs.hasFixFor(cs.getConstraintLocator(closure->getBody()), + FixKind::IgnoreInvalidFunctionBuilderBody)) + return None; + + ConstraintFix *fix = SpecifyClosureReturnType::create(cs, dstLocator); + return std::make_pair(fix, defaultImpact); + } + + if (srcLocator->directlyAt()) { + ConstraintFix *fix = SpecifyObjectLiteralTypeImport::create(cs, dstLocator); + return std::make_pair(fix, defaultImpact); + } + + if (srcLocator->isKeyPathRoot()) { + // If we recorded an invalid key path fix, let's skip this specify root + // type fix because it wouldn't produce a useful diagnostic. + auto *kpLocator = cs.getConstraintLocator(srcLocator->getAnchor()); + if (cs.hasFixFor(kpLocator, FixKind::AllowKeyPathWithoutComponents)) + return None; + + ConstraintFix *fix = SpecifyKeyPathRootType::create(cs, dstLocator); + return std::make_pair(fix, defaultImpact); + } + + if (dstLocator->directlyAt()) { + // This is a dramatic event, it means that there is absolutely + // no contextual information to resolve type of `nil`. + ConstraintFix *fix = SpecifyContextualTypeForNil::create(cs, dstLocator); + return std::make_pair(fix, /*impact=*/(unsigned)10); + } + + return None; +} + bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { auto type = Binding.BindingType; auto *srcLocator = Binding.getLocator(); @@ -1238,56 +1309,10 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { // resolved and had to be bound to a placeholder "hole" type. cs.increaseScore(SK_Hole); - ConstraintFix *fix = nullptr; - unsigned fixImpact = 1; - - if (auto *GP = TypeVar->getImpl().getGenericParameter()) { - // If it is represetative for a key path root, let's emit a more - // specific diagnostic. - auto *keyPathRoot = - cs.isRepresentativeFor(TypeVar, ConstraintLocator::KeyPathRoot); - if (keyPathRoot) { - fix = SpecifyKeyPathRootType::create( - cs, keyPathRoot->getImpl().getLocator()); - } else { - auto path = dstLocator->getPath(); - // Drop `generic parameter` locator element so that all missing - // generic parameters related to the same path can be coalesced later. - fix = DefaultGenericArgument::create( - cs, GP, - cs.getConstraintLocator(dstLocator->getAnchor(), - path.drop_back())); - } - } else if (TypeVar->getImpl().isClosureParameterType()) { - fix = SpecifyClosureParameterType::create(cs, dstLocator); - } else if (TypeVar->getImpl().isClosureResultType()) { - auto *locator = TypeVar->getImpl().getLocator(); - auto *closure = castToExpr(locator->getAnchor()); - // If the whole body is being ignored due to a pre-check failure, - // let's not record a fix about result type since there is - // just not enough context to infer it without a body. - if (!cs.hasFixFor(cs.getConstraintLocator(closure->getBody()), - FixKind::IgnoreInvalidFunctionBuilderBody)) - fix = SpecifyClosureReturnType::create(cs, dstLocator); - } else if (srcLocator->directlyAt()) { - fix = SpecifyObjectLiteralTypeImport::create(cs, dstLocator); - } else if (srcLocator->isKeyPathRoot()) { - // If we recorded an invalid key path fix, let's skip this specify root - // type fix because it wouldn't produce a useful diagnostic. - auto *kpLocator = cs.getConstraintLocator(srcLocator->getAnchor()); - if (cs.hasFixFor(kpLocator, FixKind::AllowKeyPathWithoutComponents)) + if (auto fix = fixForHole(cs)) { + if (cs.recordFix(/*fix=*/fix->first, /*impact=*/fix->second)) return true; - - fix = SpecifyKeyPathRootType::create(cs, dstLocator); - } else if (dstLocator->directlyAt()) { - fix = SpecifyContextualTypeForNil::create(cs, dstLocator); - // This is a dramatic event, it means that there is absolutely - // no contextual information to resolve type of `nil`. - fixImpact = 10; } - - if (fix && cs.recordFix(fix, fixImpact)) - return true; } } diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index d7e24ecbf94a1..d766911c31f84 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -5632,6 +5632,11 @@ class TypeVariableBinding { bool attempt(ConstraintSystem &cs) const; + /// Determine what fix (if any) needs to be introduced into a + /// constraint system as part of resolving type variable as a hole. + Optional> + fixForHole(ConstraintSystem &cs) const; + void print(llvm::raw_ostream &Out, SourceManager *) const { PrintOptions PO; PO.PrintTypesForDebugging = true; From 410b88ef07a1d18fecf9a043373b6600fb1458b2 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Mon, 28 Sep 2020 15:54:46 -0700 Subject: [PATCH 090/745] Fix @effects of finalizeUninitializedArray when assertions are enabled (#34101) When assertions are enabled _endCOWMutation writes to _native.isImmutable. But finalizeUninitializedArray is marked as @effects(readnone). This mismatch caused miscompile in the stdlib due to CodeSinking optimization which relies on SILInstruction::mayReadOrWriteMemory which looks at @effects. --- stdlib/public/core/ArrayShared.swift | 15 +++++++++++++++ test/SILOptimizer/opt-remark-generator-yaml.swift | 2 ++ test/SILOptimizer/opt-remark-generator.swift | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/stdlib/public/core/ArrayShared.swift b/stdlib/public/core/ArrayShared.swift index 28c1d25696c55..05adaf5c6feb9 100644 --- a/stdlib/public/core/ArrayShared.swift +++ b/stdlib/public/core/ArrayShared.swift @@ -64,6 +64,7 @@ func _deallocateUninitializedArray( array._deallocateUninitialized() } +#if !INTERNAL_CHECKS_ENABLED @_alwaysEmitIntoClient @_semantics("array.finalize_intrinsic") @_effects(readnone) @@ -75,6 +76,20 @@ func _finalizeUninitializedArray( mutableArray._endMutation() return mutableArray } +#else +// When asserts are enabled, _endCOWMutation writes to _native.isImmutable +// So we cannot have @_effects(readnone) +@_alwaysEmitIntoClient +@_semantics("array.finalize_intrinsic") +public // COMPILER_INTRINSIC +func _finalizeUninitializedArray( + _ array: __owned Array +) -> Array { + var mutableArray = array + mutableArray._endMutation() + return mutableArray +} +#endif extension Collection { // Utility method for collections that wish to implement diff --git a/test/SILOptimizer/opt-remark-generator-yaml.swift b/test/SILOptimizer/opt-remark-generator-yaml.swift index 13a9301f1546d..55f2862bcf955 100644 --- a/test/SILOptimizer/opt-remark-generator-yaml.swift +++ b/test/SILOptimizer/opt-remark-generator-yaml.swift @@ -3,6 +3,8 @@ // RUN: %empty-directory(%t) // RUN: %target-swiftc_driver -wmo -O -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil -save-optimization-record=yaml -save-optimization-record-path %t/note.yaml %s -o /dev/null && %FileCheck --input-file=%t/note.yaml %s +// REQUIRES: optimized_stdlib,swift_stdlib_no_asserts + // This file is testing out the basic YAML functionality to make sure that it // works without burdening opt-remark-generator-yaml.swift with having to update all // of the yaml test cases everytime new code is added. diff --git a/test/SILOptimizer/opt-remark-generator.swift b/test/SILOptimizer/opt-remark-generator.swift index 6aedcd8ca8dc8..dee756a3beb8e 100644 --- a/test/SILOptimizer/opt-remark-generator.swift +++ b/test/SILOptimizer/opt-remark-generator.swift @@ -1,5 +1,5 @@ // RUN: %target-swiftc_driver -O -Rpass-missed=sil-opt-remark-gen -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil %s -o /dev/null -Xfrontend -verify -// REQUIRES: optimized_stdlib +// REQUIRES: optimized_stdlib,swift_stdlib_no_asserts // XFAIL: OS=linux-androideabi && CPU=armv7 From aaa4e45fa819ad83f70553bc162532199d436217 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Mon, 28 Sep 2020 16:26:30 -0700 Subject: [PATCH 091/745] [ClangImporter] Update umbrella header diagnostic handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In apple/llvm-project#1860, the diagnostic for a missing import in an umbrella header was improved by moving it to the end of the file and including a fix-it suggesting the import that would be needed. This breaks two things on the Swift side: • One Swift test assumes the old source location will be used. • The `ClangSourceBufferImporter` doesn’t work correctly when a diagnostic is emitted at EOF. It tries to create a virtual file covering EOF..; diff --git a/test/ClangImporter/diags_from_module.swift b/test/ClangImporter/diags_from_module.swift index 8e74e7a8ca062..5cc81a87e1291 100644 --- a/test/ClangImporter/diags_from_module.swift +++ b/test/ClangImporter/diags_from_module.swift @@ -37,7 +37,7 @@ import Module // CHECK-PRIMARY: diags_from_module.swift:[[@LINE-4]]:8: error: could not build Objective-C module 'Module' // CHECK-WARN: Sub2.h:7:2: warning: here is some warning about something -// CHECK-WARN: :1:1: warning: umbrella header for module 'Module' does not include header 'NotInModule.h' +// CHECK-WARN: Module.h:20:1: warning: umbrella header for module 'Module' does not include header 'NotInModule.h' // FIXME: show [-Wincomplete-umbrella] // CHECK-NO-WARN-NOT: warning about something From 77a76c9900b6c1efeac0ac278210763402655a9f Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Mon, 28 Sep 2020 16:41:35 -0700 Subject: [PATCH 092/745] [LookupVisibleDecls] Use correct BaseTy to check the decls are applicable 'lookupTypeMembers()' accepts 'BaseTy' to check found value decls are applicable to the type. When looking into super classes, it used to use the interface type of the super class which was not good. Instead, use the original "base type" consistently. rdar://problem/69308207 https://bugs.swift.org/browse/SR-13574 --- lib/Sema/LookupVisibleDecls.cpp | 12 +++++++----- test/IDE/complete_sr13574.swift | 31 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 test/IDE/complete_sr13574.swift diff --git a/lib/Sema/LookupVisibleDecls.cpp b/lib/Sema/LookupVisibleDecls.cpp index f0558a251b787..8656abf12a145 100644 --- a/lib/Sema/LookupVisibleDecls.cpp +++ b/lib/Sema/LookupVisibleDecls.cpp @@ -693,11 +693,13 @@ static void lookupVisibleMemberDeclsImpl( } } + auto lookupTy = BaseTy; + const auto synthesizeAndLookupTypeMembers = [&](NominalTypeDecl *NTD) { synthesizeMemberDeclsForLookup(NTD, CurrDC); // Look in for members of a nominal type. - lookupTypeMembers(BaseTy, BaseTy, Consumer, CurrDC, LS, Reason); + lookupTypeMembers(BaseTy, lookupTy, Consumer, CurrDC, LS, Reason); }; llvm::SmallPtrSet Ancestors; @@ -725,7 +727,7 @@ static void lookupVisibleMemberDeclsImpl( Ancestors.insert(CD); Reason = getReasonForSuper(Reason); - BaseTy = CD->getSuperclass(); + lookupTy = CD->getSuperclass(); LS = LS.withOnSuperclass(); if (CD->inheritsSuperclassInitializers()) @@ -734,7 +736,7 @@ static void lookupVisibleMemberDeclsImpl( // Look into the inheritance chain. do { - const auto CurClass = BaseTy->getClassOrBoundGenericClass(); + const auto CurClass = lookupTy->getClassOrBoundGenericClass(); // FIXME: This path is no substitute for an actual circularity check. // The real fix is to check that the superclass doesn't introduce a @@ -744,10 +746,10 @@ static void lookupVisibleMemberDeclsImpl( synthesizeAndLookupTypeMembers(CurClass); - BaseTy = CurClass->getSuperclass(); + lookupTy = CurClass->getSuperclass(); if (!CurClass->inheritsSuperclassInitializers()) LS = LS.withoutInheritsSuperclassInitializers(); - } while (BaseTy); + } while (lookupTy); } swift::DynamicLookupInfo::DynamicLookupInfo( diff --git a/test/IDE/complete_sr13574.swift b/test/IDE/complete_sr13574.swift new file mode 100644 index 0000000000000..8069f4717a622 --- /dev/null +++ b/test/IDE/complete_sr13574.swift @@ -0,0 +1,31 @@ +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERRIDE | %FileCheck %s --check-prefix=OVERRIDE +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=MEMBER | %FileCheck %s --check-prefix=MEMBER + +class Root { + func onRoot() {} +} + +class Base: Root { + func onBase() -> T {} +} + +class Derived: Base { + func onDerived() {} + + func #^OVERRIDE^# +// OVERRIDE: Begin completions, 2 items +// OVERRIDE-DAG: Decl[InstanceMethod]/Super/Erase[5]: override func onBase() -> T {|}; +// OVERRIDE-DAG: Decl[InstanceMethod]/Super/Erase[5]: override func onRoot() {|}; +// OVERRIDE-DAG: End completions + +} + +func testMember(val: Derived) { + val.#^MEMBER^# +// MEMBER: Begin completions, 4 items +// MEMBER-DAG: Keyword[self]/CurrNominal: self[#Derived#]; name=self +// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal: onDerived()[#Void#]; name=onDerived() +// MEMBER-DAG: Decl[InstanceMethod]/Super: onBase()[#Int#]; name=onBase() +// MEMBER-DAG: Decl[InstanceMethod]/Super: onRoot()[#Void#]; name=onRoot() +// MEMBER: End completions +} From 9b2cd5e3ffa09063a8b64250cd0c62850a41fd26 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 26 Aug 2020 10:15:07 -0700 Subject: [PATCH 093/745] [NameLookup] Teach unqualified lookup to resolve backing property wrapper and projected value references --- include/swift/AST/NameLookup.h | 2 ++ lib/AST/UnqualifiedLookup.cpp | 25 +++++++++++++++++++++++-- lib/Sema/TypeCheckConstraints.cpp | 4 ++++ lib/Sema/TypeCheckNameLookup.cpp | 2 ++ lib/Sema/TypeChecker.h | 2 ++ 5 files changed, 33 insertions(+), 2 deletions(-) diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 80c3d72aa1051..0549d0715e9d2 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -225,6 +225,8 @@ enum class UnqualifiedLookupFlags { /// This lookup should include results from outside the innermost scope with /// results. IncludeOuterResults = 1 << 4, + /// Includes property wrapper name lookup results + IncludePropertyWrapperResults = 1 << 5, }; using UnqualifiedLookupOptions = OptionSet; diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index b32cf8eaede13..05dd839ec6e07 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -22,6 +22,7 @@ #include "swift/AST/ModuleNameLookup.h" #include "swift/AST/NameLookup.h" #include "swift/AST/NameLookupRequests.h" +#include "swift/AST/PropertyWrappers.h" #include "swift/Basic/Debug.h" #include "swift/Basic/STLExtras.h" #include "swift/Basic/SourceManager.h" @@ -602,8 +603,28 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::consume( } } - if (!value->getName().matchesRef(factory.Name.getFullName())) - continue; + auto fullName = factory.Name.getFullName(); + if (!value->getName().matchesRef(fullName)) { + if (!factory.options.contains(UnqualifiedLookupFlags::IncludePropertyWrapperResults)) + continue; + + auto *varDecl = dyn_cast(value); + if (!varDecl || !varDecl->hasAttachedPropertyWrapper()) + continue; + + auto wrapperInfo = varDecl->getPropertyWrapperBackingPropertyInfo(); + if (!wrapperInfo) + continue; + + if (wrapperInfo.backingVar->ValueDecl::getName().matchesRef(fullName)) { + value = wrapperInfo.backingVar; + } else if (wrapperInfo.projectionVar && + wrapperInfo.projectionVar->ValueDecl::getName().matchesRef(fullName)) { + value = wrapperInfo.projectionVar; + } else { + continue; + } + } // In order to preserve the behavior of the existing context-based lookup, // which finds all results for non-local variables at the top level instead diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 5e3728222ca5b..9d691d374277e 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -538,6 +538,10 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, // name/module qualifier to access top-level name. lookupOptions |= NameLookupFlags::IncludeOuterResults; + // Include property wrapper results in case we have a reference to a backing + // property wrapper or projected value + lookupOptions |= NameLookupFlags::IncludePropertyWrapperResults; + if (Loc.isInvalid()) DC = DC->getModuleScopeContext(); diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 62347f886bbec..6a474310aa4b3 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -211,6 +211,8 @@ convertToUnqualifiedLookupOptions(NameLookupOptions options) { newOptions |= UnqualifiedLookupFlags::IgnoreAccessControl; if (options.contains(NameLookupFlags::IncludeOuterResults)) newOptions |= UnqualifiedLookupFlags::IncludeOuterResults; + if (options.contains(NameLookupFlags::IncludePropertyWrapperResults)) + newOptions |= UnqualifiedLookupFlags::IncludePropertyWrapperResults; return newOptions; } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index dbd5f63e86b05..c0ae437b097b5 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -192,6 +192,8 @@ enum class NameLookupFlags { /// Whether to include results from outside the innermost scope that has a /// result. IncludeOuterResults = 1 << 1, + /// Whether to consider property wrapper names + IncludePropertyWrapperResults = 1 << 2, }; /// A set of options that control name lookup. From b33dbedd9b8d3dc2e6ee28373c69a24764249745 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sun, 27 Sep 2020 12:34:11 -0700 Subject: [PATCH 094/745] [SILGen] Teach SIlGen to emit local property wrappers --- lib/SIL/IR/SILDeclRef.cpp | 3 +- lib/SILGen/SILGenDecl.cpp | 36 ++++++++++++++++++++++ lib/SILGen/SILGenLValue.cpp | 60 ++++++++++++++++++++++++------------- 3 files changed, 78 insertions(+), 21 deletions(-) diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 1b6f2ce79950f..b7c5239a4999e 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -487,7 +487,8 @@ IsSerialized_t SILDeclRef::isSerialized() const { // Stored property initializers are inlinable if the type is explicitly // marked as @frozen. - if (isStoredPropertyInitializer() || isPropertyWrapperBackingInitializer()) { + if (isStoredPropertyInitializer() || (isPropertyWrapperBackingInitializer() && + !d->getDeclContext()->isLocalContext())) { auto *nominal = cast(d->getDeclContext()); auto scope = nominal->getFormalAccessScope(/*useDC=*/nullptr, diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index dfa1f39b72ac6..87563d3bfe07f 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -22,6 +22,7 @@ #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/PropertyWrappers.h" #include "swift/Basic/ProfileCounter.h" #include "swift/SIL/FormalLinkage.h" #include "swift/SIL/PrettyStackTrace.h" @@ -1171,6 +1172,22 @@ void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD, // the initialization. Otherwise, mark it uninitialized for DI to resolve. if (auto *Init = PBD->getExecutableInit(idx)) { FullExpr Scope(Cleanups, CleanupLocation(Init)); + + auto *var = PBD->getSingleVar(); + if (var && var->getDeclContext()->isLocalContext()) { + if (auto *orig = var->getOriginalWrappedProperty()) { + auto wrapperInfo = orig->getPropertyWrapperBackingPropertyInfo(); + Init = wrapperInfo.wrappedValuePlaceholder->getOriginalWrappedValue(); + + auto value = emitRValue(Init); + emitApplyOfPropertyWrapperBackingInitializer(SILLocation(PBD), orig, + getForwardingSubstitutionMap(), + std::move(value)) + .forwardInto(*this, SILLocation(PBD), initialization.get()); + return; + } + } + emitExprInto(Init, initialization.get(), SILLocation(PBD)); } else { initialization->finishUninitialized(*this); @@ -1178,6 +1195,13 @@ void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD, } void SILGenFunction::visitPatternBindingDecl(PatternBindingDecl *PBD) { + // Visit (local) property wrapper backing var first + auto *singleVar = PBD->getSingleVar(); + if (singleVar && singleVar->hasAttachedPropertyWrapper() && + singleVar->getDeclContext()->isLocalContext()) { + auto *backingVar = singleVar->getPropertyWrapperBackingProperty(); + visitPatternBindingDecl(backingVar->getParentPatternBinding()); + } // Allocate the variables and build up an Initialization over their // allocated storage. @@ -1189,6 +1213,18 @@ void SILGenFunction::visitPatternBindingDecl(PatternBindingDecl *PBD) { void SILGenFunction::visitVarDecl(VarDecl *D) { // We handle emitting the variable storage when we see the pattern binding. + // Visit property wrapper synthesized accessors first. + if (D->hasAttachedPropertyWrapper() && D->getDeclContext()->isLocalContext()) { + auto wrapperInfo = D->getPropertyWrapperBackingPropertyInfo(); + if (!wrapperInfo) + return; + + SGM.emitPropertyWrapperBackingInitializer(D); + visit(wrapperInfo.backingVar); + if (wrapperInfo.projectionVar) + visit(wrapperInfo.projectionVar); + } + // Emit the variable's accessors. D->visitEmittedAccessors([&](AccessorDecl *accessor) { SGM.emitFunction(accessor); diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 522f89f02f47b..eee4ebcc21a2a 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1403,7 +1403,7 @@ namespace { assert(getAccessorDecl()->isSetter()); SILDeclRef setter = Accessor; - if (IsOnSelfParameter && canRewriteSetAsPropertyWrapperInit(SGF) && + if (canRewriteSetAsPropertyWrapperInit(SGF) && !Storage->isStatic() && isBackingVarVisible(cast(Storage), SGF.FunctionDC)) { @@ -1434,7 +1434,9 @@ namespace { // Get the address of the storage property. ManagedValue proj; - if (BaseFormalType->mayHaveSuperclass()) { + if (!BaseFormalType) { + proj = SGF.maybeEmitValueOfLocalVarDecl(backingVar); + } else if (BaseFormalType->mayHaveSuperclass()) { RefElementComponent REC(backingVar, LValueOptions(), varStorageType, typeData); proj = std::move(REC).project(SGF, loc, base); @@ -1471,33 +1473,51 @@ namespace { .SILFnType) .getValue(); - } else + } else { setterFRef = SGF.emitGlobalFunctionRef(loc, setter, setterInfo); + } + CanSILFunctionType setterTy = setterFRef->getType().castTo(); SILFunctionConventions setterConv(setterTy, SGF.SGM.M); - SILValue capturedBase; - unsigned argIdx = setterConv.getNumSILArguments() - 1; - if (setterConv.getSILArgumentConvention(argIdx).isInoutConvention()) { - capturedBase = base.getValue(); - } else { - capturedBase = base.copy(SGF, loc).forward(SGF); - } + // Emit captures for the setter + SmallVector capturedArgs; + auto captureInfo = SGF.SGM.Types.getLoweredLocalCaptures(setter); + if (!captureInfo.getCaptures().empty()) { + SmallVector captures; + SGF.emitCaptures(loc, setter, CaptureEmission::PartialApplication, captures); - // If the base is a reference and the setter expects a value, emit a - // load. This pattern is emitted for property wrappers with a - // nonmutating setter, for example. - if (base.getType().isAddress() && - base.getType().getObjectType() == - setterConv.getSILArgumentType(argIdx, - SGF.getTypeExpansionContext())) { - capturedBase = SGF.B.createTrivialLoadOr( - loc, capturedBase, LoadOwnershipQualifier::Take); + for (auto capture : captures) + capturedArgs.push_back(capture.forward(SGF)); + } else { + assert(base); + + SILValue capturedBase; + unsigned argIdx = setterConv.getNumSILArguments() - 1; + + if (setterConv.getSILArgumentConvention(argIdx).isInoutConvention()) { + capturedBase = base.getValue(); + } else { + capturedBase = base.copy(SGF, loc).forward(SGF); + } + + // If the base is a reference and the setter expects a value, emit a + // load. This pattern is emitted for property wrappers with a + // nonmutating setter, for example. + if (base.getType().isAddress() && + base.getType().getObjectType() == + setterConv.getSILArgumentType(argIdx, + SGF.getTypeExpansionContext())) { + capturedBase = SGF.B.createTrivialLoadOr( + loc, capturedBase, LoadOwnershipQualifier::Take); + } + + capturedArgs.push_back(capturedBase); } PartialApplyInst *setterPAI = SGF.B.createPartialApply(loc, setterFRef, - Substitutions, { capturedBase }, + Substitutions, capturedArgs, ParameterConvention::Direct_Guaranteed); ManagedValue setterFn = SGF.emitManagedRValueWithCleanup(setterPAI); From 0842b4212773b36778d773331155df6be20d2c21 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 28 Sep 2020 16:33:50 -0700 Subject: [PATCH 095/745] [SILGen] Only use assign_by_wrapper for wrapped instance properties inside an initializer, and for wrapped local variables. --- lib/SILGen/SILGenLValue.cpp | 5 +++ test/SILGen/objc_properties.swift | 19 ++++++++--- test/SILGen/property_wrappers.swift | 3 +- test/SILGen/resilient_assign_by_wrapper.swift | 34 +++++++++++++------ 4 files changed, 45 insertions(+), 16 deletions(-) diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index eee4ebcc21a2a..65c7aadf56668 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1314,6 +1314,11 @@ namespace { IsOnSelfParameter && isa(SGF.FunctionDC->getAsDecl()); + // Assignment to a wrapped property can only be re-written to initialization for + // members of `self` in an initializer, and for local variables. + if (!(isAssignmentToSelfParamInInit || VD->getDeclContext()->isLocalContext())) + return false; + // If we have a nonmutating setter on a value type, the call // captures all of 'self' and we cannot rewrite an assignment // into an initialization. diff --git a/test/SILGen/objc_properties.swift b/test/SILGen/objc_properties.swift index 3b0cfcbe7dfaf..90098ad5d8fd8 100644 --- a/test/SILGen/objc_properties.swift +++ b/test/SILGen/objc_properties.swift @@ -285,19 +285,28 @@ public struct SomeWrapper { class SomeWrapperTests { @objc @SomeWrapper dynamic var someWrapper: Int = 0 -// CHECK-LABEL: sil hidden [ossa] @$s15objc_properties16SomeWrapperTestsC14testAssignmentyyF + @W @objc dynamic var s: String? = nil + +// CHECK-LABEL: sil hidden [ossa] @$s15objc_properties16SomeWrapperTestsCyACSScfc : $@convention(method) (@owned String, @owned SomeWrapperTests) -> @owned SomeWrapperTests { // CHECK: [[M:%.*]] = function_ref @$s15objc_properties16SomeWrapperTestsC04someD0SivsTD // CHECK: [[C:%.*]] = partial_apply [callee_guaranteed] [[M]]({{.*}}) // CHECK: assign_by_wrapper {{%.*}}: $Int to {{%.*}} : $*SomeWrapper, init {{.*}} : $@callee_guaranteed (Int) -> SomeWrapper, set [[C]] : $@callee_guaranteed (Int) -> () +// CHECK: [[M:%.*]] = function_ref @$s15objc_properties16SomeWrapperTestsC1sSSSgvsTD +// CHECK: [[C:%.*]] = partial_apply [callee_guaranteed] [[M]]( +// CHECK: assign_by_wrapper {{.*}} : $Optional to {{.*}} : $*W>, init {{.*}} : $@callee_guaranteed (@owned Optional) -> @owned W>, set [[C]] : $@callee_guaranteed (@owned Optional) -> () + init(_ s: String) { + someWrapper = 1000 + self.s = s + } + +// CHECK-LABEL: sil hidden [ossa] @$s15objc_properties16SomeWrapperTestsC14testAssignmentyyF +// CHECK: objc_method %0 : $SomeWrapperTests, #SomeWrapperTests.someWrapper!setter.foreign : (SomeWrapperTests) -> (Int) -> (), $@convention(objc_method) (Int, SomeWrapperTests) -> () func testAssignment() { someWrapper = 1000 } - @W @objc dynamic var s: String? = nil // CHECK-LABEL: sil hidden [ossa] @$s15objc_properties16SomeWrapperTestsC16testBridgedValueyySSF -// CHECK: [[M:%.*]] = function_ref @$s15objc_properties16SomeWrapperTestsC1sSSSgvsTD -// CHECK: [[C:%.*]] = partial_apply [callee_guaranteed] [[M]]( -// CHECK: assign_by_wrapper {{.*}} : $Optional to {{.*}} : $*W>, init {{.*}} : $@callee_guaranteed (@owned Optional) -> @owned W>, set [[C]] : $@callee_guaranteed (@owned Optional) -> () +// CHECK: objc_method %1 : $SomeWrapperTests, #SomeWrapperTests.s!setter.foreign : (SomeWrapperTests) -> (String?) -> (), $@convention(objc_method) (Optional, SomeWrapperTests) -> () // Let's not crash. func testBridgedValue(_ s: String) { self.s = s diff --git a/test/SILGen/property_wrappers.swift b/test/SILGen/property_wrappers.swift index f45cc897e9d92..0454de60a815c 100644 --- a/test/SILGen/property_wrappers.swift +++ b/test/SILGen/property_wrappers.swift @@ -680,7 +680,8 @@ struct WithTuples { class TestResilientDI { @MyPublished var data: Int? = nil - // CHECK: assign_by_wrapper {{%.*}} : $Optional to {{%.*}} : $*MyPublished>, init {{%.*}} : $@callee_guaranteed (Optional) -> @out MyPublished>, set {{%.*}} : $@callee_guaranteed (Optional) -> () + // CHECK-LABEL: sil hidden [ossa] @$s17property_wrappers15TestResilientDIC11doSomethingyyF : $@convention(method) (@guaranteed TestResilientDI) -> () { + // CHECK: class_method %0 : $TestResilientDI, #TestResilientDI.data!setter : (TestResilientDI) -> (Int?) -> (), $@convention(method) (Optional, @guaranteed TestResilientDI) -> () func doSomething() { self.data = Int() diff --git a/test/SILGen/resilient_assign_by_wrapper.swift b/test/SILGen/resilient_assign_by_wrapper.swift index c8eb546bbf51e..22f7ead149fba 100644 --- a/test/SILGen/resilient_assign_by_wrapper.swift +++ b/test/SILGen/resilient_assign_by_wrapper.swift @@ -24,13 +24,27 @@ public enum AddressOnlyEnum { public class AddressOnlySetter { @WrapGod var value: AddressOnlyEnum = .value(nil) + init() { + // CHECK-LABEL: sil hidden [ossa] @$s27resilient_assign_by_wrapper17AddressOnlySetterCACycfc + // CHECK: [[E1:%.*]] = alloc_stack $AddressOnlyEnum + // CHECK: [[W:%.*]] = alloc_stack $WrapGod + // CHECK: [[I:%.*]] = function_ref @$s27resilient_assign_by_wrapper17AddressOnlySetterC5valueAA0eF4EnumOvpfP : $@convention(thin) (@in AddressOnlyEnum) -> @out WrapGod + // CHECK: apply [[I]]([[W]], [[E1]]) + + // CHECK: [[E2:%.*]] = alloc_stack $AddressOnlyEnum + // CHECK-NEXT: inject_enum_addr [[E2]] : $*AddressOnlyEnum, #AddressOnlyEnum.some!enumelt + // CHECK: [[S:%.*]] = partial_apply [callee_guaranteed] {{%.*}}({{%.*}}) : $@convention(method) (@in AddressOnlyEnum, @guaranteed AddressOnlySetter) -> () + // CHECK: assign_by_wrapper [[E2]] : $*AddressOnlyEnum + // CHECK-SAME: set [[S]] : $@callee_guaranteed (@in AddressOnlyEnum) -> () + self.value = .some + } + func testAssignment() { // CHECK-LABEL: sil hidden [ossa] @$s27resilient_assign_by_wrapper17AddressOnlySetterC14testAssignmentyyF // CHECK: [[E:%.*]] = alloc_stack $AddressOnlyEnum // CHECK: inject_enum_addr [[E]] : $*AddressOnlyEnum, #AddressOnlyEnum.some!enumelt - // CHECK: [[S:%.*]] = partial_apply [callee_guaranteed] {{%.*}}({{%.*}}) : $@convention(method) (@in AddressOnlyEnum, @guaranteed AddressOnlySetter) -> () - // CHECK: assign_by_wrapper [[E]] : $*AddressOnlyEnum - // CHECK-SAME: set [[S]] : $@callee_guaranteed (@in AddressOnlyEnum) -> () + // CHECK: [[S:%.*]] = class_method %0 : $AddressOnlySetter, #AddressOnlySetter.value!setter : (AddressOnlySetter) -> (AddressOnlyEnum) -> (), $@convention(method) (@in AddressOnlyEnum, @guaranteed AddressOnlySetter) -> () + // CHECK: apply [[S]]([[E]], %0) : $@convention(method) (@in AddressOnlyEnum, @guaranteed AddressOnlySetter) -> () self.value = .some } } @@ -40,8 +54,8 @@ public struct SubstitutedSetter { } extension SubstitutedSetter where T == Bool { - mutating func testAssignment() { - // CHECK-LABEL: sil hidden [ossa] @$s27resilient_assign_by_wrapper17SubstitutedSetterVAASbRszlE14testAssignmentyyF + init() { + // CHECK-LABEL: hidden [ossa] @$s27resilient_assign_by_wrapper17SubstitutedSetterVAASbRszlEACySbGycfC // CHECK: [[W:%.*]] = struct_element_addr {{%.*}} : $*SubstitutedSetter, #SubstitutedSetter._value // CHECK: [[B:%.*]] = alloc_stack $Bool // CHECK: assign_by_wrapper [[B]] : $*Bool to [[W]] : $*WrapGod @@ -56,9 +70,9 @@ public struct ReabstractedSetter { } extension ReabstractedSetter where T == Int { - mutating func testAssignment() { - // CHECK-LABEL: sil hidden [ossa] @$s27resilient_assign_by_wrapper18ReabstractedSetterVAASiRszlE14testAssignmentyyF - // CHECK: [[F:%.*]] = function_ref @$s27resilient_assign_by_wrapper18ReabstractedSetterVAASiRszlE14testAssignmentyyFySicfU_ : $@convention(thin) (Int) -> () + init() { + // CHECK-LABEL: hidden [ossa] @$s27resilient_assign_by_wrapper18ReabstractedSetterVAASiRszlEACySiGycfC + // CHECK: [[F:%.*]] = function_ref @$s27resilient_assign_by_wrapper18ReabstractedSetterVAASiRszlEACySiGycfcySicfU_ : $@convention(thin) (Int) -> () // CHECK: [[TH_F:%.*]] = thin_to_thick_function [[F]] : $@convention(thin) (Int) -> () to $@callee_guaranteed (Int) -> () // CHECK: [[THUNK_REF:%.*]] = function_ref @$sSiIegy_SiIegn_TR : $@convention(thin) (@in_guaranteed Int, @guaranteed @callee_guaranteed (Int) -> ()) -> () // CHECK: [[CF:%.*]] = partial_apply [callee_guaranteed] [[THUNK_REF]]([[TH_F]]) : $@convention(thin) (@in_guaranteed Int, @guaranteed @callee_guaranteed (Int) -> ()) -> () @@ -76,8 +90,8 @@ public struct ObjectifiedSetter { public class SomeObject {} extension ObjectifiedSetter where T == SomeObject { - mutating func testAssignment() { - // CHECK-LABEL: sil hidden [ossa] @$s27resilient_assign_by_wrapper17ObjectifiedSetterVA2A10SomeObjectCRszrlE14testAssignmentyyF : $@convention(method) (@inout ObjectifiedSetter) -> () { + init() { + // CHECK-LABEL: sil hidden [ossa] @$s27resilient_assign_by_wrapper17ObjectifiedSetterV5valuexvs : $@convention(method) (@owned T, @inout ObjectifiedSetter) -> () { // CHECK: [[OBJ:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick SomeObject.Type) -> @owned SomeObject // CHECK: [[STORAGE:%.*]] = struct_element_addr {{%.*}} : $*ObjectifiedSetter, #ObjectifiedSetter._value // CHECK: assign_by_wrapper [[OBJ]] : $SomeObject to [[STORAGE]] : $*WrapGod From ab3c5dee3ea2bea8e29d1e70ed8b9e7a2aeff1be Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 28 Sep 2020 16:59:21 -0700 Subject: [PATCH 096/745] [Concurrency] Introduce Actor protocol to which actor classes all conform. Introduce a new Actor protocol, which is a class-bound protocol with only one requirement: func enqueue(partialTask: PartialAsyncTask) All actor classes implicitly conform to this protocol, and will synthesize a (currently empty) definition of `enqueue(partialTask:)` unless a suitable one is provided explicitly. --- include/swift/AST/Decl.h | 1 + include/swift/AST/DiagnosticsSema.def | 7 ++ include/swift/AST/KnownIdentifiers.def | 4 + include/swift/AST/KnownProtocols.def | 1 + lib/AST/ASTContext.cpp | 3 + lib/AST/Decl.cpp | 5 +- lib/AST/ProtocolConformance.cpp | 6 ++ lib/IRGen/GenMeta.cpp | 1 + lib/Sema/CMakeLists.txt | 1 + lib/Sema/DerivedConformanceActor.cpp | 104 +++++++++++++++++++++++ lib/Sema/DerivedConformances.cpp | 10 +++ lib/Sema/DerivedConformances.h | 26 +++++- lib/Sema/TypeCheckConcurrency.cpp | 9 ++ lib/Sema/TypeCheckConcurrency.h | 5 ++ lib/Sema/TypeCheckProtocol.cpp | 15 +++- stdlib/public/Concurrency/Actor.swift | 25 ++++++ stdlib/public/Concurrency/CMakeLists.txt | 1 + test/decl/protocol/special/Actor.swift | 61 +++++++++++++ 18 files changed, 282 insertions(+), 3 deletions(-) create mode 100644 lib/Sema/DerivedConformanceActor.cpp create mode 100644 stdlib/public/Concurrency/Actor.swift create mode 100644 test/decl/protocol/special/Actor.swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 588ef350f497d..b615c7348e393 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -4175,6 +4175,7 @@ enum class KnownDerivableProtocolKind : uint8_t { Decodable, AdditiveArithmetic, Differentiable, + Actor, }; /// ProtocolDecl - A declaration of a protocol, for example: diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index e27bdb0847cd8..99225191ee057 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4195,6 +4195,13 @@ ERROR(actorisolated_not_actor_instance_member,none, "'@actorIsolated' can only be applied to instance members of actors", ()) +ERROR(partial_task_type_missing,none, + "missing 'PartialAsyncTask' type, probably because the '_Concurrency' " + "module was not imported", ()) +ERROR(enqueue_partial_task_not_in_context,none, + "'enqueue(partialTask:)' can only be implemented in the definition of " + "actor class %0", (Type)) + //------------------------------------------------------------------------------ // MARK: Type Check Types //------------------------------------------------------------------------------ diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 37898169a6707..f992f6b51dec0 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -44,6 +44,7 @@ IDENTIFIER(Change) IDENTIFIER_WITH_NAME(code_, "_code") IDENTIFIER(CodingKeys) IDENTIFIER(combine) +IDENTIFIER_(Concurrency) IDENTIFIER(container) IDENTIFIER(CoreGraphics) IDENTIFIER(CoreMedia) @@ -67,6 +68,7 @@ IDENTIFIER(encode) IDENTIFIER(encodeIfPresent) IDENTIFIER(Encoder) IDENTIFIER(encoder) +IDENTIFIER(enqueue) IDENTIFIER(erasing) IDENTIFIER(error) IDENTIFIER(errorDomain) @@ -105,6 +107,8 @@ IDENTIFIER(oldValue) IDENTIFIER(Optional) IDENTIFIER_(OptionalNilComparisonType) IDENTIFIER(parameter) +IDENTIFIER(partialTask) +IDENTIFIER(PartialAsyncTask) IDENTIFIER(projected) IDENTIFIER(projectedValue) IDENTIFIER(Protocol) diff --git a/include/swift/AST/KnownProtocols.def b/include/swift/AST/KnownProtocols.def index e530e38c8bee6..1d4a8484b726c 100644 --- a/include/swift/AST/KnownProtocols.def +++ b/include/swift/AST/KnownProtocols.def @@ -58,6 +58,7 @@ #define BUILTIN_EXPRESSIBLE_BY_LITERAL_PROTOCOL_(name) \ BUILTIN_EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME(name, "_" #name) +PROTOCOL(Actor) PROTOCOL(Sequence) PROTOCOL(IteratorProtocol) PROTOCOL(RawRepresentable) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 4b8be5ff67243..f09ac7b231b5c 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -947,6 +947,9 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { case KnownProtocolKind::Differentiable: M = getLoadedModule(Id_Differentiation); break; + case KnownProtocolKind::Actor: + M = getLoadedModule(Id_Concurrency); + break; default: M = getStdlibModule(); break; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a330b63faedcb..99b035da4e37a 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5047,7 +5047,8 @@ void ProtocolDecl::computeKnownProtocolKind() const { auto module = getModuleContext(); if (module != module->getASTContext().getStdlibModule() && !module->getName().is("Foundation") && - !module->getName().is("_Differentiation")) { + !module->getName().is("_Differentiation") && + !module->getName().is("_Concurrency")) { const_cast(this)->Bits.ProtocolDecl.KnownProtocol = 1; return; } @@ -5093,6 +5094,8 @@ Optional return KnownDerivableProtocolKind::AdditiveArithmetic; case KnownProtocolKind::Differentiable: return KnownDerivableProtocolKind::Differentiable; + case KnownProtocolKind::Actor: + return KnownDerivableProtocolKind::Actor; default: return None; } } diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 5b2ff91752ca6..62b4791e29c88 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -1256,6 +1256,12 @@ void NominalTypeDecl::prepareConformanceTable() const { addSynthesized(KnownProtocolKind::RawRepresentable); } } + + // Actor classes conform to the actor protocol. + if (auto classDecl = dyn_cast(mutableThis)) { + if (classDecl->isActor()) + addSynthesized(KnownProtocolKind::Actor); + } } bool NominalTypeDecl::lookupConformance( diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index d18859efc34f0..7dfa9a3bd3094 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -5045,6 +5045,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) { case KnownProtocolKind::AdditiveArithmetic: case KnownProtocolKind::Differentiable: case KnownProtocolKind::FloatingPoint: + case KnownProtocolKind::Actor: return SpecialProtocol::None; } diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index d35c849fb435f..6d83a9b33d58b 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -17,6 +17,7 @@ add_swift_host_library(swiftSema STATIC ConstraintLocator.cpp ConstraintSystem.cpp DebuggerTestingTransform.cpp + DerivedConformanceActor.cpp DerivedConformanceAdditiveArithmetic.cpp DerivedConformanceCaseIterable.cpp DerivedConformanceCodable.cpp diff --git a/lib/Sema/DerivedConformanceActor.cpp b/lib/Sema/DerivedConformanceActor.cpp new file mode 100644 index 0000000000000..dc9328867505e --- /dev/null +++ b/lib/Sema/DerivedConformanceActor.cpp @@ -0,0 +1,104 @@ +//===--- DerivedConformanceEquatableHashable.cpp - Derived Equatable & co -===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements implicit derivation of the Actor protocol. +// +//===----------------------------------------------------------------------===// +#include "DerivedConformances.h" +#include "TypeChecker.h" +#include "swift/AST/ParameterList.h" + +using namespace swift; + +bool DerivedConformance::canDeriveActor( + NominalTypeDecl *nominal, DeclContext *dc) { + auto classDecl = dyn_cast(nominal); + return classDecl && classDecl->isActor() && dc == nominal; +} + +static DeclName getEnqueuePartialTaskName(ASTContext &ctx) { + return DeclName(ctx, ctx.Id_enqueue, { ctx.Id_partialTask }); +} + +static Type getPartialAsyncTaskType(ASTContext &ctx) { + auto concurrencyModule = ctx.getLoadedModule(ctx.Id_Concurrency); + if (!concurrencyModule) + return Type(); + + SmallVector decls; + concurrencyModule->lookupQualified( + concurrencyModule, DeclNameRef(ctx.Id_PartialAsyncTask), + NL_QualifiedDefault, decls); + for (auto decl : decls) { + if (auto typeDecl = dyn_cast(decl)) + return typeDecl->getDeclaredInterfaceType(); + } + + return Type(); +} + +static std::pair +deriveBodyActor_enqueuePartialTask( + AbstractFunctionDecl *enqueuePartialTask, void *) { + ASTContext &ctx = enqueuePartialTask->getASTContext(); + + // FIXME: Call into runtime API to enqueue the task, once we figure out + // what that runtime API should look like. + + auto body = BraceStmt::create( + ctx, SourceLoc(), { }, SourceLoc(), /*implicit=*/true); + return { body, /*isTypeChecked=*/true }; +} + +/// Derive the declaration of Actor's enqueue(partialTask:). +static ValueDecl *deriveActor_enqueuePartialTask(DerivedConformance &derived) { + ASTContext &ctx = derived.Context; + + Type partialTaskType = getPartialAsyncTaskType(ctx); + if (!partialTaskType) { + derived.Nominal->diagnose(diag::partial_task_type_missing); + return nullptr; + } + + auto parentDC = derived.getConformanceContext(); + auto partialTaskParamDecl = new (ctx) ParamDecl( + SourceLoc(), SourceLoc(), ctx.Id_partialTask, + SourceLoc(), ctx.Id_partialTask, parentDC); + partialTaskParamDecl->setInterfaceType(partialTaskType); + partialTaskParamDecl->setSpecifier(ParamSpecifier::Default); + + ParameterList *params = ParameterList::createWithoutLoc(partialTaskParamDecl); + auto func = FuncDecl::createImplicit( + ctx, StaticSpellingKind::None, getEnqueuePartialTaskName(ctx), + SourceLoc(), /*Async=*/false, /*Throws=*/false, /*GenericParams=*/nullptr, + params, TupleType::getEmpty(ctx), parentDC); + func->copyFormalAccessFrom(derived.Nominal); + func->setBodySynthesizer(deriveBodyActor_enqueuePartialTask); + + // FIXME: This function should be "actor-unsafe", not "actor-independent", but + // the latter is all we have at the moment. + func->getAttrs().add(new (ctx) ActorIndependentAttr(/*IsImplicit=*/true)); + + derived.addMembersToConformanceContext({func}); + return func; +} + +ValueDecl *DerivedConformance::deriveActor(ValueDecl *requirement) { + auto func = dyn_cast(requirement); + if (!func) + return nullptr; + + if (func->getName() == getEnqueuePartialTaskName(Context)) + return deriveActor_enqueuePartialTask(*this); + + return nullptr; +} diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index b22a351eaf6be..f82cf58c39acb 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "TypeChecker.h" +#include "TypeCheckConcurrency.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/Decl.h" #include "swift/AST/Stmt.h" @@ -74,6 +75,10 @@ bool DerivedConformance::derivesProtocolConformance(DeclContext *DC, return canDeriveHashable(Nominal); } + if (*derivableKind == KnownDerivableProtocolKind::Actor) { + return canDeriveActor(Nominal, DC); + } + if (*derivableKind == KnownDerivableProtocolKind::AdditiveArithmetic) return canDeriveAdditiveArithmetic(Nominal, DC); @@ -359,6 +364,11 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, return getRequirement(KnownProtocolKind::Hashable); } + // Actor.enqueue(partialTask: PartialTask) + if (isEnqueuePartialTask(ctx, name)) { + return getRequirement(KnownProtocolKind::Actor); + } + return nullptr; } diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index c5dba4bb20172..7deee41999f2f 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -18,15 +18,31 @@ #ifndef SWIFT_SEMA_DERIVEDCONFORMANCES_H #define SWIFT_SEMA_DERIVEDCONFORMANCES_H +#include "swift/Basic/LLVM.h" #include namespace swift { +class AbstractFunctionDecl; +class AccessorDecl; +class AssociatedTypeDecl; +class ASTContext; +struct ASTNode; class Decl; +class DeclContext; class DeclRefExpr; -class AccessorDecl; +class EnumDecl; +class EnumElementDecl; +class Expr; +class GuardStmt; +class Identifier; class NominalTypeDecl; +class ParamDecl; +class Pattern; class PatternBindingDecl; +class ProtocolDecl; +class StructDecl; class Type; +class TypeDecl; class ValueDecl; class VarDecl; @@ -277,6 +293,14 @@ class DerivedConformance { /// \returns the derived member, which will also be added to the type. ValueDecl *deriveDecodable(ValueDecl *requirement); + /// Whether we can derive the given Actor requirement in the given context. + static bool canDeriveActor(NominalTypeDecl *nominal, DeclContext *dc); + + /// Derive an Actor requirement for an actor class. + /// + /// \returns the derived member, which will also be added to the type. + ValueDecl *deriveActor(ValueDecl *requirement); + /// Declare a read-only property. std::pair declareDerivedProperty(Identifier name, Type propertyInterfaceType, diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index b5c19ab93c570..597354a2c3d00 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -749,3 +749,12 @@ ActorIsolation swift::getActorIsolation(ValueDecl *value) { ctx.evaluator, ActorIsolationRequest{value}, ActorIsolation::forUnspecified()); } + +bool swift::isEnqueuePartialTask(ASTContext &ctx, DeclName name) { + if (name.isCompoundName() && name.getBaseName() == ctx.Id_enqueue) { + auto argumentNames = name.getArgumentNames(); + return argumentNames.size() == 1 && argumentNames[0] == ctx.Id_partialTask; + } + + return false; +} diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index ea0f92a7ff3a6..b1ecd19fec9ed 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -20,8 +20,10 @@ namespace swift { class ActorIsolation; +class ASTContext; class ClassDecl; class DeclContext; +class DeclName; class Expr; class FuncDecl; class ValueDecl; @@ -36,6 +38,9 @@ void checkActorIsolation(const Expr *expr, const DeclContext *dc); /// Determine how the given value declaration is isolated. ActorIsolation getActorIsolation(ValueDecl *value); +/// Whether the given declaration name is enqueue(partialTask:). +bool isEnqueuePartialTask(ASTContext &ctx, DeclName name); + } // end namespace swift #endif /* SWIFT_SEMA_TYPECHECKCONCURRENCY_H */ diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 08df65e1a80f6..0b37eeb01549e 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4216,8 +4216,18 @@ void ConformanceChecker::resolveValueWitnesses() { auto witness = Conformance->getWitnessUncached(requirement).getDecl(); if (!witness) return; - // Objective-C checking for @objc requirements. auto &C = witness->getASTContext(); + + // Ensure that Actor.enqueue(partialTask:) is implemented within the + // class itself. + if (isEnqueuePartialTask(C, requirement->getName()) && + Proto->isSpecificProtocol(KnownProtocolKind::Actor) && + DC != witness->getDeclContext()) { + witness->diagnose(diag::enqueue_partial_task_not_in_context, Adoptee); + return; + } + + // Objective-C checking for @objc requirements. if (requirement->isObjC() && requirement->getName() == witness->getName() && !requirement->getAttrs().isUnavailable(getASTContext())) { @@ -5804,6 +5814,9 @@ ValueDecl *TypeChecker::deriveProtocolRequirement(DeclContext *DC, case KnownDerivableProtocolKind::Differentiable: return derived.deriveDifferentiable(Requirement); + case KnownDerivableProtocolKind::Actor: + return derived.deriveActor(Requirement); + case KnownDerivableProtocolKind::OptionSet: llvm_unreachable( "When possible, OptionSet is derived via memberwise init synthesis"); diff --git a/stdlib/public/Concurrency/Actor.swift b/stdlib/public/Concurrency/Actor.swift new file mode 100644 index 0000000000000..8f62d14b02dd7 --- /dev/null +++ b/stdlib/public/Concurrency/Actor.swift @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Swift +@_implementationOnly import _SwiftConcurrencyShims + +/// Common protocol to which all actor classes conform. +/// +/// The \c Actor protocol provides the core functionality of an actor class, +/// which involves enqueuing new partial tasks to be executed at some +/// point. Actor classes implicitly conform to this protocol as part of their +/// primary class definition. +public protocol Actor: AnyObject { + /// Enqueue a new partial task that will be executed in the actor's context. + func enqueue(partialTask: PartialAsyncTask) +} diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index fa491ec431295..e63b3e475255b 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -11,6 +11,7 @@ #===----------------------------------------------------------------------===# add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB + Actor.swift PartialAsyncTask.swift SWIFT_MODULE_DEPENDS_OSX Darwin diff --git a/test/decl/protocol/special/Actor.swift b/test/decl/protocol/special/Actor.swift new file mode 100644 index 0000000000000..5bf3c4fd9c6d3 --- /dev/null +++ b/test/decl/protocol/special/Actor.swift @@ -0,0 +1,61 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency + +// Synthesis of for actor classes. +import _Concurrency + +actor class A1 { + var x: Int = 17 +} + +actor class A2: Actor { + var x: Int = 17 +} + +actor class A3: Actor { + var x: Int = 17 +} + +actor class A4: A1 { +} + +actor class A5: A2 { +} + +actor class A6: A1, Actor { // expected-error{{redundant conformance of 'A6' to protocol 'Actor'}} + // expected-note@-1{{'A6' inherits conformance to protocol 'Actor' from superclass here}} +} + +actor class A7 { + // Okay: satisfy the requirement explicitly + @actorIndependent func enqueue(partialTask: PartialAsyncTask) { } +} + +// Bad actors, that incorrectly try to satisfy the various requirements. + +// Method that is not usable as a witness. +actor class BA1 { + func enqueue(partialTask: PartialAsyncTask) { } // expected-error{{invalid redeclaration of synthesized implementation for protocol requirement 'enqueue(partialTask:)'}} +} + +// Method that isn't part of the main class definition cannot be used to +// satisfy the requirement, because then it would not have a vtable slot. +actor class BA2 { } + +extension BA2 { + // expected-error@+1{{'enqueue(partialTask:)' can only be implemented in the definition of actor class 'BA2'}} + @actorIndependent func enqueue(partialTask: PartialAsyncTask) { } +} + + +// Make sure the conformances actually happen. +func acceptActor(_: T.Type) { } + +func testConformance() { + acceptActor(A1.self) + acceptActor(A2.self) + acceptActor(A3.self) + acceptActor(A4.self) + acceptActor(A5.self) + acceptActor(A6.self) + acceptActor(A7.self) +} From cdba663d805773653a3da129368e5ef995d4f72c Mon Sep 17 00:00:00 2001 From: Valeriy Van Date: Mon, 24 Aug 2020 17:14:24 +0200 Subject: [PATCH 097/745] Fixes example snippets in Array.swift considering endianness --- stdlib/public/core/Array.swift | 10 +++++++--- stdlib/public/core/ArraySlice.swift | 10 +++++++--- stdlib/public/core/ContiguousArray.swift | 10 +++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index fe5f0da2198d7..fafc36b0fd4e4 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -1788,7 +1788,7 @@ extension Array { /// and enums. /// /// The following example copies bytes from the `byteValues` array into - /// `numbers`, an array of `Int`: + /// `numbers`, an array of `Int32`: /// /// var numbers: [Int32] = [0, 0] /// var byteValues: [UInt8] = [0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00] @@ -1800,6 +1800,8 @@ extension Array { /// } /// // numbers == [1, 2] /// + /// - Note: This example shows the behavior on a little-endian platform. + /// /// The pointer passed as an argument to `body` is valid only for the /// lifetime of the closure. Do not escape it from the closure for later /// use. @@ -1837,12 +1839,14 @@ extension Array { /// The following example copies the bytes of the `numbers` array into a /// buffer of `UInt8`: /// - /// var numbers = [1, 2, 3] + /// var numbers: [Int32] = [1, 2, 3] /// var byteBuffer: [UInt8] = [] /// numbers.withUnsafeBytes { /// byteBuffer.append(contentsOf: $0) /// } - /// // byteBuffer == [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, ...] + /// // byteBuffer == [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0] + /// + /// - Note: This example shows the behavior on a little-endian platform. /// /// - Parameter body: A closure with an `UnsafeRawBufferPointer` parameter /// that points to the contiguous storage for the array. diff --git a/stdlib/public/core/ArraySlice.swift b/stdlib/public/core/ArraySlice.swift index 8ce49b2f84dce..eeda582f57e84 100644 --- a/stdlib/public/core/ArraySlice.swift +++ b/stdlib/public/core/ArraySlice.swift @@ -1429,7 +1429,7 @@ extension ArraySlice { /// and enums. /// /// The following example copies bytes from the `byteValues` array into - /// `numbers`, an array of `Int`: + /// `numbers`, an array of `Int32`: /// /// var numbers: [Int32] = [0, 0] /// var byteValues: [UInt8] = [0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00] @@ -1441,6 +1441,8 @@ extension ArraySlice { /// } /// // numbers == [1, 2] /// + /// - Note: This example shows the behavior on a little-endian platform. + /// /// The pointer passed as an argument to `body` is valid only for the /// lifetime of the closure. Do not escape it from the closure for later /// use. @@ -1478,12 +1480,14 @@ extension ArraySlice { /// The following example copies the bytes of the `numbers` array into a /// buffer of `UInt8`: /// - /// var numbers = [1, 2, 3] + /// var numbers: [Int32] = [1, 2, 3] /// var byteBuffer: [UInt8] = [] /// numbers.withUnsafeBytes { /// byteBuffer.append(contentsOf: $0) /// } - /// // byteBuffer == [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, ...] + /// // byteBuffer == [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0] + /// + /// - Note: This example shows the behavior on a little-endian platform. /// /// - Parameter body: A closure with an `UnsafeRawBufferPointer` parameter /// that points to the contiguous storage for the array. diff --git a/stdlib/public/core/ContiguousArray.swift b/stdlib/public/core/ContiguousArray.swift index b854c18942745..ce3612fe52386 100644 --- a/stdlib/public/core/ContiguousArray.swift +++ b/stdlib/public/core/ContiguousArray.swift @@ -1361,7 +1361,7 @@ extension ContiguousArray { /// and enums. /// /// The following example copies bytes from the `byteValues` array into - /// `numbers`, an array of `Int`: + /// `numbers`, an array of `Int32`: /// /// var numbers: [Int32] = [0, 0] /// var byteValues: [UInt8] = [0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00] @@ -1373,6 +1373,8 @@ extension ContiguousArray { /// } /// // numbers == [1, 2] /// + /// - Note: This example shows the behavior on a little-endian platform. + /// /// The pointer passed as an argument to `body` is valid only for the /// lifetime of the closure. Do not escape it from the closure for later /// use. @@ -1410,12 +1412,14 @@ extension ContiguousArray { /// The following example copies the bytes of the `numbers` array into a /// buffer of `UInt8`: /// - /// var numbers = [1, 2, 3] + /// var numbers: [Int32] = [1, 2, 3] /// var byteBuffer: [UInt8] = [] /// numbers.withUnsafeBytes { /// byteBuffer.append(contentsOf: $0) /// } - /// // byteBuffer == [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, ...] + /// // byteBuffer == [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0] + /// + /// - Note: This example shows the behavior on a little-endian platform. /// /// - Parameter body: A closure with an `UnsafeRawBufferPointer` parameter /// that points to the contiguous storage for the array. From 9bd3d0b67fc9599c693735d98c6ff98defccc8e5 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 28 Sep 2020 18:13:16 -0700 Subject: [PATCH 098/745] [Property Wrappers] Make sure captures are computed for synthesized property wrapper accessors. --- lib/Sema/TypeCheckDeclPrimary.cpp | 11 +++++++++++ lib/Sema/TypeCheckStorage.cpp | 6 ++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index ead29eca35c4b..986af5dbbf9fd 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1418,6 +1418,17 @@ class DeclChecker : public DeclVisitor { (void) VD->getPropertyWrapperBackingProperty(); (void) VD->getImplInfo(); + if (VD->hasAttachedPropertyWrapper() && VD->getDeclContext()->isLocalContext()) { + // If this is a local wrapped variable, visit the synthesized + // storage and projection first + auto wrapperInfo = VD->getPropertyWrapperBackingPropertyInfo(); + assert(wrapperInfo); + + visitBoundVariable(wrapperInfo.backingVar); + if (wrapperInfo.projectionVar) + visitBoundVariable(wrapperInfo.projectionVar); + } + // Add the '@_hasStorage' attribute if this property is stored. if (VD->hasStorage() && !VD->getAttrs().hasAttribute()) VD->getAttrs().add(new (getASTContext()) diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 69525ed1881e7..3a4ef5a4c61d2 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -1147,8 +1147,9 @@ synthesizeTrivialGetterBody(AccessorDecl *getter, TargetImpl target, body.push_back(returnStmt); } + // Don't mark local accessors as type-checked - captures still need to be computed. return { BraceStmt::create(ctx, loc, body, loc, true), - /*isTypeChecked=*/true }; + /*isTypeChecked=*/!getter->getDeclContext()->isLocalContext() }; } /// Synthesize the body of a getter which just directly accesses the @@ -1423,8 +1424,9 @@ synthesizeTrivialSetterBodyWithStorage(AccessorDecl *setter, createPropertyStoreOrCallSuperclassSetter(setter, valueDRE, storageToUse, target, setterBody, ctx); + // Don't mark local accessors as type-checked - captures still need to be computed. return { BraceStmt::create(ctx, loc, setterBody, loc, true), - /*isTypeChecked=*/true }; + /*isTypeChecked=*/!setter->getDeclContext()->isLocalContext() }; } static std::pair From d8df6217d32cf7ec970d67bc66ff966df0c1a671 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 28 Sep 2020 18:43:13 -0700 Subject: [PATCH 099/745] [Property Wrappers] Allow property wrappers on local variables. --- include/swift/AST/DiagnosticsSema.def | 2 -- lib/Sema/TypeCheckPropertyWrapper.cpp | 6 ------ test/decl/var/property_wrappers.swift | 29 ++++++++++++++++++++++++--- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 8b4061cee7658..81862759c0942 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5202,8 +5202,6 @@ ERROR(property_wrapper_mutating_get_composed_to_get_only,none, "property wrapper %0 with a mutating getter cannot be composed inside " "get-only property wrapper %1", (TypeLoc, TypeLoc)) -ERROR(property_wrapper_local,none, - "property wrappers are not yet supported on local properties", ()) ERROR(property_wrapper_top_level,none, "property wrappers are not yet supported in top-level code", ()) ERROR(property_wrapper_let, none, diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 0ff4e76afc5c3..14f18c8a4f412 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -414,12 +414,6 @@ AttachedPropertyWrappersRequest::evaluate(Evaluator &evaluator, // Check various restrictions on which properties can have wrappers // attached to them. - // Local properties do not yet support wrappers. - if (var->getDeclContext()->isLocalContext()) { - ctx.Diags.diagnose(attr->getLocation(), diag::property_wrapper_local); - continue; - } - // Nor does top-level code. if (var->getDeclContext()->isModuleScopeContext()) { ctx.Diags.diagnose(attr->getLocation(), diag::property_wrapper_top_level); diff --git a/test/decl/var/property_wrappers.swift b/test/decl/var/property_wrappers.swift index 020951e99c40f..1a3ef8a2fff9c 100644 --- a/test/decl/var/property_wrappers.swift +++ b/test/decl/var/property_wrappers.swift @@ -140,16 +140,39 @@ struct _UppercaseWrapper { } // --------------------------------------------------------------------------- -// Limitations on where property wrappers can be used +// Local property wrappers // --------------------------------------------------------------------------- func testLocalContext() { - @WrapperWithInitialValue // expected-error{{property wrappers are not yet supported on local properties}} + @WrapperWithInitialValue var x = 17 x = 42 - _ = x + let _: Int = x + let _: WrapperWithInitialValue = _x + + @WrapperWithInitialValue(wrappedValue: 17) + var initialValue + let _: Int = initialValue + let _: WrapperWithInitialValue = _initialValue + + @Clamping(min: 0, max: 100) + var percent = 50 + let _: Int = percent + let _: Clamping = _percent + + @WrapperA @WrapperB + var composed = "hello" + let _: WrapperA = _composed + + @WrapperWithStorageRef + var hasProjection = 10 + let _: Wrapper = $hasProjection } +// --------------------------------------------------------------------------- +// Limitations on where property wrappers can be used +// --------------------------------------------------------------------------- + enum SomeEnum { case foo From 21cbdfa337dfa41e108573d5d99c6259bee671c9 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 28 Sep 2020 19:31:50 -0700 Subject: [PATCH 100/745] [Property Wrappers] Add a VarDecl helper method for visiting synthesized property wrapper vars. --- include/swift/AST/Decl.h | 8 ++++++++ lib/AST/Decl.cpp | 11 +++++++++++ lib/SILGen/SILGenDecl.cpp | 27 ++++++++++----------------- lib/Sema/TypeCheckDeclPrimary.cpp | 14 ++++---------- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 280e77580a15c..e1a76cd0c274a 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5202,6 +5202,14 @@ class VarDecl : public AbstractStorageDecl { /// property wrapper with a \c projectedValue . VarDecl *getPropertyWrapperProjectionVar() const; + /// Visit all auxiliary declarations to this VarDecl. + /// + /// An auxiliary declaration is a declaration synthesized by the compiler to support + /// this VarDecl, such as synthesized property wrapper variables. + /// + /// \note this function only visits auxiliary decls that are not part of the AST. + void visitAuxiliaryDecls(llvm::function_ref) const; + /// Retrieve the backing storage property for a lazy property. VarDecl *getLazyStorageProperty() const; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index c1ab74e4d66e6..fa1a254e2e01a 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5956,6 +5956,17 @@ VarDecl *VarDecl::getPropertyWrapperProjectionVar() const { return getPropertyWrapperBackingPropertyInfo().projectionVar; } +void VarDecl::visitAuxiliaryDecls(llvm::function_ref visit) const { + if (!getDeclContext()->isLocalContext()) + return; + + if (auto *backingVar = getPropertyWrapperBackingProperty()) + visit(backingVar); + + if (auto *projectionVar = getPropertyWrapperProjectionVar()) + visit(projectionVar); +} + VarDecl *VarDecl::getLazyStorageProperty() const { auto &ctx = getASTContext(); auto mutableThis = const_cast(this); diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index 87563d3bfe07f..9e53c5babd0fd 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -1195,13 +1195,6 @@ void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD, } void SILGenFunction::visitPatternBindingDecl(PatternBindingDecl *PBD) { - // Visit (local) property wrapper backing var first - auto *singleVar = PBD->getSingleVar(); - if (singleVar && singleVar->hasAttachedPropertyWrapper() && - singleVar->getDeclContext()->isLocalContext()) { - auto *backingVar = singleVar->getPropertyWrapperBackingProperty(); - visitPatternBindingDecl(backingVar->getParentPatternBinding()); - } // Allocate the variables and build up an Initialization over their // allocated storage. @@ -1213,17 +1206,17 @@ void SILGenFunction::visitPatternBindingDecl(PatternBindingDecl *PBD) { void SILGenFunction::visitVarDecl(VarDecl *D) { // We handle emitting the variable storage when we see the pattern binding. - // Visit property wrapper synthesized accessors first. - if (D->hasAttachedPropertyWrapper() && D->getDeclContext()->isLocalContext()) { - auto wrapperInfo = D->getPropertyWrapperBackingPropertyInfo(); - if (!wrapperInfo) - return; - + // Emit the property wrapper backing initializer if necessary. + auto wrapperInfo = D->getPropertyWrapperBackingPropertyInfo(); + if (wrapperInfo && wrapperInfo.initializeFromOriginal) SGM.emitPropertyWrapperBackingInitializer(D); - visit(wrapperInfo.backingVar); - if (wrapperInfo.projectionVar) - visit(wrapperInfo.projectionVar); - } + + D->visitAuxiliaryDecls([&](VarDecl *var) { + if (auto *patternBinding = var->getParentPatternBinding()) + visitPatternBindingDecl(patternBinding); + + visit(var); + }); // Emit the variable's accessors. D->visitEmittedAccessors([&](AccessorDecl *accessor) { diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 986af5dbbf9fd..96cba18efa412 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1418,16 +1418,10 @@ class DeclChecker : public DeclVisitor { (void) VD->getPropertyWrapperBackingProperty(); (void) VD->getImplInfo(); - if (VD->hasAttachedPropertyWrapper() && VD->getDeclContext()->isLocalContext()) { - // If this is a local wrapped variable, visit the synthesized - // storage and projection first - auto wrapperInfo = VD->getPropertyWrapperBackingPropertyInfo(); - assert(wrapperInfo); - - visitBoundVariable(wrapperInfo.backingVar); - if (wrapperInfo.projectionVar) - visitBoundVariable(wrapperInfo.projectionVar); - } + // Visit auxiliary decls first + VD->visitAuxiliaryDecls([&](VarDecl *var) { + this->visitBoundVariable(var); + }); // Add the '@_hasStorage' attribute if this property is stored. if (VD->hasStorage() && !VD->getAttrs().hasAttribute()) From 8f3c912f46dc6a2ee3734f741de50bdebf803ec0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 28 Sep 2020 21:16:59 -0700 Subject: [PATCH 101/745] [Concurrency] Fix new source file header. --- lib/Sema/DerivedConformanceActor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/DerivedConformanceActor.cpp b/lib/Sema/DerivedConformanceActor.cpp index dc9328867505e..7299c18137fca 100644 --- a/lib/Sema/DerivedConformanceActor.cpp +++ b/lib/Sema/DerivedConformanceActor.cpp @@ -1,4 +1,4 @@ -//===--- DerivedConformanceEquatableHashable.cpp - Derived Equatable & co -===// +//===--- DerivedConformanceActor.cpp - Derived Actor Conformance ----------===// // // This source file is part of the Swift.org open source project // From 163d47ec904b26f380c15936547f585dce9d2635 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Mon, 28 Sep 2020 23:08:14 -0700 Subject: [PATCH 102/745] Revert "Revert #33106 and #33205" (#34106) --- include/swift/SIL/SILCloner.h | 7 ++ include/swift/SIL/SILModule.h | 22 +++++ .../SILOptimizer/PassManager/PassManager.h | 5 ++ lib/SIL/IR/SILModule.cpp | 2 + .../Mandatory/OwnershipModelEliminator.cpp | 88 ++++++++++--------- lib/SILOptimizer/PassManager/PassManager.cpp | 20 ++++- lib/SILOptimizer/PassManager/PassPipeline.cpp | 9 +- test/DebugInfo/dbgvalue-insertpt.swift | 12 +-- test/IRGen/big_types_corner_cases.swift | 6 -- test/SILOptimizer/copyforward_ossa.sil | 2 +- test/SILOptimizer/prespecialize.swift | 6 -- 11 files changed, 106 insertions(+), 73 deletions(-) diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 07efa4a4ef15b..08880bac019d0 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1542,6 +1542,13 @@ template void SILCloner::visitUncheckedValueCastInst( UncheckedValueCastInst *Inst) { getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + if (!getBuilder().hasOwnership()) { + recordClonedInstruction(Inst, getBuilder().createUncheckedBitwiseCast( + getOpLocation(Inst->getLoc()), + getOpValue(Inst->getOperand()), + getOpType(Inst->getType()))); + return; + } recordClonedInstruction(Inst, getBuilder().createUncheckedValueCast( getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()), diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index 0610893425ba2..33f7b07ff875f 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -263,6 +263,15 @@ class SILModule { /// to ensure that the module is serialized only once. bool serialized; + /// Set if we have registered a deserialization notification handler for + /// lowering ownership in non transparent functions. + /// This gets set in NonTransparent OwnershipModelEliminator pass. + bool regDeserializationNotificationHandlerForNonTransparentFuncOME; + /// Set if we have registered a deserialization notification handler for + /// lowering ownership in transparent functions. + /// This gets set in OwnershipModelEliminator pass. + bool regDeserializationNotificationHandlerForAllFuncOME; + /// Action to be executed for serializing the SILModule. ActionCallback SerializeSILAction; @@ -301,6 +310,19 @@ class SILModule { deserializationNotificationHandlers.erase(handler); } + bool hasRegisteredDeserializationNotificationHandlerForNonTransparentFuncOME() { + return regDeserializationNotificationHandlerForNonTransparentFuncOME; + } + bool hasRegisteredDeserializationNotificationHandlerForAllFuncOME() { + return regDeserializationNotificationHandlerForAllFuncOME; + } + void setRegisteredDeserializationNotificationHandlerForNonTransparentFuncOME() { + regDeserializationNotificationHandlerForNonTransparentFuncOME = true; + } + void setRegisteredDeserializationNotificationHandlerForAllFuncOME() { + regDeserializationNotificationHandlerForAllFuncOME = true; + } + /// Add a delete notification handler \p Handler to the module context. void registerDeleteNotificationHandler(DeleteNotificationHandler* Handler); diff --git a/include/swift/SILOptimizer/PassManager/PassManager.h b/include/swift/SILOptimizer/PassManager/PassManager.h index b72969c96a01b..8c82ff0169dc0 100644 --- a/include/swift/SILOptimizer/PassManager/PassManager.h +++ b/include/swift/SILOptimizer/PassManager/PassManager.h @@ -281,6 +281,11 @@ class SILPassManager { /// Run the passes in Transform from \p FromTransIdx to \p ToTransIdx. void runFunctionPasses(unsigned FromTransIdx, unsigned ToTransIdx); + /// Helper function to check if the function pass should be run mandatorily + /// All passes in mandatory pass pipeline and ownership model elimination are + /// mandatory function passes. + bool isMandatoryFunctionPass(SILFunctionTransform *); + /// A helper function that returns (based on SIL stage and debug /// options) whether we should continue running passes. bool continueTransforming(); diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index c70e4865c3641..2966cb89e7ba7 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -95,6 +95,8 @@ class SILModule::SerializationCallback final SILModule::SILModule(llvm::PointerUnion context, Lowering::TypeConverter &TC, const SILOptions &Options) : Stage(SILStage::Raw), Options(Options), serialized(false), + regDeserializationNotificationHandlerForNonTransparentFuncOME(false), + regDeserializationNotificationHandlerForAllFuncOME(false), SerializeSILAction(), Types(TC) { assert(!context.isNull()); if (auto *file = context.dyn_cast()) { diff --git a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp index 769ee6c2cef75..0e57fc67d8a6e 100644 --- a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp +++ b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp @@ -370,7 +370,7 @@ static void prepareSILFunctionForOptimization(ModuleDecl *, SILFunction *F) { namespace { -struct OwnershipModelEliminator : SILModuleTransform { +struct OwnershipModelEliminator : SILFunctionTransform { bool SkipTransparent; bool SkipStdlibModule; @@ -379,10 +379,11 @@ struct OwnershipModelEliminator : SILModuleTransform { void run() override { if (DumpBefore.size()) { - getModule()->dump(DumpBefore.c_str()); + getFunction()->dump(DumpBefore.c_str()); } - auto &Mod = *getModule(); + auto *F = getFunction(); + auto &Mod = getFunction()->getModule(); // If we are supposed to skip the stdlib module and we are in the stdlib // module bail. @@ -390,42 +391,38 @@ struct OwnershipModelEliminator : SILModuleTransform { return; } - for (auto &F : Mod) { - // If F does not have ownership, skip it. We have no further work to do. - if (!F.hasOwnership()) - continue; - - // If we were asked to not strip ownership from transparent functions in - // /our/ module, continue. - if (SkipTransparent && F.isTransparent()) - continue; - - // Verify here to make sure ownership is correct before we strip. - { - // Add a pretty stack trace entry to tell users who see a verification - // failure triggered by this verification check that they need to re-run - // with -sil-verify-all to actually find the pass that introduced the - // verification error. - // - // DISCUSSION: This occurs due to the crash from the verification - // failure happening in the pass itself. This causes us to dump the - // SILFunction and emit a msg that this pass (OME) is the culprit. This - // is generally correct for most passes, but not for OME since we are - // verifying before we have even modified the function to ensure that - // all ownership invariants have been respected before we lower - // ownership from the function. - llvm::PrettyStackTraceString silVerifyAllMsgOnFailure( - "Found verification error when verifying before lowering " - "ownership. Please re-run with -sil-verify-all to identify the " - "actual pass that introduced the verification error."); - F.verify(); - } + if (!F->hasOwnership()) + return; - if (stripOwnership(F)) { - auto InvalidKind = - SILAnalysis::InvalidationKind::BranchesAndInstructions; - invalidateAnalysis(&F, InvalidKind); - } + // If we were asked to not strip ownership from transparent functions in + // /our/ module, return. + if (SkipTransparent && F->isTransparent()) + return; + + // Verify here to make sure ownership is correct before we strip. + { + // Add a pretty stack trace entry to tell users who see a verification + // failure triggered by this verification check that they need to re-run + // with -sil-verify-all to actually find the pass that introduced the + // verification error. + // + // DISCUSSION: This occurs due to the crash from the verification + // failure happening in the pass itself. This causes us to dump the + // SILFunction and emit a msg that this pass (OME) is the culprit. This + // is generally correct for most passes, but not for OME since we are + // verifying before we have even modified the function to ensure that + // all ownership invariants have been respected before we lower + // ownership from the function. + llvm::PrettyStackTraceString silVerifyAllMsgOnFailure( + "Found verification error when verifying before lowering " + "ownership. Please re-run with -sil-verify-all to identify the " + "actual pass that introduced the verification error."); + F->verify(); + } + + if (stripOwnership(*F)) { + auto InvalidKind = SILAnalysis::InvalidationKind::BranchesAndInstructions; + invalidateAnalysis(InvalidKind); } // If we were asked to strip transparent, we are at the beginning of the @@ -435,12 +432,19 @@ struct OwnershipModelEliminator : SILModuleTransform { FunctionBodyDeserializationNotificationHandler; std::unique_ptr ptr; if (SkipTransparent) { - ptr.reset(new NotificationHandlerTy( - prepareNonTransparentSILFunctionForOptimization)); + if (!Mod.hasRegisteredDeserializationNotificationHandlerForNonTransparentFuncOME()) { + ptr.reset(new NotificationHandlerTy( + prepareNonTransparentSILFunctionForOptimization)); + Mod.registerDeserializationNotificationHandler(std::move(ptr)); + Mod.setRegisteredDeserializationNotificationHandlerForNonTransparentFuncOME(); + } } else { - ptr.reset(new NotificationHandlerTy(prepareSILFunctionForOptimization)); + if (!Mod.hasRegisteredDeserializationNotificationHandlerForAllFuncOME()) { + ptr.reset(new NotificationHandlerTy(prepareSILFunctionForOptimization)); + Mod.registerDeserializationNotificationHandler(std::move(ptr)); + Mod.setRegisteredDeserializationNotificationHandlerForAllFuncOME(); + } } - Mod.registerDeserializationNotificationHandler(std::move(ptr)); } }; diff --git a/lib/SILOptimizer/PassManager/PassManager.cpp b/lib/SILOptimizer/PassManager/PassManager.cpp index e6bee2886a953..f315eec53f65d 100644 --- a/lib/SILOptimizer/PassManager/PassManager.cpp +++ b/lib/SILOptimizer/PassManager/PassManager.cpp @@ -383,11 +383,24 @@ void SILPassManager::dumpPassInfo(const char *Title, unsigned TransIdx, llvm::dbgs() << '\n'; } +bool SILPassManager::isMandatoryFunctionPass(SILFunctionTransform *sft) { + return isMandatory || sft->getPassKind() == + PassKind::NonTransparentFunctionOwnershipModelEliminator || + sft->getPassKind() == PassKind::OwnershipModelEliminator || + sft->getPassKind() == + PassKind::NonStdlibNonTransparentFunctionOwnershipModelEliminator; +} + void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { assert(analysesUnlocked() && "Expected all analyses to be unlocked!"); auto *SFT = cast(Transformations[TransIdx]); + + if (!F->shouldOptimize() && !isMandatoryFunctionPass(SFT)) { + return; + } + SFT->injectPassManager(this); SFT->injectFunction(F); @@ -395,9 +408,10 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { DebugPrintEnabler DebugPrint(NumPassesRun); // If nothing changed since the last run of this pass, we can skip this - // pass. + // pass if it is not mandatory CompletedPasses &completedPasses = CompletedPassesMap[F]; - if (completedPasses.test((size_t)SFT->getPassKind()) && + if (!isMandatoryFunctionPass(SFT) && + completedPasses.test((size_t)SFT->getPassKind()) && !SILDisableSkippingPasses) { if (SILPrintPassName) dumpPassInfo("(Skip)", TransIdx, F); @@ -513,7 +527,7 @@ runFunctionPasses(unsigned FromTransIdx, unsigned ToTransIdx) { // Only include functions that are definitions, and which have not // been intentionally excluded from optimization. - if (F.isDefinition() && (isMandatory || F.shouldOptimize())) + if (F.isDefinition()) FunctionWorklist.push_back(*I); } diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index fda5b5fe7f589..f93042ad18bcb 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -283,6 +283,10 @@ void addFunctionPasses(SILPassPipelinePlan &P, // splits up copy_addr. P.addCopyForwarding(); + // We earlier eliminated ownership if we are not compiling the stdlib. Now + // handle the stdlib functions. + P.addNonTransparentFunctionOwnershipModelEliminator(); + // Optimize copies from a temporary (an "l-value") to a destination. P.addTempLValueOpt(); @@ -489,10 +493,7 @@ static void addHighLevelFunctionPipeline(SILPassPipelinePlan &P) { // FIXME: update EagerSpecializer to be a function pass! P.addEagerSpecializer(); - // We earlier eliminated ownership if we are not compiling the stdlib. Now - // handle the stdlib functions. - P.addNonTransparentFunctionOwnershipModelEliminator(); - + // stdlib ownership model elimination is done within addFunctionPasses addFunctionPasses(P, OptimizationLevelKind::HighLevel); addHighLevelLoopOptPasses(P); diff --git a/test/DebugInfo/dbgvalue-insertpt.swift b/test/DebugInfo/dbgvalue-insertpt.swift index 0d670e4bb7dba..469f1223f4a93 100644 --- a/test/DebugInfo/dbgvalue-insertpt.swift +++ b/test/DebugInfo/dbgvalue-insertpt.swift @@ -1,19 +1,9 @@ -// RUN: %target-swift-frontend -g -emit-ir %s | %FileCheck %s +// RUN: %target-swift-frontend -g -emit-ir -Xllvm '-sil-inline-never-functions=next' %s | %FileCheck %s // FIXME: This test should be testing a non-shadow-copied value instead. for i in 0 ..< 3 { // CHECK: %[[ALLOCA:[0-9]+]] = alloca %TSiSg // CHECK: %i.debug = alloca i{{32|64}} // CHECK-NEXT: call void @llvm.dbg.declare(metadata i{{32|64}}* %i.debug, // CHECK-SAME: metadata ![[I:[0-9]+]], - // CHECK: call swiftcc{{.*}} @{{.*}}next{{.*}} - // CHECK: %[[LD:[0-9]+]] = load i{{32|64}}, i{{32|64}}* - // CHECK: br i1 {{%.*}}, label %[[FAIL:.*]], label %[[SUCCESS:.*]], - // - // CHECK: [[SUCCESS]]: - // CHECK: br label %[[NEXT_BB:.*]], - // - // CHECK: [[NEXT_BB]]: - // CHECK: %[[PHI_VAL:.*]] = phi i{{32|64}} [ %[[LD]], %[[SUCCESS]] ] - // CHECK: store i{{32|64}} %[[PHI_VAL]], i{{32|64}}* %i.debug // CHECK: ![[I]] = !DILocalVariable(name: "i", } diff --git a/test/IRGen/big_types_corner_cases.swift b/test/IRGen/big_types_corner_cases.swift index f956b23f7effe..7acfaa13c601d 100644 --- a/test/IRGen/big_types_corner_cases.swift +++ b/test/IRGen/big_types_corner_cases.swift @@ -204,12 +204,6 @@ public func testGetFunc() { // CHECK: [[CALL1:%.*]] = call {{.*}} @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$sSayy22big_types_corner_cases9BigStructVcSgGMD" // CHECK: [[CALL2:%.*]] = call i8** @"$sSayy22big_types_corner_cases9BigStructVcSgGSayxGSlsWl // CHECK: call swiftcc void @"$sSlsE10firstIndex5where0B0QzSgSb7ElementQzKXE_tKF"(%TSq.{{.*}}* noalias nocapture sret %{{[0-9]+}}, i8* bitcast ({{.*}}* @"$s22big_types_corner_cases9BigStruct{{.*}}_TRTA{{(\.ptrauth)?}}" to i8*), %swift.opaque* %{{[0-9]+}}, %swift.type* %{{[0-9]+}}, i8** [[CALL2]] - -// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} hidden swiftcc void @"$s22big_types_corner_cases7TestBigC5test2yyF"(%T22big_types_corner_cases7TestBigC* swiftself %0) -// CHECK: [[CALL1:%.*]] = call {{.*}} @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$sSaySS2ID_y22big_types_corner_cases9BigStructVcSg7handlertGMD" -// CHECK: [[CALL2:%.*]] = call i8** @"$sSaySS2ID_y22big_types_corner_cases9BigStructVcSg7handlertGSayxGSlsWl" -// CHECK: call swiftcc void @"$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF"(%Ts16IndexingIteratorV{{.*}}* noalias nocapture sret {{.*}}, %swift.type* [[CALL1]], i8** [[CALL2]], %swift.opaque* noalias nocapture swiftself {{.*}}) -// CHECK: ret void class TestBig { typealias Handler = (BigStruct) -> Void diff --git a/test/SILOptimizer/copyforward_ossa.sil b/test/SILOptimizer/copyforward_ossa.sil index 9719fc0ec92cc..11be2a5140875 100644 --- a/test/SILOptimizer/copyforward_ossa.sil +++ b/test/SILOptimizer/copyforward_ossa.sil @@ -265,7 +265,7 @@ sil [ossa] @_TFSq4someU__fMGSqQ__FQ_GSqQ__ : $@convention(thin) <τ_0_0> (@in τ sil [ossa] @_TFsoi2neU__FTGSqQ__Vs26_OptionalNilComparisonType_Sb : $@convention(thin) <τ_0_0> (@in Optional<τ_0_0>, _OptionalNilComparisonType) -> Bool -// CHECK-LABEL: sil hidden [ossa] @option_init +// CHECK-LABEL: sil hidden [ossa] @option_init : // CHECK: alloc_stack // CHECK: alloc_stack // CHECK: alloc_stack diff --git a/test/SILOptimizer/prespecialize.swift b/test/SILOptimizer/prespecialize.swift index f9c27e9e02cb9..1cbc165230253 100644 --- a/test/SILOptimizer/prespecialize.swift +++ b/test/SILOptimizer/prespecialize.swift @@ -7,12 +7,6 @@ // CHECK-LABEL: sil [noinline] @$s13prespecialize4test_4sizeySaySiGz_SitF // -// function_ref specialized Collection.makeIterator() -> IndexingIterator -// CHECK: function_ref @$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyFSnySiG_Tg5 -// -// function_ref specialized IndexingIterator.next() -> A.Element? -// CHECK: function_ref @$ss16IndexingIteratorV4next7ElementQzSgyFSnySiG_Tg5 -// // Look for generic specialization of Swift.Array.subscript.getter : (Swift.Int) -> A // CHECK: function_ref @$sSayxSicigSi_Tg5 // CHECK: return From e64848274f9918e842b48cf772fc3ccbc3314040 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 29 Sep 2020 13:43:10 +0200 Subject: [PATCH 103/745] tests: add a missing codesign command in merge_func_ptrauth.swift rdar://problem/69727772 --- test/LLVMPasses/merge_func_ptrauth.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/test/LLVMPasses/merge_func_ptrauth.swift b/test/LLVMPasses/merge_func_ptrauth.swift index 031997f804578..8d4d9dc0c44e5 100644 --- a/test/LLVMPasses/merge_func_ptrauth.swift +++ b/test/LLVMPasses/merge_func_ptrauth.swift @@ -2,6 +2,7 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift -O -module-name=test %s -o %t/a.out +// RUN: %target-codesign %t/a.out // RUN: %target-run %t/a.out | %FileCheck %s -check-prefix=CHECK-OUTPUT // REQUIRES: executable_test From f18f3daba856ea08660225810e8e84bc2818f145 Mon Sep 17 00:00:00 2001 From: Alexey Komnin Date: Sat, 26 Sep 2020 02:35:27 +0300 Subject: [PATCH 104/745] Fix SR-13490: Fix LT operator and add tests for ImportPath --- include/swift/AST/Import.h | 19 ++++++++- lib/IDE/ModuleInterfacePrinting.cpp | 10 +---- unittests/AST/CMakeLists.txt | 1 + unittests/AST/ImportTests.cpp | 60 +++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 11 deletions(-) create mode 100644 unittests/AST/ImportTests.cpp diff --git a/include/swift/AST/Import.h b/include/swift/AST/Import.h index b4649d259b5c9..55f5bd642f0f5 100644 --- a/include/swift/AST/Import.h +++ b/include/swift/AST/Import.h @@ -22,9 +22,10 @@ #include "swift/AST/Identifier.h" #include "swift/Basic/Located.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include namespace swift { class ASTContext; @@ -191,6 +192,20 @@ namespace detail { }; } +/// @name ImportPathBase Comparison Operators +/// @{ +template +inline bool operator<(const detail::ImportPathBase &LHS, + const detail::ImportPathBase &RHS) { + using Element = typename detail::ImportPathBase::Element; + auto Comparator = [](const Element &l, const Element &r) { + return l.Item.compare(r.Item) < 0; + }; + return std::lexicographical_compare(LHS.begin(), LHS.end(), RHS.begin(), + RHS.end(), Comparator); +} +/// @} + /// An undifferentiated series of dotted identifiers in an \c import statement, /// like \c Foo.Bar. Each identifier is packaged with its corresponding source /// location. diff --git a/lib/IDE/ModuleInterfacePrinting.cpp b/lib/IDE/ModuleInterfacePrinting.cpp index b7a7ba2f08486..3446c135f8455 100644 --- a/lib/IDE/ModuleInterfacePrinting.cpp +++ b/lib/IDE/ModuleInterfacePrinting.cpp @@ -374,15 +374,7 @@ static bool printModuleInterfaceDecl(Decl *D, /// Sorts import declarations for display. static bool compareImports(ImportDecl *LHS, ImportDecl *RHS) { - // TODO(SR-13490): Probably buggy--thinks "import Foo" == "import Foo.Bar". - // ImportPathBase should provide universal comparison functions to avoid this. - auto LHSPath = LHS->getImportPath(); - auto RHSPath = RHS->getImportPath(); - for (unsigned i: range(std::min(LHSPath.size(), RHSPath.size()))) { - if (int Ret = LHSPath[i].Item.str().compare(RHSPath[i].Item.str())) - return Ret < 0; - } - return false; + return LHS->getImportPath() < RHS->getImportPath(); }; /// Sorts Swift declarations for display. diff --git a/unittests/AST/CMakeLists.txt b/unittests/AST/CMakeLists.txt index edd0cc8f1951b..03a3064319f2c 100644 --- a/unittests/AST/CMakeLists.txt +++ b/unittests/AST/CMakeLists.txt @@ -6,6 +6,7 @@ add_swift_unittest(SwiftASTTests TestContext.cpp TypeMatchTests.cpp VersionRangeLattice.cpp + ImportTests.cpp ) target_link_libraries(SwiftASTTests diff --git a/unittests/AST/ImportTests.cpp b/unittests/AST/ImportTests.cpp new file mode 100644 index 0000000000000..8b4d4ca93dc00 --- /dev/null +++ b/unittests/AST/ImportTests.cpp @@ -0,0 +1,60 @@ +//===--- ImportTests.cpp - Tests for representation of imports ------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "TestContext.h" +#include "swift/AST/Import.h" +#include "gtest/gtest.h" + +using namespace swift; +using namespace swift::unittest; + +namespace { +/// Helper class used to create ImportPath and hold all strings for identifiers +class ImportPathContext { + TestContext Ctx; + +public: + ImportPathContext() = default; + + /// Hepler routine for building ImportPath + /// Build() + /// @see ImportPathBuilder + inline ImportPath Build(StringRef Name) noexcept { + return ImportPath::Builder(Ctx.Ctx, Name, '.').copyTo(Ctx.Ctx); + } +}; + +} // namespace + +TEST(ImportPath, Comparison) { + ImportPathContext ctx; + + /// Simple sanity check: + EXPECT_FALSE(ctx.Build("A.B.C") < ctx.Build("A.B.C")); + + /// Check order chain: + /// A < A.A < A.A.A < A.A.B < A.B < A.B.A < AA < B < B.A + EXPECT_LT(ctx.Build("A"), ctx.Build("A.A")); + EXPECT_LT(ctx.Build("A.A"), ctx.Build("A.A.A")); + EXPECT_LT(ctx.Build("A.A.A"), ctx.Build("A.A.B")); + EXPECT_LT(ctx.Build("A.A.B"), ctx.Build("A.B")); + EXPECT_LT(ctx.Build("A.B"), ctx.Build("A.B.A")); + EXPECT_LT(ctx.Build("A.B.A"), ctx.Build("AA")); + EXPECT_LT(ctx.Build("B"), ctx.Build("B.A")); + + /// Further ImportPaths are semantically incorrect, but we must + /// check that comparing them does not cause compiler to crash. + EXPECT_LT(ctx.Build("."), ctx.Build("A")); + EXPECT_LT(ctx.Build("A"), ctx.Build("AA.")); + EXPECT_LT(ctx.Build("A"), ctx.Build("AA..")); + EXPECT_LT(ctx.Build(".A"), ctx.Build("AA")); +} From 28228eba49a9669ca9c5fb8c88143b8a9c1d2ca5 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Tue, 29 Sep 2020 09:41:13 -0700 Subject: [PATCH 105/745] [code-completion] Fix cache writer for non-decl kind Thanks to Ben Barham for spotting this: sizeof(~static_cast(...)) is 4, but we need to write a single byte here. Thankfully this code was probably not being hit in the current caching scheme, which only caches declarations. Looking at the history, this code was broken by d8fbaa01eb10, which was fixing an MSVC warning in this code. Unfortunately I do not have access to the version of MSVC to check if there is still a warning here or for what. --- lib/IDE/CodeCompletionCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/IDE/CodeCompletionCache.cpp b/lib/IDE/CodeCompletionCache.cpp index ede2b04cdd6f4..dc5aeb80ca19f 100644 --- a/lib/IDE/CodeCompletionCache.cpp +++ b/lib/IDE/CodeCompletionCache.cpp @@ -343,7 +343,7 @@ static void writeCachedModule(llvm::raw_ostream &out, if (R->getKind() == CodeCompletionResult::Declaration) LE.write(static_cast(R->getAssociatedDeclKind())); else - LE.write(~static_cast(0u)); + LE.write(static_cast(~0u)); if (R->isOperator()) LE.write(static_cast(R->getOperatorKind())); else From 8e8c57caf59f043b4046ad0df6e7c358bcac9903 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Fri, 25 Sep 2020 13:30:38 -0700 Subject: [PATCH 106/745] SIL type lowering: Compute IsTypeExpansionSensitive and use it instead of TypeBase's hasOpaqueArchetypePropertiesOrCases A type is IsTypeExpansionSensitive if it contains an opaque result type that influences how the type is lowered. This could be because the type mentions an opaque archetype and therefore we would look through to the underlying type depending on the type expansion and could get a different SIL type. For example '() -> out some P' could lower to '() -> @out Int'. Or this could be because when we lower an aggregate type some of its fields are opaque types that could be looked through and therefore the aggregate has different lowering (e.g address vs. loadable) in different type expansion contexts. By replacing it this change also fixes an infinite recursion in hasOpaqueArchetypePropertiesOrCases. rdar://68798822 --- include/swift/AST/Types.h | 3 - include/swift/SIL/TypeLowering.h | 75 +++-- lib/AST/Type.cpp | 21 -- lib/SIL/IR/TypeLowering.cpp | 438 ++++++++++++++++++---------- test/IRGen/opaque_result_type.swift | 14 +- 5 files changed, 340 insertions(+), 211 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 12c2643de24ea..c58a598ec96b9 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -600,9 +600,6 @@ class alignas(1 << TypeAlignInBits) TypeBase { bool hasOpaqueArchetype() const { return getRecursiveProperties().hasOpaqueArchetype(); } - /// Determine whether the type has any stored properties or enum cases that - /// involve an opaque type. - bool hasOpaqueArchetypePropertiesOrCases(); /// Determine whether the type is an opened existential type. /// diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index ed135eccf8e2f..7976b5c5f00a4 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -147,16 +147,23 @@ enum IsResilient_t : bool { IsResilient = true }; +/// Does this type contain an opaque result type that affects type lowering? +enum IsTypeExpansionSensitive_t : bool { + IsNotTypeExpansionSensitive = false, + IsTypeExpansionSensitive = true +}; + /// Extended type information used by SIL. class TypeLowering { public: class RecursiveProperties { // These are chosen so that bitwise-or merges the flags properly. enum : unsigned { - NonTrivialFlag = 1 << 0, - NonFixedABIFlag = 1 << 1, - AddressOnlyFlag = 1 << 2, - ResilientFlag = 1 << 3, + NonTrivialFlag = 1 << 0, + NonFixedABIFlag = 1 << 1, + AddressOnlyFlag = 1 << 2, + ResilientFlag = 1 << 3, + TypeExpansionSensitiveFlag = 1 << 4, }; uint8_t Flags; @@ -165,15 +172,17 @@ class TypeLowering { /// a trivial, loadable, fixed-layout type. constexpr RecursiveProperties() : Flags(0) {} - constexpr RecursiveProperties(IsTrivial_t isTrivial, - IsFixedABI_t isFixedABI, - IsAddressOnly_t isAddressOnly, - IsResilient_t isResilient) - : Flags((isTrivial ? 0U : NonTrivialFlag) | - (isFixedABI ? 0U : NonFixedABIFlag) | - (isAddressOnly ? AddressOnlyFlag : 0U) | - (isResilient ? ResilientFlag : 0U)) {} - + constexpr RecursiveProperties( + IsTrivial_t isTrivial, IsFixedABI_t isFixedABI, + IsAddressOnly_t isAddressOnly, IsResilient_t isResilient, + IsTypeExpansionSensitive_t isTypeExpansionSensitive = + IsNotTypeExpansionSensitive) + : Flags((isTrivial ? 0U : NonTrivialFlag) | + (isFixedABI ? 0U : NonFixedABIFlag) | + (isAddressOnly ? AddressOnlyFlag : 0U) | + (isResilient ? ResilientFlag : 0U) | + (isTypeExpansionSensitive ? TypeExpansionSensitiveFlag : 0U)) {} + constexpr bool operator==(RecursiveProperties p) const { return Flags == p.Flags; } @@ -183,7 +192,7 @@ class TypeLowering { } static constexpr RecursiveProperties forReference() { - return {IsNotTrivial, IsFixedABI, IsNotAddressOnly, IsNotResilient }; + return {IsNotTrivial, IsFixedABI, IsNotAddressOnly, IsNotResilient}; } static constexpr RecursiveProperties forOpaque() { @@ -194,6 +203,7 @@ class TypeLowering { return {IsTrivial, IsFixedABI, IsNotAddressOnly, IsResilient}; } + void addSubobject(RecursiveProperties other) { Flags |= other.Flags; } @@ -210,10 +220,19 @@ class TypeLowering { IsResilient_t isResilient() const { return IsResilient_t((Flags & ResilientFlag) != 0); } + IsTypeExpansionSensitive_t isTypeExpansionSensitive() const { + return IsTypeExpansionSensitive_t( + (Flags & TypeExpansionSensitiveFlag) != 0); + } void setNonTrivial() { Flags |= NonTrivialFlag; } void setNonFixedABI() { Flags |= NonFixedABIFlag; } void setAddressOnly() { Flags |= AddressOnlyFlag; } + void setTypeExpansionSensitive( + IsTypeExpansionSensitive_t isTypeExpansionSensitive) { + Flags = (Flags & ~TypeExpansionSensitiveFlag) | + (isTypeExpansionSensitive ? TypeExpansionSensitiveFlag : 0); + } }; private: @@ -305,6 +324,12 @@ class TypeLowering { return Properties.isResilient(); } + /// Does this type contain an opaque result type that could influence how the + /// type is lowered if we could look through to the underlying type. + bool isTypeExpansionSensitive() const { + return Properties.isTypeExpansionSensitive(); + } + ResilienceExpansion getResilienceExpansion() const { return expansionContext.getResilienceExpansion(); } @@ -705,8 +730,6 @@ class TypeConverter { llvm::DenseMap LoweredCaptures; - llvm::DenseMap opaqueArchetypeFields; - /// Cache of loadable SILType to number of (estimated) fields /// /// Second element is a ResilienceExpansion. @@ -719,17 +742,15 @@ class TypeConverter { Optional BridgedType##Ty; #include "swift/SIL/BridgedTypes.def" - const TypeLowering & - getTypeLoweringForLoweredType(AbstractionPattern origType, - CanType loweredType, - TypeExpansionContext forExpansion, - bool origHadOpaqueTypeArchetype); + const TypeLowering &getTypeLoweringForLoweredType( + AbstractionPattern origType, CanType loweredType, + TypeExpansionContext forExpansion, + IsTypeExpansionSensitive_t isTypeExpansionSensitive); - const TypeLowering * - getTypeLoweringForExpansion(TypeKey key, - TypeExpansionContext forExpansion, - const TypeLowering *lowering, - bool origHadOpaqueTypeArchetype); + const TypeLowering *getTypeLoweringForExpansion( + TypeKey key, TypeExpansionContext forExpansion, + const TypeLowering *minimalExpansionLowering, + IsTypeExpansionSensitive_t isOrigTypeExpansionSensitive); public: ModuleDecl &M; @@ -885,8 +906,6 @@ class TypeConverter { CanType getLoweredTypeOfGlobal(VarDecl *var); - bool hasOpaqueArchetypeOrPropertiesOrCases(CanType ty); - /// Return the SILFunctionType for a native function value of the /// given type. CanSILFunctionType getSILFunctionType(TypeExpansionContext context, diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 129efae29ed8c..6d9dae653c108 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -5048,27 +5048,6 @@ Type TypeBase::openAnyExistentialType(OpenedArchetypeType *&opened) { return opened; } -bool TypeBase::hasOpaqueArchetypePropertiesOrCases() { - if (auto *structDecl = getStructOrBoundGenericStruct()) { - for (auto *field : structDecl->getStoredProperties()) { - auto fieldTy = field->getInterfaceType()->getCanonicalType(); - if (fieldTy->hasOpaqueArchetype() || - fieldTy->hasOpaqueArchetypePropertiesOrCases()) - return true; - } - } - - if (auto *enumDecl = getEnumOrBoundGenericEnum()) { - for (auto *elt : enumDecl->getAllElements()) { - auto eltType = elt->getInterfaceType(); - if (eltType->hasOpaqueArchetype() || - eltType->getCanonicalType()->hasOpaqueArchetypePropertiesOrCases()) - return true; - } - } - return false; -} - CanType swift::substOpaqueTypesWithUnderlyingTypes(CanType ty, TypeExpansionContext context, bool allowLoweredTypes) { diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index a1f00e6bb8699..af7eca86b8330 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -154,7 +154,7 @@ namespace { /// classification. template class TypeClassifierBase - : public CanTypeVisitor + : public CanTypeVisitor { Impl &asImpl() { return *static_cast(this); } protected: @@ -167,9 +167,10 @@ namespace { // The subclass should implement: // // Trivial, fixed-layout, and non-address-only. // RetTy handleTrivial(CanType); - // RetTy handleTrivial(CanType. RecursiveProperties properties); + // RetTy handleTrivial(CanType, RecursiveProperties properties); // // A reference type. - // RetTy handleReference(CanType); + // RetTy handleReference(CanType, RecursiveProperties properties); + // RetTy handleReference(CanType, RecursiveProperties properties); // // Non-trivial and address-only. // RetTy handleAddressOnly(CanType, RecursiveProperties properties); // and, if it doesn't override handleTupleType, @@ -196,13 +197,46 @@ namespace { RetTy handleTrivial(CanType type) { return asImpl().handleTrivial(type, RecursiveProperties::forTrivial()); } + RetTy handleReference(CanType type) { - return asImpl().handle(type, RecursiveProperties::forReference()); + return handleReference(type, RecursiveProperties::forReference()); + } + + RetTy handleReference(CanType type, RecursiveProperties properties) { + return asImpl().handle(type, properties); + } + + RecursiveProperties + mergeIsTypeExpansionSensitive(IsTypeExpansionSensitive_t isSensitive, + RecursiveProperties props) { + if (isSensitive == IsTypeExpansionSensitive) + props.setTypeExpansionSensitive(isSensitive); + return props; + } + + RecursiveProperties + getTrivialRecursiveProperties(IsTypeExpansionSensitive_t isSensitive) { + return mergeIsTypeExpansionSensitive(isSensitive, + RecursiveProperties::forTrivial()); + } + + RecursiveProperties + getReferenceRecursiveProperties(IsTypeExpansionSensitive_t isSensitive) { + return mergeIsTypeExpansionSensitive(isSensitive, + RecursiveProperties::forReference()); + } + + RecursiveProperties + getOpaqueRecursiveProperties(IsTypeExpansionSensitive_t isSensitive) { + return mergeIsTypeExpansionSensitive(isSensitive, + RecursiveProperties::forOpaque()); } #define IMPL(TYPE, LOWERING) \ - RetTy visit##TYPE##Type(Can##TYPE##Type type, AbstractionPattern orig) { \ - return asImpl().handle##LOWERING(type); \ + RetTy visit##TYPE##Type(Can##TYPE##Type type, AbstractionPattern orig, \ + IsTypeExpansionSensitive_t isSensitive) { \ + return asImpl().handle##LOWERING(type, \ + get##LOWERING##RecursiveProperties(isSensitive)); \ } IMPL(BuiltinInteger, Trivial) @@ -222,36 +256,46 @@ namespace { RetTy visitBuiltinUnsafeValueBufferType( CanBuiltinUnsafeValueBufferType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { return asImpl().handleAddressOnly(type, {IsNotTrivial, IsFixedABI, - IsAddressOnly, IsNotResilient}); + IsAddressOnly, IsNotResilient, + isSensitive}); } RetTy visitAnyFunctionType(CanAnyFunctionType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { switch (type->getRepresentation()) { case AnyFunctionType::Representation::Swift: case AnyFunctionType::Representation::Block: - return asImpl().handleReference(type); + return asImpl().handleReference( + type, getReferenceRecursiveProperties(isSensitive)); case AnyFunctionType::Representation::CFunctionPointer: case AnyFunctionType::Representation::Thin: - return asImpl().handleTrivial(type); + return asImpl().handleTrivial( + type, getTrivialRecursiveProperties(isSensitive)); } llvm_unreachable("bad function representation"); } RetTy visitSILFunctionType(CanSILFunctionType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { // Handle `@differentiable` and `@differentiable(linear)` functions. switch (type->getDifferentiabilityKind()) { case DifferentiabilityKind::Normal: return asImpl().visitNormalDifferentiableSILFunctionType( - type, getNormalDifferentiableSILFunctionTypeRecursiveProperties( - type, origType)); + type, mergeIsTypeExpansionSensitive( + isSensitive, + getNormalDifferentiableSILFunctionTypeRecursiveProperties( + type, origType))); case DifferentiabilityKind::Linear: return asImpl().visitLinearDifferentiableSILFunctionType( - type, getLinearDifferentiableSILFunctionTypeRecursiveProperties( - type, origType)); + type, mergeIsTypeExpansionSensitive( + isSensitive, + getLinearDifferentiableSILFunctionTypeRecursiveProperties( + type, origType))); case DifferentiabilityKind::NonDifferentiable: break; } @@ -260,9 +304,11 @@ namespace { type->getExtInfo().getRepresentation() == SILFunctionType::Representation::Thick; if (type->getExtInfo().hasContext() && !isSwiftEscaping) - return asImpl().handleReference(type); + return asImpl().handleReference( + type, getReferenceRecursiveProperties(isSensitive)); // No escaping closures are trivial types. - return asImpl().handleTrivial(type); + return asImpl().handleTrivial(type, + getTrivialRecursiveProperties(isSensitive)); } RecursiveProperties @@ -315,48 +361,55 @@ namespace { } RetTy visitLValueType(CanLValueType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t) { llvm_unreachable("shouldn't get an l-value type here"); } RetTy visitInOutType(CanInOutType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t) { llvm_unreachable("shouldn't get an inout type here"); } RetTy visitErrorType(CanErrorType type, - AbstractionPattern origType) { - return asImpl().handleTrivial(type); + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return asImpl().handleTrivial(type, + getTrivialRecursiveProperties(isSensitive)); } // Dependent types can be lowered according to their corresponding // abstraction pattern. - RetTy visitAbstractTypeParamType(CanType type, - AbstractionPattern origType) { + RetTy visitAbstractTypeParamType(CanType type, AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { if (origType.isTypeParameterOrOpaqueArchetype() || origType.isOpaqueFunctionOrOpaqueDerivativeFunction()) { if (origType.requiresClass()) { - return asImpl().handleReference(type); + return asImpl().handleReference( + type, getReferenceRecursiveProperties(isSensitive)); } else { - return asImpl().handleAddressOnly(type, - RecursiveProperties::forOpaque()); + return asImpl().handleAddressOnly( + type, getOpaqueRecursiveProperties(isSensitive)); } } else { // If the abstraction pattern provides a concrete type, lower as that // type. This can occur if the abstraction pattern provides a more // constrained generic signature with more same-type constraints than // the original declaration whose type we're lowering. - return asImpl().visit(origType.getType(), origType); + return asImpl().visit(origType.getType(), origType, isSensitive); } } RetTy visitGenericTypeParamType(CanGenericTypeParamType type, - AbstractionPattern origType) { - return visitAbstractTypeParamType(type, origType); + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return visitAbstractTypeParamType(type, origType, isSensitive); } RetTy visitDependentMemberType(CanDependentMemberType type, - AbstractionPattern origType) { - return visitAbstractTypeParamType(type, origType); + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return visitAbstractTypeParamType(type, origType, isSensitive); } Type getConcreteReferenceStorageReferent(Type type) { @@ -369,78 +422,102 @@ namespace { #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ RetTy visit##Name##StorageType(Can##Name##StorageType type, \ - AbstractionPattern origType) { \ + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t isSensitive) { \ return asImpl().handleAddressOnly(type, {IsNotTrivial, \ IsFixedABI, \ IsAddressOnly, \ - IsNotResilient}); \ + IsNotResilient, \ + isSensitive}); \ } #define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ RetTy visit##Name##StorageType(Can##Name##StorageType type, \ - AbstractionPattern origType) { \ - return asImpl().handleReference(type); \ + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t isSensitive) { \ + return asImpl().handleReference(type, \ + getReferenceRecursiveProperties(isSensitive)); \ } #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ RetTy visitLoadable##Name##StorageType(Can##Name##StorageType type, \ - AbstractionPattern origType) { \ - return asImpl().handleReference(type); \ + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t isSensitive) { \ + return asImpl().handleReference(type, \ + getReferenceRecursiveProperties(isSensitive)); \ } \ RetTy visitAddressOnly##Name##StorageType(Can##Name##StorageType type, \ - AbstractionPattern origType) { \ + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t isSensitive) { \ return asImpl().handleAddressOnly(type, {IsNotTrivial, \ IsFixedABI, \ IsAddressOnly, \ - IsNotResilient}); \ + IsNotResilient, \ + isSensitive}); \ } \ RetTy visit##Name##StorageType(Can##Name##StorageType type, \ - AbstractionPattern origType) { \ + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t isSensitive) { \ auto referentType = \ type->getReferentType()->lookThroughSingleOptionalType(); \ auto concreteType = getConcreteReferenceStorageReferent(referentType); \ if (Name##StorageType::get(concreteType, TC.Context) \ ->isLoadable(Expansion.getResilienceExpansion())) { \ - return asImpl().visitLoadable##Name##StorageType(type, origType); \ + return asImpl().visitLoadable##Name##StorageType(type, origType, \ + isSensitive); \ } else { \ - return asImpl().visitAddressOnly##Name##StorageType(type, origType); \ + return asImpl().visitAddressOnly##Name##StorageType(type, origType, \ + isSensitive); \ } \ } #define UNCHECKED_REF_STORAGE(Name, ...) \ RetTy visit##Name##StorageType(Can##Name##StorageType type, \ - AbstractionPattern origType) { \ - return asImpl().handleTrivial(type); \ + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t isSensitive) { \ + return asImpl().handleTrivial(type, \ + getTrivialRecursiveProperties(isSensitive)); \ } #include "swift/AST/ReferenceStorage.def" RetTy visitOpaqueTypeArchetypeType(CanOpaqueTypeArchetypeType ty, - AbstractionPattern origType) { + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t) { auto replacedTy = substOpaqueTypesWithUnderlyingTypes(ty, Expansion); if (replacedTy == ty) - return visitArchetypeType(ty, origType); - return this->visit(replacedTy, origType); + return visitArchetypeType(ty, origType, IsTypeExpansionSensitive); + return this->visit(replacedTy, origType, IsTypeExpansionSensitive); } - RetTy visitArchetypeType(CanArchetypeType type, - AbstractionPattern origType) { + RetTy visitArchetypeType(CanArchetypeType ty, AbstractionPattern origType) { + return visitArchetypeType(ty, origType, IsNotTypeExpansionSensitive); + } + + RetTy + visitArchetypeType(CanArchetypeType type, AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { auto LayoutInfo = type->getLayoutConstraint(); if (LayoutInfo) { if (LayoutInfo->isFixedSizeTrivial()) { - return asImpl().handleTrivial(type); + return asImpl().handleTrivial( + type, getTrivialRecursiveProperties(isSensitive)); } if (LayoutInfo->isAddressOnlyTrivial()) { - auto properties = RecursiveProperties::forTrivial(); + auto properties = getTrivialRecursiveProperties(isSensitive); properties.setAddressOnly(); return asImpl().handleAddressOnly(type, properties); } - if (LayoutInfo->isRefCounted()) - return asImpl().handleReference(type); + if (LayoutInfo->isRefCounted()) { + return asImpl().handleReference( + type, getReferenceRecursiveProperties(isSensitive)); + } } - return asImpl().handleAddressOnly(type, RecursiveProperties::forOpaque()); + return asImpl().handleAddressOnly( + type, getOpaqueRecursiveProperties(isSensitive)); } RetTy visitExistentialType(CanType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { switch (SILType::getPrimitiveObjectType(type) .getPreferredExistentialRepresentation()) { case ExistentialRepresentation::None: @@ -450,77 +527,93 @@ namespace { return asImpl().handleAddressOnly(type, {IsNotTrivial, IsFixedABI, IsAddressOnly, - IsNotResilient}); + IsNotResilient, + isSensitive}); // Class-constrained and boxed existentials are refcounted. case ExistentialRepresentation::Class: case ExistentialRepresentation::Boxed: - return asImpl().handleReference(type); + return asImpl().handleReference( + type, getReferenceRecursiveProperties(isSensitive)); // Existential metatypes are trivial. case ExistentialRepresentation::Metatype: - return asImpl().handleTrivial(type); + return asImpl().handleTrivial( + type, getTrivialRecursiveProperties(isSensitive)); } llvm_unreachable("Unhandled ExistentialRepresentation in switch."); } - RetTy visitProtocolType(CanProtocolType type, - AbstractionPattern origType) { - return visitExistentialType(type, origType); + RetTy visitProtocolType(CanProtocolType type, AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return visitExistentialType(type, origType, isSensitive); } RetTy visitProtocolCompositionType(CanProtocolCompositionType type, - AbstractionPattern origType) { - return visitExistentialType(type, origType); + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return visitExistentialType(type, origType, isSensitive); } // Enums depend on their enumerators. - RetTy visitEnumType(CanEnumType type, - AbstractionPattern origType) { - return asImpl().visitAnyEnumType(type, origType, type->getDecl()); + RetTy visitEnumType(CanEnumType type, AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return asImpl().visitAnyEnumType(type, origType, type->getDecl(), + isSensitive); } RetTy visitBoundGenericEnumType(CanBoundGenericEnumType type, - AbstractionPattern origType) { - return asImpl().visitAnyEnumType(type, origType, type->getDecl()); + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return asImpl().visitAnyEnumType(type, origType, type->getDecl(), + isSensitive); } - + // Structs depend on their physical fields. - RetTy visitStructType(CanStructType type, - AbstractionPattern origType) { - return asImpl().visitAnyStructType(type, origType, type->getDecl()); + RetTy visitStructType(CanStructType type, AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return asImpl().visitAnyStructType(type, origType, type->getDecl(), + isSensitive); } RetTy visitBoundGenericStructType(CanBoundGenericStructType type, - AbstractionPattern origType) { - return asImpl().visitAnyStructType(type, origType, type->getDecl()); + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return asImpl().visitAnyStructType(type, origType, type->getDecl(), + isSensitive); } // Tuples depend on their elements. - RetTy visitTupleType(CanTupleType type, - AbstractionPattern origType) { + RetTy visitTupleType(CanTupleType type, AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { RecursiveProperties props; for (unsigned i = 0, e = type->getNumElements(); i < e; ++i) { props.addSubobject(classifyType(origType.getTupleElementType(i), type.getElementType(i), TC, Expansion)); } + props = mergeIsTypeExpansionSensitive(isSensitive, props); return asImpl().handleAggregateByProperties(type, props); } RetTy visitDynamicSelfType(CanDynamicSelfType type, - AbstractionPattern origType) { - return this->visit(type.getSelfType(), origType); + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return this->visit(type.getSelfType(), origType, isSensitive); } RetTy visitSILBlockStorageType(CanSILBlockStorageType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { // Should not be loaded. return asImpl().handleAddressOnly(type, {IsNotTrivial, IsFixedABI, IsAddressOnly, - IsNotResilient}); + IsNotResilient, + isSensitive}); } RetTy visitSILBoxType(CanSILBoxType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { // Should not be loaded. - return asImpl().handleReference(type); + return asImpl().handleReference( + type, getReferenceRecursiveProperties(isSensitive)); } RetTy handleAggregateByProperties(CanType type, RecursiveProperties props) { @@ -548,32 +641,37 @@ namespace { RecursiveProperties visitAnyEnumType(CanType type, AbstractionPattern origType, - EnumDecl *D) { + EnumDecl *D, + IsTypeExpansionSensitive_t isSensitive) { // We have to look through optionals here without grabbing the // type lowering because the way that optionals are reabstracted // can trip recursion checks if we try to build a lowered type. if (D->isOptionalDecl()) { return visit(type.getOptionalObjectType(), - origType.getOptionalObjectType()); + origType.getOptionalObjectType(), + isSensitive); } // Consult the type lowering. auto &lowering = TC.getTypeLowering(origType, type, Expansion); - return handleClassificationFromLowering(type, lowering); + return handleClassificationFromLowering(type, lowering, isSensitive); } RecursiveProperties visitAnyStructType(CanType type, AbstractionPattern origType, - StructDecl *D) { + StructDecl *D, + IsTypeExpansionSensitive_t isSensitive) { // Consult the type lowering. auto &lowering = TC.getTypeLowering(origType, type, Expansion); - return handleClassificationFromLowering(type, lowering); + return handleClassificationFromLowering(type, lowering, isSensitive); } private: - RecursiveProperties handleClassificationFromLowering(CanType type, - const TypeLowering &lowering) { - return handle(type, lowering.getRecursiveProperties()); + RecursiveProperties + handleClassificationFromLowering(CanType type, const TypeLowering &lowering, + IsTypeExpansionSensitive_t isSensitive) { + return handle(type, mergeIsTypeExpansionSensitive( + isSensitive, lowering.getRecursiveProperties())); } }; } // end anonymous namespace @@ -582,7 +680,8 @@ static RecursiveProperties classifyType(AbstractionPattern origType, CanType type, TypeConverter &tc, TypeExpansionContext expansion) { - return TypeClassifier(tc, expansion).visit(type, origType); + return TypeClassifier(tc, expansion) + .visit(type, origType, IsNotTypeExpansionSensitive); } /// True if the type, or the referenced type of an address @@ -1292,9 +1391,10 @@ namespace { /// loadable. class ReferenceTypeLowering : public LeafLoadableTypeLowering { public: - ReferenceTypeLowering(SILType type, TypeExpansionContext forExpansion) - : LeafLoadableTypeLowering(type, RecursiveProperties::forReference(), - IsReferenceCounted, forExpansion) {} + ReferenceTypeLowering(SILType type, RecursiveProperties properties, + TypeExpansionContext forExpansion) + : LeafLoadableTypeLowering(type, properties, IsReferenceCounted, + forExpansion) {} SILValue emitCopyValue(SILBuilder &B, SILLocation loc, SILValue value) const override { @@ -1324,8 +1424,9 @@ namespace { class Loadable##Name##TypeLowering final : public LeafLoadableTypeLowering { \ public: \ Loadable##Name##TypeLowering(SILType type, \ - TypeExpansionContext forExpansion) \ - : LeafLoadableTypeLowering(type, RecursiveProperties::forReference(), \ + TypeExpansionContext forExpansion, \ + RecursiveProperties props) \ + : LeafLoadableTypeLowering(type, props, \ IsReferenceCounted, \ forExpansion) {} \ SILValue emitCopyValue(SILBuilder &B, SILLocation loc, \ @@ -1436,10 +1537,11 @@ namespace { class UnsafeValueBufferTypeLowering : public AddressOnlyTypeLowering { public: UnsafeValueBufferTypeLowering(SILType type, - TypeExpansionContext forExpansion) + TypeExpansionContext forExpansion, + IsTypeExpansionSensitive_t isSensitive) : AddressOnlyTypeLowering(type, {IsNotTrivial, IsFixedABI, - IsAddressOnly, IsNotResilient}, + IsAddressOnly, IsNotResilient, isSensitive}, forExpansion) {} void emitCopyInto(SILBuilder &B, SILLocation loc, @@ -1520,9 +1622,16 @@ namespace { return new (TC) TrivialTypeLowering(silType, properties, Expansion); } + TypeLowering *handleReference(CanType type, + RecursiveProperties properties) { + auto silType = SILType::getPrimitiveObjectType(type); + return new (TC) ReferenceTypeLowering(silType, properties, Expansion); + } + TypeLowering *handleReference(CanType type) { auto silType = SILType::getPrimitiveObjectType(type); - return new (TC) ReferenceTypeLowering(silType, Expansion); + return new (TC) ReferenceTypeLowering( + silType, RecursiveProperties::forReference(), Expansion); } TypeLowering *handleAddressOnly(CanType type, @@ -1539,30 +1648,37 @@ namespace { #define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ TypeLowering * \ visit##Name##StorageType(Can##Name##StorageType type, \ - AbstractionPattern origType) { \ + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t isSensitive) { \ return new (TC) Loadable##Name##TypeLowering( \ SILType::getPrimitiveObjectType(type), \ - Expansion); \ + Expansion, \ + getReferenceRecursiveProperties(isSensitive)); \ } #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ TypeLowering * \ visitLoadable##Name##StorageType(Can##Name##StorageType type, \ - AbstractionPattern origType) { \ + AbstractionPattern origType, \ + IsTypeExpansionSensitive_t isSensitive) { \ return new (TC) Loadable##Name##TypeLowering( \ SILType::getPrimitiveObjectType(type), \ - Expansion); \ + Expansion, \ + getReferenceRecursiveProperties(isSensitive)); \ } #include "swift/AST/ReferenceStorage.def" TypeLowering * visitBuiltinUnsafeValueBufferType(CanBuiltinUnsafeValueBufferType type, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { auto silType = SILType::getPrimitiveAddressType(type); - return new (TC) UnsafeValueBufferTypeLowering(silType, Expansion); + return new (TC) + UnsafeValueBufferTypeLowering(silType, Expansion, isSensitive); } TypeLowering *visitTupleType(CanTupleType tupleType, - AbstractionPattern origType) { + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { RecursiveProperties properties; for (unsigned i = 0, e = tupleType->getNumElements(); i < e; ++i) { auto eltType = tupleType.getElementType(i); @@ -1570,6 +1686,7 @@ namespace { auto &lowering = TC.getTypeLowering(origEltType, eltType, Expansion); properties.addSubobject(lowering.getRecursiveProperties()); } + properties = mergeIsTypeExpansionSensitive(isSensitive, properties); return handleAggregateByProperties(tupleType, properties); @@ -1602,9 +1719,12 @@ namespace { TypeLowering *visitAnyStructType(CanType structType, AbstractionPattern origType, - StructDecl *D) { + StructDecl *D, + IsTypeExpansionSensitive_t isSensitive) { RecursiveProperties properties; + properties = mergeIsTypeExpansionSensitive(isSensitive, properties); + if (handleResilience(structType, D, properties)) return handleAddressOnly(structType, properties); @@ -1638,9 +1758,12 @@ namespace { TypeLowering *visitAnyEnumType(CanType enumType, AbstractionPattern origType, - EnumDecl *D) { + EnumDecl *D, + IsTypeExpansionSensitive_t isSensitive) { RecursiveProperties properties; + properties = mergeIsTypeExpansionSensitive(isSensitive, properties); + if (handleResilience(enumType, D, properties)) return handleAddressOnly(enumType, properties); @@ -1859,32 +1982,33 @@ TypeConverter::getSILFunctionType(TypeExpansionContext context, getLoweredRValueType(context, origType, substType)); } -bool TypeConverter::hasOpaqueArchetypeOrPropertiesOrCases(CanType ty) { - if (ty->hasOpaqueArchetype()) - return true; - - auto it = opaqueArchetypeFields.find(ty); - if (it == opaqueArchetypeFields.end()) { - bool res = ty->hasOpaqueArchetypePropertiesOrCases(); - opaqueArchetypeFields[ty] = res; - return res; - } - return it->second; -} - const TypeLowering & TypeConverter::getTypeLowering(AbstractionPattern origType, Type origSubstType, TypeExpansionContext forExpansion) { CanType substType = origSubstType->getCanonicalType(); - auto origHadOpaqueTypeArchetype = - hasOpaqueArchetypeOrPropertiesOrCases(origSubstType->getCanonicalType()); + bool origHasOpaqueArchetype = substType->hasOpaqueArchetype(); + // A type is type expansion sensitive if its lowering could depend on the type + // expansion context: + // - If the type has an opaque archetype + // Because depending on the type expansion context we might get a different + // SIL type (Foo vs Foo). + // - or if during type lowering we discover an opaque archetype that + // influences type lowering by type expansion context + // E.g a struct containing a field that is a opaque archetype will be + // loadable or not depending on the type expansion context. In a more + // permissive type expansion context we will look through the opaque + // archetype and could discover a loadable type making the whole aggregate + // loadable. + auto isTypeExpansionSensitive = origHasOpaqueArchetype + ? IsTypeExpansionSensitive + : IsNotTypeExpansionSensitive; auto key = getTypeKey(origType, substType, forExpansion); assert(!substType->is()); auto *candidateLowering = find(key.getKeyForMinimalExpansion()); auto *lowering = getTypeLoweringForExpansion( - key, forExpansion, candidateLowering, origHadOpaqueTypeArchetype); + key, forExpansion, candidateLowering, IsNotTypeExpansionSensitive); if (lowering != nullptr) return *lowering; @@ -1902,20 +2026,19 @@ TypeConverter::getTypeLowering(AbstractionPattern origType, // point in re-checking the table, so just construct a type lowering // and cache it. if (loweredSubstType == substType && key.isCacheable()) { - lowering = LowerType(*this, forExpansion) - .visit(key.SubstType, key.OrigType); + lowering = + LowerType(*this, forExpansion) + .visit(key.SubstType, key.OrigType, isTypeExpansionSensitive); - // Otherwise, check the table at a key that would be used by the - // SILType-based lookup path for the type we just lowered to, then cache - // that same result at this key if possible. + // Otherwise, check the table at a key that would be used by the + // SILType-based lookup path for the type we just lowered to, then cache + // that same result at this key if possible. } else { - lowering = &getTypeLoweringForLoweredType(origType, - loweredSubstType, - forExpansion, - origHadOpaqueTypeArchetype); + lowering = &getTypeLoweringForLoweredType( + origType, loweredSubstType, forExpansion, isTypeExpansionSensitive); } - if (!lowering->isResilient() && !origHadOpaqueTypeArchetype) { + if (!lowering->isResilient() && !lowering->isTypeExpansionSensitive()) { insert(key.getKeyForMinimalExpansion(), lowering); } else { insert(key, lowering); @@ -2099,13 +2222,12 @@ TypeConverter::getTypeLowering(SILType type, // The type lowering for a type parameter relies on its context. assert(sig || !type.getASTType()->hasTypeParameter()); auto loweredType = type.getASTType(); - auto origHadOpaqueTypeArchetype = - hasOpaqueArchetypeOrPropertiesOrCases(loweredType); - - return getTypeLoweringForLoweredType( - AbstractionPattern(sig, loweredType), - loweredType, forExpansion, - origHadOpaqueTypeArchetype); + auto isTypeExpansionSensitive = loweredType->hasOpaqueArchetype() + ? IsTypeExpansionSensitive + : IsNotTypeExpansionSensitive; + return getTypeLoweringForLoweredType(AbstractionPattern(sig, loweredType), + loweredType, forExpansion, + isTypeExpansionSensitive); } const TypeLowering & @@ -2114,11 +2236,10 @@ TypeConverter::getTypeLowering(SILType t, SILFunction &F) { F.getLoweredFunctionType()->getSubstGenericSignature()); } -const TypeLowering & -TypeConverter::getTypeLoweringForLoweredType(AbstractionPattern origType, - CanType loweredType, - TypeExpansionContext forExpansion, - bool origHadOpaqueTypeArchetype) { +const TypeLowering &TypeConverter::getTypeLoweringForLoweredType( + AbstractionPattern origType, CanType loweredType, + TypeExpansionContext forExpansion, + IsTypeExpansionSensitive_t isTypeExpansionSensitive) { assert(loweredType->isLegalSILType() && "type is not lowered!"); (void)loweredType; @@ -2133,7 +2254,7 @@ TypeConverter::getTypeLoweringForLoweredType(AbstractionPattern origType, auto *candidateLowering = find(key.getKeyForMinimalExpansion()); auto *lowering = getTypeLoweringForExpansion( - key, forExpansion, candidateLowering, origHadOpaqueTypeArchetype); + key, forExpansion, candidateLowering, isTypeExpansionSensitive); if (lowering != nullptr) return *lowering; @@ -2151,9 +2272,9 @@ TypeConverter::getTypeLoweringForLoweredType(AbstractionPattern origType, lowering = LowerType(*this, forExpansion) - .visit(loweredType, origType); + .visit(loweredType, origType, isTypeExpansionSensitive); - if (!lowering->isResilient() && !origHadOpaqueTypeArchetype) + if (!lowering->isResilient() && !lowering->isTypeExpansionSensitive()) insert(key.getKeyForMinimalExpansion(), lowering); else { insert(key, lowering); @@ -2169,22 +2290,23 @@ TypeConverter::getTypeLoweringForLoweredType(AbstractionPattern origType, /// check if its the one we want; if not, walk the list until we /// find the right one, returning nullptr if the caller needs to /// go ahead and lower the type with the correct expansion. -const TypeLowering *TypeConverter:: -getTypeLoweringForExpansion(TypeKey key, - TypeExpansionContext forExpansion, - const TypeLowering *lowering, - bool origHadOpaqueTypeArchetype) { - if (lowering == nullptr) +const TypeLowering *TypeConverter::getTypeLoweringForExpansion( + TypeKey key, TypeExpansionContext forExpansion, + const TypeLowering *minimalExpansionLowering, + IsTypeExpansionSensitive_t isOrigTypeSensitive) { + if (minimalExpansionLowering == nullptr) return nullptr; - if (!lowering->isResilient() && !origHadOpaqueTypeArchetype) { + if (!minimalExpansionLowering->isResilient() && + !minimalExpansionLowering->isTypeExpansionSensitive() && + !isOrigTypeSensitive) { // Don't try to refine the lowering for other resilience expansions if // we don't expect to get a different lowering anyway. Similar if the // original type did not have opaque type archetypes. // // See LowerType::handleResilience() for the gory details; we only // set this flag if the type is resilient *and* inside our module. - return lowering; + return minimalExpansionLowering; } auto *exactLowering = find(key); diff --git a/test/IRGen/opaque_result_type.swift b/test/IRGen/opaque_result_type.swift index 3a2d092961f3f..7d046aeec693e 100644 --- a/test/IRGen/opaque_result_type.swift +++ b/test/IRGen/opaque_result_type.swift @@ -195,7 +195,19 @@ struct X: R { var globalOProp: some O = 0 -struct OpaqueProps { +public struct OpaqueProps { static var staticOProp: some O = 0 var instanceOProp: some O = 0 } + +// Make sure we don't recurse indefinitely on recursive enums. +public enum RecursiveEnum { + case a(Int) + case b(String) + indirect case c(RecursiveEnum) +} + +public enum EnumWithTupleWithOpaqueField { + case a(Int) + case b((OpaqueProps, String)) +} From a845c5aff3528ad522034d01d42ed222fc0b862e Mon Sep 17 00:00:00 2001 From: Richard Wei Date: Tue, 29 Sep 2020 10:49:33 -0700 Subject: [PATCH 107/745] [AutoDiff] NFC: Rename AutoDiff-specific method and improve documentation. (#34112) `findAbstractFunctionDecl` in TypeCheckAttr.cpp has a very general name but the functionality is specific to AutoDiff. This patch renames it to `findAutoDiffOriginalFunctionDecl` and introduces minor improvements to the documentation. --- lib/Sema/TypeCheckAttr.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 549c2419bf5bc..309bc313707ae 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -3731,18 +3731,23 @@ enum class AbstractFunctionDeclLookupErrorKind { CandidateNotFunctionDeclaration }; -/// Returns the function declaration corresponding to the given base type -/// (optional), function name, and lookup context. +/// Returns the original function (in the context of a derivative or transpose +/// function) declaration corresponding to the given base type (optional), +/// function name, lookup context, and the expected original function type. /// /// If the base type of the function is specified, member lookup is performed. /// Otherwise, unqualified lookup is performed. /// +/// If the expected original function type has a generic signature, any +/// candidate with a less constrained type signature than the expected original +/// function type will be treated as a viable candidate. +/// /// If the function declaration cannot be resolved, emits a diagnostic and /// returns nullptr. /// /// Used for resolving the referenced declaration in `@derivative` and /// `@transpose` attributes. -static AbstractFunctionDecl *findAbstractFunctionDecl( +static AbstractFunctionDecl *findAutoDiffOriginalFunctionDecl( DeclAttribute *attr, Type baseType, DeclNameRefWithLoc funcNameWithLoc, DeclContext *lookupContext, NameLookupOptions lookupOptions, const llvm::function_ref( @@ -4671,7 +4676,7 @@ static bool typeCheckDerivativeAttr(ASTContext &Ctx, Decl *D, } // Look up original function. - auto *originalAFD = findAbstractFunctionDecl( + auto *originalAFD = findAutoDiffOriginalFunctionDecl( attr, baseType, originalName, derivativeTypeCtx, lookupOptions, isValidOriginalCandidate, originalFnType); if (!originalAFD) { @@ -5230,7 +5235,7 @@ void AttributeChecker::visitTransposeAttr(TransposeAttr *attr) { auto funcLoc = originalName.Loc.getBaseNameLoc(); if (attr->getBaseTypeRepr()) funcLoc = attr->getBaseTypeRepr()->getLoc(); - auto *originalAFD = findAbstractFunctionDecl( + auto *originalAFD = findAutoDiffOriginalFunctionDecl( attr, baseType, originalName, transposeTypeCtx, lookupOptions, isValidOriginalCandidate, expectedOriginalFnType); if (!originalAFD) { From 26b5230f3c93ace3b5ad77ce36826bb5ea5da0d6 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Mon, 28 Sep 2020 10:01:38 -0700 Subject: [PATCH 108/745] Remove stray character. --- include/swift/Strings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/Strings.h b/include/swift/Strings.h index 69b29ff4a52b9..2573fa1c8e616 100644 --- a/include/swift/Strings.h +++ b/include/swift/Strings.h @@ -113,7 +113,7 @@ constexpr static BuiltinNameStringLiteral BUILTIN_TYPE_NAME_UNSAFEVALUEBUFFER = {"Builtin.UnsafeValueBuffer"}; /// The name of the Builtin type for UnknownObject /// -/// This no longer exists as an AST-accessible type, but it's still used for  +/// This no longer exists as an AST-accessible type, but it's still used for /// fields shaped like AnyObject when ObjC interop is enabled. constexpr static BuiltinNameStringLiteral BUILTIN_TYPE_NAME_UNKNOWNOBJECT = { "Builtin.UnknownObject"}; From 427c6478bc448218b11ce04b681502809d3a6c15 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 29 Sep 2020 12:46:23 -0700 Subject: [PATCH 109/745] [ConstraintSystem] `ConstraintSystem` cannot be const when inferring new bindings It's possible that bindings would need to mutate constraint system e.g. by allocating new locators. --- lib/Sema/CSBindings.cpp | 11 +++++------ lib/Sema/ConstraintSystem.h | 10 +++++----- lib/Sema/TypeCheckConstraints.cpp | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index da8fbc4525aae..6fd713ced1cd0 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -22,7 +22,7 @@ using namespace swift; using namespace constraints; void ConstraintSystem::PotentialBindings::inferTransitiveBindings( - const ConstraintSystem &cs, llvm::SmallPtrSetImpl &existingTypes, + ConstraintSystem &cs, llvm::SmallPtrSetImpl &existingTypes, const llvm::SmallDenseMap &inferredBindings) { @@ -144,7 +144,7 @@ isUnviableDefaultType(Type defaultType, } void ConstraintSystem::PotentialBindings::inferDefaultTypes( - const ConstraintSystem &cs, llvm::SmallPtrSetImpl &existingTypes) { + ConstraintSystem &cs, llvm::SmallPtrSetImpl &existingTypes) { auto isDirectRequirement = [&](Constraint *constraint) -> bool { if (auto *typeVar = constraint->getFirstType()->getAs()) { auto *repr = cs.getRepresentative(typeVar); @@ -300,7 +300,7 @@ void ConstraintSystem::PotentialBindings::inferDefaultTypes( } void ConstraintSystem::PotentialBindings::finalize( - const ConstraintSystem &cs, + ConstraintSystem &cs, const llvm::SmallDenseMap &inferredBindings) { @@ -620,8 +620,7 @@ bool ConstraintSystem::PotentialBindings::favoredOverDisjunction( } ConstraintSystem::PotentialBindings -ConstraintSystem::inferBindingsFor(TypeVariableType *typeVar, - bool finalize) const { +ConstraintSystem::inferBindingsFor(TypeVariableType *typeVar, bool finalize) { assert(typeVar->getImpl().getRepresentative(nullptr) == typeVar && "not a representative"); assert(!typeVar->getImpl().getFixedType(nullptr) && "has a fixed type"); @@ -829,7 +828,7 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint( /// representative type variable, along with flags indicating whether /// those types should be opened. bool ConstraintSystem::PotentialBindings::infer( - const ConstraintSystem &cs, llvm::SmallPtrSetImpl &exactTypes, + ConstraintSystem &cs, llvm::SmallPtrSetImpl &exactTypes, Constraint *constraint) { switch (constraint->getKind()) { case ConstraintKind::Bind: diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index d766911c31f84..4d0ef6930530e 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -4775,7 +4775,7 @@ class ConstraintSystem { /// \param inferredBindings The set of all bindings inferred for type /// variables in the workset. void inferTransitiveBindings( - const ConstraintSystem &cs, + ConstraintSystem &cs, llvm::SmallPtrSetImpl &existingTypes, const llvm::SmallDenseMap @@ -4783,17 +4783,17 @@ class ConstraintSystem { /// Infer bindings based on any protocol conformances that have default /// types. - void inferDefaultTypes(const ConstraintSystem &cs, + void inferDefaultTypes(ConstraintSystem &cs, llvm::SmallPtrSetImpl &existingTypes); public: - bool infer(const ConstraintSystem &cs, + bool infer(ConstraintSystem &cs, llvm::SmallPtrSetImpl &exactTypes, Constraint *constraint); /// Finalize binding computation for this type variable by /// inferring bindings from context e.g. transitive bindings. - void finalize(const ConstraintSystem &cs, + void finalize(ConstraintSystem &cs, const llvm::SmallDenseMap &inferredBindings); @@ -4862,7 +4862,7 @@ class ConstraintSystem { /// Infer bindings for the given type variable based on current /// state of the constraint system. PotentialBindings inferBindingsFor(TypeVariableType *typeVar, - bool finalize = true) const; + bool finalize = true); private: Optional diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index e2ac8e0bcec5a..ef179ec4acd1f 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2991,7 +2991,7 @@ void ConstraintSystem::print(raw_ostream &out) const { out << " as "; Type(fixed).print(out, PO); } else { - inferBindingsFor(tv).dump(out, 1); + const_cast(this)->inferBindingsFor(tv).dump(out, 1); } } else { out << " equivalent to "; From c22027260d69e8f9f0279a27b4b310a2b4bdda5e Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 29 Sep 2020 12:53:50 -0700 Subject: [PATCH 110/745] [CSBindings] Infer any type variable connected to a code completion token as a potential hole If type variable is associated with a code completion token it's possible that it doesn't have enough contextual information to be resolved to anything, so let's add a hole type which originates from type variable associated with code completion expression to make this relationship explicit and avoid "fixing" problems rooted in fact that type variable is underconstrained due to code completion. --- lib/Sema/CSBindings.cpp | 21 +++++++++++++++++++++ lib/Sema/ConstraintSystem.h | 2 ++ 2 files changed, 23 insertions(+) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 6fd713ced1cd0..da9b2ef15abf8 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -377,6 +377,15 @@ void ConstraintSystem::PotentialBindings::finalize( PotentiallyIncomplete = true; } + // If this type variable is associated with a code completion token + // and it failed to infer any bindings let's adjust hole's locator + // to point to a code completion token to avoid attempting to "fix" + // this problem since its rooted in the fact that constraint system + // is under-constrained. + if (AssociatedCodeCompletionToken) { + locator = cs.getConstraintLocator(AssociatedCodeCompletionToken); + } + addPotentialBinding(PotentialBinding::forHole(TypeVar, locator)); } @@ -768,6 +777,18 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint( result.InvolvesTypeVariables = true; + // If current type variable is associated with a code completion token + // it's possible that it doesn't have enough contextual information + // to be resolved to anything, so let's note that fact in the potential + // bindings and use it when forming a hole if there are no other bindings + // available. + if (auto *locator = bindingTypeVar->getImpl().getLocator()) { + if (locator->directlyAt()) { + result.AssociatedCodeCompletionToken = locator->getAnchor(); + result.PotentiallyIncomplete = true; + } + } + if (constraint->getKind() == ConstraintKind::Subtype && kind == AllowedBindingKind::Subtypes) { result.SubtypeOf.insert(bindingTypeVar); diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 4d0ef6930530e..bcbc630b4d3ea 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -4644,6 +4644,8 @@ class ConstraintSystem { /// `bind param` are present in the system. bool PotentiallyIncomplete = false; + ASTNode AssociatedCodeCompletionToken = ASTNode(); + /// Whether this type variable has literal bindings. LiteralBindingKind LiteralBinding = LiteralBindingKind::None; From a6c44bfc54faaa96e78f35ed84af80279624210d Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 28 Sep 2020 16:14:28 -0700 Subject: [PATCH 111/745] [CSBindings] If hole originates from code completion token avoid fixing it If the hole is originated from code completion expression let's not try to add a fix, anything connected to a code completion is allowed to be a hole because presence of a code completion token makes constraint system under-constrained due to e.g. lack of expressions on the right-hand side of the token, which are required for a regular type-check. --- lib/Sema/CSBindings.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index da9b2ef15abf8..a187f8cd51a28 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -1242,6 +1242,21 @@ TypeVariableBinding::fixForHole(ConstraintSystem &cs) const { auto *dstLocator = TypeVar->getImpl().getLocator(); auto *srcLocator = Binding.getLocator(); + // FIXME: This check could be turned into an assert once + // all code completion kinds are ported to use + // `TypeChecker::typeCheckForCodeCompletion` API. + if (cs.isForCodeCompletion()) { + // If the hole is originated from code completion expression + // let's not try to fix this, anything connected to a + // code completion is allowed to be a hole because presence + // of a code completion token makes constraint system + // under-constrained due to e.g. lack of expressions on the + // right-hand side of the token, which are required for a + // regular type-check. + if (dstLocator->directlyAt()) + return None; + } + unsigned defaultImpact = 1; if (auto *GP = TypeVar->getImpl().getGenericParameter()) { From 2a67c651eecb6ccd6384a90961cfee46338c954e Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 29 Sep 2020 13:04:59 -0700 Subject: [PATCH 112/745] [Property Wrappers] Generalize a few property wrapper decl context checks to check for type context instead of local context. This generalization will help us implement property wrappers on global variables, which should use the same approach of not adding synthesized accessors to the AST and instead lazily visit them in SILGen. --- lib/AST/Decl.cpp | 2 +- lib/SIL/IR/SILDeclRef.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index fa1a254e2e01a..00042d79aefd8 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5957,7 +5957,7 @@ VarDecl *VarDecl::getPropertyWrapperProjectionVar() const { } void VarDecl::visitAuxiliaryDecls(llvm::function_ref visit) const { - if (!getDeclContext()->isLocalContext()) + if (getDeclContext()->isTypeContext()) return; if (auto *backingVar = getPropertyWrapperBackingProperty()) diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index b7c5239a4999e..89a70eb4d8cfa 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -488,7 +488,7 @@ IsSerialized_t SILDeclRef::isSerialized() const { // Stored property initializers are inlinable if the type is explicitly // marked as @frozen. if (isStoredPropertyInitializer() || (isPropertyWrapperBackingInitializer() && - !d->getDeclContext()->isLocalContext())) { + d->getDeclContext()->isTypeContext())) { auto *nominal = cast(d->getDeclContext()); auto scope = nominal->getFormalAccessScope(/*useDC=*/nullptr, From c2b8565a107920f67ed9074fd4d28d3d7a190fa4 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 13:24:34 -0700 Subject: [PATCH 113/745] [Concurrency] Implicitly synthesize actor queue storage and enqueue. When an actor class has its `enqueue(partialTask:)` implicitly synthesized, also synthesize a stored property for the actor's queue. The type of the property is defined by the _Concurrency library (`_DefaultActorQueue`), and it will be initialized with a call to `_defaultActorQueueCreate` (also provided by the _Concurrency library). Also synthesize the body of the implicitly-generated `enqueue(partialTask:)`, which will be a call to `_defaultActorQueueEnqueuePartialTask(actor:queue:partialTask:)`. Together, all of these allow us to experiment with the form of the queue and the queue operation without affecting the type checker. When `enqueue(partialTask:)` is not implicitly synthesized, the queue storage is not synthesized either. In such cases, the user has taken over the execution of tasks for the actor, rather than using the default implementation. --- include/swift/AST/DiagnosticsSema.def | 6 +- include/swift/AST/KnownIdentifiers.def | 1 + lib/Sema/DerivedConformanceActor.cpp | 202 +++++++++++++++++- lib/Sema/TypeCheckStorage.cpp | 26 ++- stdlib/public/Concurrency/Actor.swift | 30 +++ .../synthesized_conformance_actor.swift | 41 ++++ test/decl/protocol/special/Actor.swift | 11 + 7 files changed, 306 insertions(+), 11 deletions(-) create mode 100644 test/SILGen/synthesized_conformance_actor.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 99225191ee057..f353c230f813d 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4195,9 +4195,9 @@ ERROR(actorisolated_not_actor_instance_member,none, "'@actorIsolated' can only be applied to instance members of actors", ()) -ERROR(partial_task_type_missing,none, - "missing 'PartialAsyncTask' type, probably because the '_Concurrency' " - "module was not imported", ()) +ERROR(concurrency_lib_missing,none, + "missing '%0' declaration, probably because the '_Concurrency' " + "module was not imported", (StringRef)) ERROR(enqueue_partial_task_not_in_context,none, "'enqueue(partialTask:)' can only be implemented in the definition of " "actor class %0", (Type)) diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index f992f6b51dec0..a9c4d53248615 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -141,6 +141,7 @@ IDENTIFIER(withKeywordArguments) IDENTIFIER(wrapped) IDENTIFIER(wrappedValue) IDENTIFIER(wrapperValue) +IDENTIFIER_WITH_NAME(actorStorage, "$__actor_storage") // Kinds of layout constraints IDENTIFIER_WITH_NAME(UnknownLayout, "_UnknownLayout") diff --git a/lib/Sema/DerivedConformanceActor.cpp b/lib/Sema/DerivedConformanceActor.cpp index 7299c18137fca..92f2e2f89021c 100644 --- a/lib/Sema/DerivedConformanceActor.cpp +++ b/lib/Sema/DerivedConformanceActor.cpp @@ -15,6 +15,8 @@ //===----------------------------------------------------------------------===// #include "DerivedConformances.h" #include "TypeChecker.h" +#include "TypeCheckConcurrency.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/ParameterList.h" using namespace swift; @@ -46,16 +48,146 @@ static Type getPartialAsyncTaskType(ASTContext &ctx) { return Type(); } +/// Look for the default actor queue type. +static Type getDefaultActorQueueType(DeclContext *dc, SourceLoc loc) { + ASTContext &ctx = dc->getASTContext(); + UnqualifiedLookupOptions options; + options |= UnqualifiedLookupFlags::TypeLookup; + auto desc = UnqualifiedLookupDescriptor( + DeclNameRef(ctx.getIdentifier("_DefaultActorQueue")), dc, loc, options); + auto lookup = + evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{desc}, {}); + for (const auto &result : lookup) { + if (auto typeDecl = dyn_cast(result.getValueDecl())) + return typeDecl->getDeclaredInterfaceType(); + } + + return Type(); +} + +/// Look for the initialization function for the default actor storage. +static FuncDecl *getDefaultActorQueueCreate(DeclContext *dc, SourceLoc loc) { + ASTContext &ctx = dc->getASTContext(); + auto desc = UnqualifiedLookupDescriptor( + DeclNameRef(ctx.getIdentifier("_defaultActorQueueCreate")), dc, loc, + UnqualifiedLookupOptions()); + auto lookup = + evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{desc}, {}); + for (const auto &result : lookup) { + // FIXME: Validate this further, because we're assuming the exact type. + if (auto func = dyn_cast(result.getValueDecl())) + return func; + } + + return nullptr; +} + +/// Look for the default enqueue operation. +static FuncDecl *getDefaultActorQueueEnqueue(DeclContext *dc, SourceLoc loc) { + ASTContext &ctx = dc->getASTContext(); + auto desc = UnqualifiedLookupDescriptor( + DeclNameRef(ctx.getIdentifier("_defaultActorQueueEnqueuePartialTask")), + dc, loc, UnqualifiedLookupOptions()); + auto lookup = + evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{desc}, {}); + for (const auto &result : lookup) { + // FIXME: Validate this further, because we're assuming the exact type. + if (auto func = dyn_cast(result.getValueDecl())) + return func; + } + + return nullptr; +} + static std::pair deriveBodyActor_enqueuePartialTask( AbstractFunctionDecl *enqueuePartialTask, void *) { + // func enqueue(partialTask: PartialAsyncTask) { + // _defaultActorQueueEnqueuePartialTask( + // actor: self, queue: &self.$__actor_storage, partialTask: partialTask) + // } ASTContext &ctx = enqueuePartialTask->getASTContext(); - // FIXME: Call into runtime API to enqueue the task, once we figure out - // what that runtime API should look like. + // Dig out the $__actor_storage property. + auto classDecl = enqueuePartialTask->getDeclContext()->getSelfClassDecl(); + VarDecl *storageVar = nullptr; + for (auto decl : classDecl->lookupDirect(ctx.Id_actorStorage)) { + storageVar = dyn_cast(decl); + if (storageVar) + break; + } + + // Produce an empty brace statement on failure. + auto failure = [&]() -> std::pair { + auto body = BraceStmt::create( + ctx, SourceLoc(), { }, SourceLoc(), /*implicit=*/true); + return { body, /*isTypeChecked=*/true }; + }; + + if (!storageVar) { + classDecl->diagnose( + diag::concurrency_lib_missing, ctx.Id_actorStorage.str()); + return failure(); + } + + // Call into the runtime to enqueue the task. + auto fn = getDefaultActorQueueEnqueue(classDecl, classDecl->getLoc()); + if (!fn) { + classDecl->diagnose( + diag::concurrency_lib_missing, "_defaultActorQueueEnqueuePartialTask"); + return failure(); + } + + // Reference to _defaultActorQueueEnqueuePartialTask. + auto fnRef = new (ctx) DeclRefExpr(fn, DeclNameLoc(), /*Implicit=*/true); + fnRef->setType(fn->getInterfaceType()); + + // self argument to the function. + auto selfDecl = enqueuePartialTask->getImplicitSelfDecl(); + Type selfType = enqueuePartialTask->mapTypeIntoContext( + selfDecl->getValueInterfaceType()); + Expr *selfArg = new (ctx) DeclRefExpr( + selfDecl, DeclNameLoc(), /*Implicit=*/true, AccessSemantics::Ordinary, + selfType); + selfArg = ErasureExpr::create(ctx, selfArg, ctx.getAnyObjectType(), { }); + selfArg->setImplicit(); + + // Address of the actor storage. + auto module = classDecl->getModuleContext(); + Expr *selfBase = new (ctx) DeclRefExpr( + selfDecl, DeclNameLoc(), /*Implicit=*/true, AccessSemantics::Ordinary, + selfType); + SubstitutionMap storageVarSubs = classDecl->getDeclaredTypeInContext() + ->getMemberSubstitutionMap(module, storageVar); + ConcreteDeclRef storageVarDeclRef(storageVar, storageVarSubs); + Type storageVarType = classDecl->mapTypeIntoContext( + storageVar->getValueInterfaceType()); + Type storageVarRefType = LValueType::get(storageVarType); + Expr *storageVarRefExpr = new (ctx) MemberRefExpr( + selfBase, SourceLoc(), storageVarDeclRef, DeclNameLoc(), + /*Implicit=*/true); + storageVarRefExpr->setType(storageVarRefType); + storageVarRefExpr = new (ctx) InOutExpr( + SourceLoc(), storageVarRefExpr, storageVarType, /*isImplicit=*/true); + + // The partial asynchronous task. + auto partialTaskParam = enqueuePartialTask->getParameters()->get(0); + Expr *partialTask = new (ctx) DeclRefExpr( + partialTaskParam, DeclNameLoc(), /*Implicit=*/true, + AccessSemantics::Ordinary, + enqueuePartialTask->mapTypeIntoContext( + partialTaskParam->getValueInterfaceType())); + + // Form the call itself. + auto call = CallExpr::createImplicit( + ctx, fnRef, { selfArg, storageVarRefExpr, partialTask }, + { ctx.getIdentifier("actor"), ctx.getIdentifier("queue"), + ctx.Id_partialTask }); + call->setType(fn->getResultInterfaceType()); + call->setThrows(false); auto body = BraceStmt::create( - ctx, SourceLoc(), { }, SourceLoc(), /*implicit=*/true); + ctx, SourceLoc(), { call }, SourceLoc(), /*implicit=*/true); return { body, /*isTypeChecked=*/true }; } @@ -63,19 +195,39 @@ deriveBodyActor_enqueuePartialTask( static ValueDecl *deriveActor_enqueuePartialTask(DerivedConformance &derived) { ASTContext &ctx = derived.Context; + // Retrieve the types and declarations we'll need to form this operation. Type partialTaskType = getPartialAsyncTaskType(ctx); if (!partialTaskType) { - derived.Nominal->diagnose(diag::partial_task_type_missing); + derived.Nominal->diagnose( + diag::concurrency_lib_missing, ctx.Id_PartialAsyncTask.str()); return nullptr; } auto parentDC = derived.getConformanceContext(); + Type defaultActorQueueType = getDefaultActorQueueType( + parentDC, derived.ConformanceDecl->getLoc()); + if (!defaultActorQueueType) { + derived.Nominal->diagnose( + diag::concurrency_lib_missing, "_DefaultActorQueue"); + return nullptr; + } + + auto actorStorageCreateFn = getDefaultActorQueueCreate( + parentDC, derived.ConformanceDecl->getLoc()); + if (!actorStorageCreateFn) { + derived.Nominal->diagnose( + diag::concurrency_lib_missing, "_defaultActorQueueCreate"); + return nullptr; + } + + // Partial task parameter to enqueue(partialTask:). auto partialTaskParamDecl = new (ctx) ParamDecl( SourceLoc(), SourceLoc(), ctx.Id_partialTask, SourceLoc(), ctx.Id_partialTask, parentDC); partialTaskParamDecl->setInterfaceType(partialTaskType); partialTaskParamDecl->setSpecifier(ParamSpecifier::Default); + // enqueue(partialTask:) method. ParameterList *params = ParameterList::createWithoutLoc(partialTaskParamDecl); auto func = FuncDecl::createImplicit( ctx, StaticSpellingKind::None, getEnqueuePartialTaskName(ctx), @@ -83,12 +235,50 @@ static ValueDecl *deriveActor_enqueuePartialTask(DerivedConformance &derived) { params, TupleType::getEmpty(ctx), parentDC); func->copyFormalAccessFrom(derived.Nominal); func->setBodySynthesizer(deriveBodyActor_enqueuePartialTask); + func->setSynthesized(); // FIXME: This function should be "actor-unsafe", not "actor-independent", but // the latter is all we have at the moment. func->getAttrs().add(new (ctx) ActorIndependentAttr(/*IsImplicit=*/true)); - derived.addMembersToConformanceContext({func}); + // Actor storage property and its initialization. + auto actorStorage = new (ctx) VarDecl( + /*isStatic=*/false, VarDecl::Introducer::Var, SourceLoc(), + ctx.Id_actorStorage, parentDC); + actorStorage->setInterfaceType(defaultActorQueueType); + actorStorage->setImplicit(); + actorStorage->setAccess(AccessLevel::Private); + actorStorage->getAttrs().add(new (ctx) FinalAttr(/*Implicit=*/true)); + + // Pattern binding to initialize the actor storage. + Pattern *actorStoragePattern = NamedPattern::createImplicit( + ctx, actorStorage); + actorStoragePattern = TypedPattern::createImplicit( + ctx, actorStoragePattern, defaultActorQueueType); + + // Initialization expression. + // FIXME: We want the equivalent of type(of: self) here, but we cannot refer + // to self, so for now we use the static type instead. + Type nominalType = derived.Nominal->getDeclaredTypeInContext(); + Expr *metatypeArg = TypeExpr::createImplicit(nominalType, ctx); + Type anyObjectMetatype = ExistentialMetatypeType::get(ctx.getAnyObjectType()); + metatypeArg = ErasureExpr::create(ctx, metatypeArg, anyObjectMetatype, { }); + Expr *actorStorageCreateFnRef = new (ctx) DeclRefExpr( + actorStorageCreateFn, DeclNameLoc(), /*Implicit=*/true); + actorStorageCreateFnRef->setType(actorStorageCreateFn->getInterfaceType()); + + auto actorStorageInit = CallExpr::createImplicit( + ctx, actorStorageCreateFnRef, { metatypeArg}, { Identifier() }); + actorStorageInit->setType(actorStorageCreateFn->getResultInterfaceType()); + actorStorageInit->setThrows(false); + + auto actorStoragePatternBinding = PatternBindingDecl::createImplicit( + ctx, StaticSpellingKind::None, actorStoragePattern, actorStorageInit, + parentDC); + actorStoragePatternBinding->setInitializerChecked(0); + + derived.addMembersToConformanceContext( + { func, actorStorage, actorStoragePatternBinding }); return func; } @@ -97,7 +287,7 @@ ValueDecl *DerivedConformance::deriveActor(ValueDecl *requirement) { if (!func) return nullptr; - if (func->getName() == getEnqueuePartialTaskName(Context)) + if (isEnqueuePartialTask(Context, func->getName())) return deriveActor_enqueuePartialTask(*this); return nullptr; diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 69525ed1881e7..1f550ab32c01d 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -30,6 +30,7 @@ #include "swift/AST/ParameterList.h" #include "swift/AST/Pattern.h" #include "swift/AST/PropertyWrappers.h" +#include "swift/AST/ProtocolConformance.h" #include "swift/AST/SourceFile.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/Types.h" @@ -113,6 +114,21 @@ static void computeLoweredStoredProperties(NominalTypeDecl *decl) { if (var->hasAttachedPropertyWrapper()) (void) var->getPropertyWrapperBackingProperty(); } + + // If this is an actor class, check conformance to the Actor protocol to + // ensure that the actor storage will get created (if needed). + if (auto classDecl = dyn_cast(decl)) { + if (classDecl->isActor()) { + ASTContext &ctx = decl->getASTContext(); + if (auto actorProto = ctx.getProtocol(KnownProtocolKind::Actor)) { + SmallVector conformances; + classDecl->lookupConformance( + decl->getModuleContext(), actorProto, conformances); + for (auto conformance : conformances) + TypeChecker::checkConformance(conformance->getRootNormalConformance()); + } + } + } } ArrayRef @@ -128,10 +144,16 @@ StoredPropertiesRequest::evaluate(Evaluator &evaluator, if (isa(decl->getModuleScopeContext())) computeLoweredStoredProperties(decl); + ASTContext &ctx = decl->getASTContext(); for (auto *member : decl->getMembers()) { if (auto *var = dyn_cast(member)) - if (!var->isStatic() && var->hasStorage()) - results.push_back(var); + if (!var->isStatic() && var->hasStorage()) { + // Actor storage always goes at the beginning. + if (var->getName() == ctx.Id_actorStorage) + results.insert(results.begin(), var); + else + results.push_back(var); + } } return decl->getASTContext().AllocateCopy(results); diff --git a/stdlib/public/Concurrency/Actor.swift b/stdlib/public/Concurrency/Actor.swift index 8f62d14b02dd7..212651ecfb6b3 100644 --- a/stdlib/public/Concurrency/Actor.swift +++ b/stdlib/public/Concurrency/Actor.swift @@ -23,3 +23,33 @@ public protocol Actor: AnyObject { /// Enqueue a new partial task that will be executed in the actor's context. func enqueue(partialTask: PartialAsyncTask) } + +/// A native actor queue, which schedules partial tasks onto a serial queue. +public struct _NativeActorQueue { + // TODO: This is just a stub for now +} + +/// The default type to be used for an actor's queue when an actor does not +/// provide its own implementation of `enqueue(partialTask:)`. +public typealias _DefaultActorQueue = _NativeActorQueue + +/// Called to create a new default actor queue instance for a class of the given +/// type. The implementation will call this within the actor's initializer to +/// initialize the actor queue. +public func _defaultActorQueueCreate( + _ actorClass: AnyObject.Type +) -> _DefaultActorQueue { + _DefaultActorQueue() +} + +/// Called by the synthesized implementation of enqueue(partialTask:). +/// +/// The implementation is provided with the address of the synthesized instance +/// property for the actor queue, so that it need not be at a fixed offset. +public func _defaultActorQueueEnqueuePartialTask( + actor: AnyObject, + queue: inout _DefaultActorQueue, + partialTask: PartialAsyncTask +) { + // TODO: Implement queueing. +} diff --git a/test/SILGen/synthesized_conformance_actor.swift b/test/SILGen/synthesized_conformance_actor.swift new file mode 100644 index 0000000000000..fe0c141e8bdab --- /dev/null +++ b/test/SILGen/synthesized_conformance_actor.swift @@ -0,0 +1,41 @@ +// RUN: %target-swift-frontend -emit-silgen %s -swift-version 5 -enable-experimental-concurrency | %FileCheck -check-prefix CHECK %s +// REQUIRES: concurrency + +import _Concurrency + +public protocol DefaultInit { + init() +} + +public actor class A1 { + var x: Int = 17 + var y: T = T() + + public func f() { } +} + +extension Int: DefaultInit { } + +func buildIt() { + _ = A1() +} + +// variable initialization expression of A1.$__actor_storage +// CHECK-LABEL: sil hidden [transparent] [ossa] @$s29synthesized_conformance_actor2A1C03$__C8_storage33_550E67F1F00BFF89F882603E5B70A41BLL12_Concurrency17_NativeActorQueueVvpfi : $@convention(thin) () -> @out _NativeActorQueue { +// CHECK: bb0([[PROPERTY:%.*]] : $*_NativeActorQueue): +// CHECK-NEXT: [[META:%.*]] = metatype $@thick A1.Type +// CHECK-NEXT: [[ERASED_META:%.*]] = init_existential_metatype [[META]] : $@thick A1.Type, $@thick AnyObject.Type +// CHECK: [[INIT_FN:%.*]] = function_ref @$s12_Concurrency24_defaultActorQueueCreateyAA07_NativecD0VyXlXpF : $@convention(thin) (@thick AnyObject.Type) -> @out _NativeActorQueue +// CHECK-NEXT: = apply [[INIT_FN]]([[PROPERTY]], [[ERASED_META]]) : $@convention(thin) (@thick AnyObject.Type) -> @out _NativeActorQueue + +// A1.enqueue(partialTask:) +// CHECK-LABEL: sil hidden [ossa] @$s29synthesized_conformance_actor2A1C7enqueue11partialTasky12_Concurrency012PartialAsyncG0V_tF : $@convention(method) (@in_guaranteed PartialAsyncTask, @guaranteed A1) -> () { +// CHECK: bb0([[PARTIAL_TASK:%.*]] : $*PartialAsyncTask, [[SELF:%.*]] : @guaranteed $A1): +// CHECK: [[SELF_COPY:%.*]] = copy_value [[SELF]] : $A1 +// CHECK-NEXT: [[SELF_ANY_OBJECT:%.*]] = init_existential_ref [[SELF_COPY]] : $A1 : $A1, $AnyObject +// CHECK-NEXT: [[PROPERTY_REF:%.*]] = ref_element_addr [[SELF]] : $A1, #A1.$__actor_storage +// FIXME: Need to eliminate this exclusivity check. +// CHECK-NEXT: [[DYNAMIC_ACCESS:%.*]] = begin_access [modify] [dynamic] [[PROPERTY_REF]] : $*_NativeActorQueue +// CHECK: [[ENQUEUE_FN:%.*]] = function_ref @$s12_Concurrency36_defaultActorQueueEnqueuePartialTask5actor5queue07partialG0yyXl_AA07_NativecD0VzAA0f5AsyncG0VtF : $@convention(thin) (@guaranteed AnyObject, @inout _NativeActorQueue, @in_guaranteed PartialAsyncTask) -> () +// CHECK-NEXT: apply [[ENQUEUE_FN]]([[SELF_ANY_OBJECT]], [[DYNAMIC_ACCESS]], [[PARTIAL_TASK]]) : $@convention(thin) (@guaranteed AnyObject, @inout _NativeActorQueue, @in_guaranteed PartialAsyncTask) -> () +// CHECK-NEXT: end_access [[DYNAMIC_ACCESS]] : $*_NativeActorQueue diff --git a/test/decl/protocol/special/Actor.swift b/test/decl/protocol/special/Actor.swift index 5bf3c4fd9c6d3..76c8f83b8a6ab 100644 --- a/test/decl/protocol/special/Actor.swift +++ b/test/decl/protocol/special/Actor.swift @@ -1,4 +1,5 @@ // RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// REQUIRES: concurrency // Synthesis of for actor classes. import _Concurrency @@ -25,11 +26,18 @@ actor class A6: A1, Actor { // expected-error{{redundant conformance of 'A6' to // expected-note@-1{{'A6' inherits conformance to protocol 'Actor' from superclass here}} } +// Explicitly satisfying the requirement. + actor class A7 { // Okay: satisfy the requirement explicitly @actorIndependent func enqueue(partialTask: PartialAsyncTask) { } } +// A non-actor class can conform to the Actor protocol, if it does it properly. +class C1: Actor { + func enqueue(partialTask: PartialAsyncTask) { } +} + // Bad actors, that incorrectly try to satisfy the various requirements. // Method that is not usable as a witness. @@ -46,6 +54,9 @@ extension BA2 { @actorIndependent func enqueue(partialTask: PartialAsyncTask) { } } +// No synthesis for non-actor classes. +class C2: Actor { // expected-error{{type 'C2' does not conform to protocol 'Actor'}} +} // Make sure the conformances actually happen. func acceptActor(_: T.Type) { } From 8615904f768b9e313e796cbc1c6e03cb42bbf974 Mon Sep 17 00:00:00 2001 From: Alexey Komnin Date: Tue, 29 Sep 2020 23:02:11 +0300 Subject: [PATCH 114/745] SR-13490: fix tests to align with new import sorting algorithm --- test/IDE/print_ast_overlay.swift | 2 +- .../InterfaceGen/gen_clang_module.swift | 12 +++++------ .../gen_clang_module.swift.response | 20 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/test/IDE/print_ast_overlay.swift b/test/IDE/print_ast_overlay.swift index a2fe49f48b2eb..97b9490cf6b82 100644 --- a/test/IDE/print_ast_overlay.swift +++ b/test/IDE/print_ast_overlay.swift @@ -41,6 +41,6 @@ public class FooOverlayClassDerived : FooOverlayClassBase { // PASS_NO_INTERNAL-NOT: overlay_func_internal -// PASS_ANNOTATED: @_exported import Foo.FooSub // PASS_ANNOTATED: @_exported import Foo +// PASS_ANNOTATED: @_exported import Foo.FooSub // PASS_ANNOTATED: @_exported import FooHelper diff --git a/test/SourceKit/InterfaceGen/gen_clang_module.swift b/test/SourceKit/InterfaceGen/gen_clang_module.swift index d97fc0b498eed..1892ba9c86b02 100644 --- a/test/SourceKit/InterfaceGen/gen_clang_module.swift +++ b/test/SourceKit/InterfaceGen/gen_clang_module.swift @@ -73,21 +73,21 @@ var x: FooClassBase // RUN: %sourcekitd-test -req=interface-gen-open -module Foo -- -I %t.overlays -F %S/../Inputs/libIDE-mock-sdk \ // RUN: -target %target-triple %clang-importer-sdk-nosource -I %t \ -// RUN: == -req=cursor -pos=1:8 == -req=cursor -pos=1:12 \ -// RUN: == -req=cursor -pos=2:10 \ -// RUN: == -req=cursor -pos=3:10 | %FileCheck -check-prefix=CHECK-IMPORT %s +// RUN: == -req=cursor -pos=1:8 \ +// RUN: == -req=cursor -pos=2:8 == -req=cursor -pos=2:12 \ +// RUN: == -req=cursor -pos=3:8 | %FileCheck -check-prefix=CHECK-IMPORT %s // The cursors point to module names inside the imports, see 'gen_clang_module.swift.response' // CHECK-IMPORT: source.lang.swift.ref.module () // CHECK-IMPORT-NEXT: Foo{{$}} // CHECK-IMPORT-NEXT: Foo{{$}} // CHECK-IMPORT: source.lang.swift.ref.module () -// CHECK-IMPORT-NEXT: FooSub{{$}} -// CHECK-IMPORT-NEXT: Foo.FooSub{{$}} -// CHECK-IMPORT: source.lang.swift.ref.module () // CHECK-IMPORT-NEXT: Foo{{$}} // CHECK-IMPORT-NEXT: Foo{{$}} // CHECK-IMPORT: source.lang.swift.ref.module () +// CHECK-IMPORT-NEXT: FooSub{{$}} +// CHECK-IMPORT-NEXT: Foo.FooSub{{$}} +// CHECK-IMPORT: source.lang.swift.ref.module () // CHECK-IMPORT-NEXT: FooHelper{{$}} // CHECK-IMPORT-NEXT: FooHelper{{$}} diff --git a/test/SourceKit/InterfaceGen/gen_clang_module.swift.response b/test/SourceKit/InterfaceGen/gen_clang_module.swift.response index 0927f5c120840..5f2fde5b7f1fc 100644 --- a/test/SourceKit/InterfaceGen/gen_clang_module.swift.response +++ b/test/SourceKit/InterfaceGen/gen_clang_module.swift.response @@ -1,5 +1,5 @@ -import Foo.FooSub import Foo +import Foo.FooSub import FooHelper import SwiftOnoneSupport @@ -370,19 +370,19 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.length: 3 }, { - key.kind: source.lang.swift.syntaxtype.identifier, + key.kind: source.lang.swift.syntaxtype.keyword, key.offset: 11, key.length: 6 }, { - key.kind: source.lang.swift.syntaxtype.keyword, + key.kind: source.lang.swift.syntaxtype.identifier, key.offset: 18, - key.length: 6 + key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 25, - key.length: 3 + key.offset: 22, + key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, @@ -3608,13 +3608,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { }, { key.kind: source.lang.swift.ref.module, - key.offset: 11, - key.length: 6 + key.offset: 18, + key.length: 3 }, { key.kind: source.lang.swift.ref.module, - key.offset: 25, - key.length: 3 + key.offset: 22, + key.length: 6 }, { key.kind: source.lang.swift.ref.module, From d1f43032fcf0b8738e6b05bd7de307d9af0e261c Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 29 Sep 2020 15:29:55 -0500 Subject: [PATCH 115/745] [ownership] Move ownership passed TempLValueOpt for the stdlib and add an ossa test case. --- lib/SILOptimizer/PassManager/PassPipeline.cpp | 6 +- lib/SILOptimizer/Transforms/TempLValueOpt.cpp | 5 +- test/SILOptimizer/templvalueopt_ossa.sil | 251 ++++++++++++++++++ 3 files changed, 257 insertions(+), 5 deletions(-) create mode 100644 test/SILOptimizer/templvalueopt_ossa.sil diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index f93042ad18bcb..8f6fcc0a52e8b 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -283,13 +283,13 @@ void addFunctionPasses(SILPassPipelinePlan &P, // splits up copy_addr. P.addCopyForwarding(); + // Optimize copies from a temporary (an "l-value") to a destination. + P.addTempLValueOpt(); + // We earlier eliminated ownership if we are not compiling the stdlib. Now // handle the stdlib functions. P.addNonTransparentFunctionOwnershipModelEliminator(); - // Optimize copies from a temporary (an "l-value") to a destination. - P.addTempLValueOpt(); - // Split up opaque operations (copy_addr, retain_value, etc.). P.addLowerAggregateInstrs(); diff --git a/lib/SILOptimizer/Transforms/TempLValueOpt.cpp b/lib/SILOptimizer/Transforms/TempLValueOpt.cpp index 8511de4452e75..36303388a2a02 100644 --- a/lib/SILOptimizer/Transforms/TempLValueOpt.cpp +++ b/lib/SILOptimizer/Transforms/TempLValueOpt.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "cow-opts" + #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Analysis/AliasAnalysis.h" #include "swift/SIL/SILFunction.h" @@ -105,8 +106,8 @@ void TempLValueOptPass::run() { } // Do the optimizations. for (CopyAddrInst *copyInst : copyInsts) { - changed |=combineCopyAndDestroy(copyInst); - changed |=tempLValueOpt(copyInst); + changed |= combineCopyAndDestroy(copyInst); + changed |= tempLValueOpt(copyInst); } } diff --git a/test/SILOptimizer/templvalueopt_ossa.sil b/test/SILOptimizer/templvalueopt_ossa.sil new file mode 100644 index 0000000000000..9e0a5562c5103 --- /dev/null +++ b/test/SILOptimizer/templvalueopt_ossa.sil @@ -0,0 +1,251 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -temp-lvalue-opt | %FileCheck %s + +import Swift +import Builtin + +// CHECK-LABEL: sil [ossa] @test_enum_with_initialize +// CHECK: bb0(%0 : $*Optional, %1 : $*Any): +// CHECK-NEXT: [[E:%[0-9]+]] = init_enum_data_addr %0 +// CHECK-NEXT: copy_addr [take] %1 to [initialization] [[E]] +// CHECK-NEXT: inject_enum_addr %0 : $*Optional, #Optional.some!enumelt +// CHECK-NOT: copy_addr +// CHECK: } // end sil function 'test_enum_with_initialize' +sil [ossa] @test_enum_with_initialize : $@convention(thin) (@in Any) -> @out Optional { +bb0(%0 : $*Optional, %1 : $*Any): + %2 = alloc_stack $Optional + %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %1 to [initialization] %3 : $*Any + inject_enum_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %2 to [initialization] %0 : $*Optional + dealloc_stack %2 : $*Optional + %6 = tuple () + return %6 : $() +} + +// CHECK-LABEL: sil [ossa] @test_enum_without_initialize +// CHECK: bb0(%0 : $*Optional, %1 : $*Any): +// CHECK-NEXT: destroy_addr %0 +// CHECK-NEXT: [[E:%[0-9]+]] = init_enum_data_addr %0 +// CHECK-NEXT: copy_addr [take] %1 to [initialization] [[E]] +// CHECK-NEXT: inject_enum_addr %0 : $*Optional, #Optional.some!enumelt +// CHECK-NOT: copy_addr +// CHECK: } // end sil function 'test_enum_without_initialize' +sil [ossa] @test_enum_without_initialize : $@convention(thin) (@inout Optional, @in Any) -> () { +bb0(%0 : $*Optional, %1 : $*Any): + %2 = alloc_stack $Optional + %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %1 to [initialization] %3 : $*Any + inject_enum_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %2 to %0 : $*Optional + dealloc_stack %2 : $*Optional + %6 = tuple () + return %6 : $() +} + +protocol Proto {} + +struct ConformingStruct : Proto { + @_hasStorage let a: Any +} + +// CHECK-LABEL: sil [ossa] @test_existential +// CHECK: bb0(%0 : $*Proto, %1 : $*ConformingStruct): +// CHECK-NEXT: [[E:%[0-9]+]] = init_existential_addr %0 +// CHECK-NEXT: copy_addr [take] %1 to [initialization] [[E]] +// CHECK-NOT: copy_addr +// CHECK: } // end sil function 'test_existential' +sil [ossa] @test_existential : $@convention(thin) (@in ConformingStruct) -> @out Proto { +bb0(%0 : $*Proto, %1 : $*ConformingStruct): + %2 = alloc_stack $Proto + %3 = init_existential_addr %2 : $*Proto, $ConformingStruct + copy_addr [take] %1 to [initialization] %3 : $*ConformingStruct + copy_addr [take] %2 to [initialization] %0 : $*Proto + dealloc_stack %2 : $*Proto + %6 = tuple () + return %6 : $() +} + +enum Either { + case left(Left), right(Right) +} + +public struct TestStruct { + @_hasStorage var e: Either +} + +struct AddressOnlyPayload { + @_hasStorage let a: Any + @_hasStorage let i: Int +} + +// There should only be a single copy_addr after optimization. +// +// CHECK-LABEL: sil [ossa] @test_initialize_struct +// CHECK: bb0(%0 : $*TestStruct, %1 : $*Any, %2 : $Int): +// CHECK-NEXT: [[E:%[0-9]+]] = struct_element_addr %0 +// CHECK-NEXT: [[LEFT:%[0-9]+]] = init_enum_data_addr [[E]] +// CHECK-NEXT: [[A:%[0-9]+]] = struct_element_addr [[LEFT]] +// CHECK-NEXT: copy_addr [take] %1 to [initialization] [[A]] +// CHECK-NEXT: [[I:%[0-9]+]] = struct_element_addr [[LEFT]] +// CHECK-NEXT: store %2 to [trivial] [[I]] +// CHECK-NEXT: inject_enum_addr [[E]] +// CHECK-NOT: copy_addr +// CHECK: } // end sil function 'test_initialize_struct' +sil [ossa] @test_initialize_struct : $@convention(method) (@in Any, Int) -> @out TestStruct { +bb0(%0 : $*TestStruct, %1 : $*Any, %2 : $Int): + %3 = alloc_stack $TestStruct + %4 = alloc_stack $Either + %5 = alloc_stack $AddressOnlyPayload + %6 = struct_element_addr %5 : $*AddressOnlyPayload, #AddressOnlyPayload.a + copy_addr [take] %1 to [initialization] %6 : $*Any + %8 = struct_element_addr %5 : $*AddressOnlyPayload, #AddressOnlyPayload.i + store %2 to [trivial] %8 : $*Int + %10 = init_enum_data_addr %4 : $*Either, #Either.left!enumelt + copy_addr [take] %5 to [initialization] %10 : $*AddressOnlyPayload + inject_enum_addr %4 : $*Either, #Either.left!enumelt + dealloc_stack %5 : $*AddressOnlyPayload + %14 = struct_element_addr %3 : $*TestStruct, #TestStruct.e + copy_addr [take] %4 to [initialization] %14 : $*Either + dealloc_stack %4 : $*Either + copy_addr %3 to [initialization] %0 : $*TestStruct + destroy_addr %3 : $*TestStruct + dealloc_stack %3 : $*TestStruct + %20 = tuple () + return %20 : $() +} + +// CHECK-LABEL: sil [ossa] @bail_on_write_to_dest +// CHECK: alloc_stack +// CHECK: copy_addr +// CHECK: copy_addr +// CHECK: } // end sil function 'bail_on_write_to_dest' +sil [ossa] @bail_on_write_to_dest : $@convention(thin) (@inout Optional, @in Any) -> () { +bb0(%0 : $*Optional, %1 : $*Any): + %2 = alloc_stack $Optional + %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %1 to [initialization] %3 : $*Any + inject_enum_addr %2 : $*Optional, #Optional.some!enumelt + destroy_addr %0 : $*Optional + copy_addr [take] %2 to [initialization] %0 : $*Optional + dealloc_stack %2 : $*Optional + %6 = tuple () + return %6 : $() +} + +// CHECK-LABEL: sil [ossa] @write_to_dest_ok_if_before_liferange +// CHECK: bb0(%0 : $*Optional, %1 : $*Any): +// CHECK-NEXT: destroy_addr +// CHECK-NEXT: init_enum_data_addr +// CHECK-NEXT: copy_addr +// CHECK-NEXT: inject_enum_addr +// CHECK-NOT: copy_addr +// CHECK: } // end sil function 'write_to_dest_ok_if_before_liferange' +sil [ossa] @write_to_dest_ok_if_before_liferange : $@convention(thin) (@inout Optional, @in Any) -> () { +bb0(%0 : $*Optional, %1 : $*Any): + %2 = alloc_stack $Optional + destroy_addr %0 : $*Optional + %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %1 to [initialization] %3 : $*Any + inject_enum_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %2 to [initialization] %0 : $*Optional + dealloc_stack %2 : $*Optional + %6 = tuple () + return %6 : $() +} + +enum Enum { + case A(Optional), B +} + +struct StructWithEnum : Proto { + @_hasStorage let e: Enum +} + +// CHECK-LABEL: sil [ossa] @move_projections +// CHECK: bb0(%0 : $*Proto, %1 : $*Any): +// CHECK-NEXT: [[S:%[0-9]+]] = init_existential_addr %0 : $*Proto, $StructWithEnum +// CHECK-NEXT: [[E:%[0-9]+]] = struct_element_addr [[S]] : $*StructWithEnum, #StructWithEnum.e +// CHECK-NEXT: [[ENUMA:%[0-9]+]] = init_enum_data_addr [[E]] : $*Enum, #Enum.A!enumelt +// CHECK-NEXT: [[OPTIONAL:%[0-9]+]] = init_enum_data_addr [[ENUMA]] : $*Optional, #Optional.some!enumelt +// CHECK-NEXT: copy_addr [take] %1 to [initialization] [[OPTIONAL]] : $*Any +// CHECK-NEXT: inject_enum_addr [[ENUMA]] : $*Optional, #Optional.some!enumelt +// CHECK-NEXT: inject_enum_addr [[E]] : $*Enum, #Enum.A!enumelt +// CHECK-NOT: copy_addr +// CHECK: } // end sil function 'move_projections' +sil [ossa] @move_projections : $@convention(thin) (@in Any) -> @out Proto { +bb0(%0 : $*Proto, %1 : $*Any): + %2 = alloc_stack $Optional + %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %1 to [initialization] %3 : $*Any + inject_enum_addr %2 : $*Optional, #Optional.some!enumelt + %4 = init_existential_addr %0 : $*Proto, $StructWithEnum + %5 = struct_element_addr %4 : $*StructWithEnum, #StructWithEnum.e + %6 = init_enum_data_addr %5 : $*Enum, #Enum.A!enumelt + copy_addr [take] %2 to [initialization] %6 : $*Optional + inject_enum_addr %5 : $*Enum, #Enum.A!enumelt + dealloc_stack %2 : $*Optional + %10 = tuple () + return %10 : $() +} + +// CHECK-LABEL: sil [ossa] @cant_move_projections +// CHECK: alloc_stack +// CHECK: copy_addr +// CHECK: load +// CHECK: copy_addr +// CHECK: } // end sil function 'cant_move_projections' +sil [ossa] @cant_move_projections : $@convention(thin) (@in Any, @in_guaranteed Builtin.RawPointer) -> () { +bb0(%0 : $*Any, %1 : $*Builtin.RawPointer): + %2 = alloc_stack $Optional + %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %0 to [initialization] %3 : $*Any + inject_enum_addr %2 : $*Optional, #Optional.some!enumelt + %4 = load [trivial] %1 : $*Builtin.RawPointer + %5 = pointer_to_address %4 : $Builtin.RawPointer to $*Optional + copy_addr [take] %2 to [initialization] %5 : $*Optional + dealloc_stack %2 : $*Optional + %10 = tuple () + return %10 : $() +} + +sil [ossa] @init_optional : $@convention(thin) () -> @out Optional + +// CHECK-LABEL: sil [ossa] @instructions_after_copy_addr +// CHECK: alloc_stack +// CHECK: copy_addr +// CHECK: copy_addr +// CHECK: apply +// CHECK: } // end sil function 'instructions_after_copy_addr' +sil [ossa] @instructions_after_copy_addr : $@convention(thin) (@in Any) -> @out Optional { +bb0(%0 : $*Optional, %1 : $*Any): + %2 = alloc_stack $Optional + %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %1 to [initialization] %3 : $*Any + inject_enum_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %2 to [initialization] %0 : $*Optional + %4 = function_ref @init_optional : $@convention(thin) () -> @out Optional + %5 = apply %4(%2) : $@convention(thin) () -> @out Optional + destroy_addr %2 : $*Optional + dealloc_stack %2 : $*Optional + %6 = tuple () + return %6 : $() +} + +// CHECK-LABEL: sil [ossa] @dont_optimize_swap +// CHECK: alloc_stack +// CHECK: copy_addr +// CHECK: copy_addr +// CHECK: copy_addr +// CHECK: dealloc_stack +// CHECK: } // end sil function 'dont_optimize_swap' +sil [ossa] @dont_optimize_swap : $@convention(thin) (@inout T, @inout T) -> () { +bb0(%0 : $*T, %1 : $*T): + %2 = alloc_stack $T + copy_addr [take] %0 to [initialization] %2 : $*T + copy_addr [take] %1 to [initialization] %0 : $*T + copy_addr [take] %2 to [initialization] %1 : $*T + dealloc_stack %2 : $*T + %78 = tuple () + return %78 : $() +} + From aeef419de894d646132ce1a11fca61c8d8d9e536 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 15:25:49 -0700 Subject: [PATCH 116/745] [Concurrency] Ensure that enqueue(partialTask:) is first in actor vtables. Actor classes never have non-actor superclasses, so we can ensure that all actor classes have a common vtable prefix for the `enqueue(partialTask:)` operation. This allows us to treat all actor classes uniformly, without having to go through the Actor witness table every time. --- include/swift/AST/Decl.h | 8 +++ lib/AST/Decl.cpp | 50 +++++++++++++++++++ lib/Sema/DerivedConformanceActor.cpp | 2 +- lib/Sema/DerivedConformances.cpp | 2 +- lib/Sema/TypeCheckConcurrency.cpp | 9 ---- lib/Sema/TypeCheckConcurrency.h | 4 -- lib/Sema/TypeCheckDecl.cpp | 9 ++++ lib/Sema/TypeCheckProtocol.cpp | 8 +-- .../synthesized_conformance_actor.swift | 30 +++++++---- 9 files changed, 95 insertions(+), 27 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index b615c7348e393..c2b01e73e78cd 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -6329,6 +6329,14 @@ class FuncDecl : public AbstractFunctionDecl { bool isMainTypeMainMethod() const; + /// Whether the given name is enqueue(partialTask:), which is used for + /// actors. + static bool isEnqueuePartialTaskName(ASTContext &ctx, DeclName name); + + /// Determine whether this function is the witness to the Actor protocol's + /// enqueue(partialTask:) operation within an actor. + bool isActorEnqueuePartialTaskWitness() const; + SelfAccessKind getSelfAccessKind() const; void setSelfAccessKind(SelfAccessKind mod) { diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 99b035da4e37a..8cabb3003ce76 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -7508,6 +7508,56 @@ bool FuncDecl::isMainTypeMainMethod() const { getParameters()->size() == 0; } +bool FuncDecl::isEnqueuePartialTaskName(ASTContext &ctx, DeclName name) { + if (name.isCompoundName() && name.getBaseName() == ctx.Id_enqueue) { + auto argumentNames = name.getArgumentNames(); + return argumentNames.size() == 1 && argumentNames[0] == ctx.Id_partialTask; + } + + return false; +} + +bool FuncDecl::isActorEnqueuePartialTaskWitness() const { + if (!isEnqueuePartialTaskName(getASTContext(), getName())) + return false; + + auto classDecl = getDeclContext()->getSelfClassDecl(); + if (!classDecl) + return false; + + if (!classDecl->isActor()) + return false; + + ASTContext &ctx = getASTContext(); + auto actorProto = ctx.getProtocol(KnownProtocolKind::Actor); + if (!actorProto) + return false; + + FuncDecl *requirement = nullptr; + for (auto protoMember : actorProto->getParsedMembers()) { + if (auto protoFunc = dyn_cast(protoMember)) { + if (isEnqueuePartialTaskName(ctx, protoFunc->getName())) { + requirement = protoFunc; + break; + } + } + } + + if (!requirement) + return false; + + SmallVector conformances; + classDecl->lookupConformance( + classDecl->getModuleContext(), actorProto, conformances); + for (auto conformance : conformances) { + auto witness = conformance->getWitnessDecl(requirement); + if (witness == this) + return true; + } + + return false; +} + ConstructorDecl::ConstructorDecl(DeclName Name, SourceLoc ConstructorLoc, bool Failable, SourceLoc FailabilityLoc, bool Throws, diff --git a/lib/Sema/DerivedConformanceActor.cpp b/lib/Sema/DerivedConformanceActor.cpp index 92f2e2f89021c..27b26bfecc0d9 100644 --- a/lib/Sema/DerivedConformanceActor.cpp +++ b/lib/Sema/DerivedConformanceActor.cpp @@ -287,7 +287,7 @@ ValueDecl *DerivedConformance::deriveActor(ValueDecl *requirement) { if (!func) return nullptr; - if (isEnqueuePartialTask(Context, func->getName())) + if (FuncDecl::isEnqueuePartialTaskName(Context, func->getName())) return deriveActor_enqueuePartialTask(*this); return nullptr; diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index f82cf58c39acb..c4dd45728b060 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -365,7 +365,7 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, } // Actor.enqueue(partialTask: PartialTask) - if (isEnqueuePartialTask(ctx, name)) { + if (FuncDecl::isEnqueuePartialTaskName(ctx, name)) { return getRequirement(KnownProtocolKind::Actor); } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 597354a2c3d00..b5c19ab93c570 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -749,12 +749,3 @@ ActorIsolation swift::getActorIsolation(ValueDecl *value) { ctx.evaluator, ActorIsolationRequest{value}, ActorIsolation::forUnspecified()); } - -bool swift::isEnqueuePartialTask(ASTContext &ctx, DeclName name) { - if (name.isCompoundName() && name.getBaseName() == ctx.Id_enqueue) { - auto argumentNames = name.getArgumentNames(); - return argumentNames.size() == 1 && argumentNames[0] == ctx.Id_partialTask; - } - - return false; -} diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index b1ecd19fec9ed..b2ec4f05784ec 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -23,7 +23,6 @@ class ActorIsolation; class ASTContext; class ClassDecl; class DeclContext; -class DeclName; class Expr; class FuncDecl; class ValueDecl; @@ -38,9 +37,6 @@ void checkActorIsolation(const Expr *expr, const DeclContext *dc); /// Determine how the given value declaration is isolated. ActorIsolation getActorIsolation(ValueDecl *value); -/// Whether the given declaration name is enqueue(partialTask:). -bool isEnqueuePartialTask(ASTContext &ctx, DeclName name); - } // end namespace swift #endif /* SWIFT_SEMA_TYPECHECKCONCURRENCY_H */ diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 3a1f7aaf921af..dfa0aeaa3a639 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2704,6 +2704,15 @@ SemanticMembersRequest::evaluate(Evaluator &evaluator, for (auto *member : idc->getMembers()) { if (auto *afd = dyn_cast(member)) { + // If this is a witness to Actor.enqueue(partialTask:), put it at the + // beginning of the vtable. + if (auto func = dyn_cast(afd)) { + if (func->isActorEnqueuePartialTaskWitness()) { + result.insert(result.begin(), func); + continue; + } + } + // Add synthesized members to a side table and sort them by their mangled // name, since they could have been added to the class in any order. if (afd->isSynthesized()) { diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 0b37eeb01549e..dc0c1c182f14a 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4219,10 +4219,12 @@ void ConformanceChecker::resolveValueWitnesses() { auto &C = witness->getASTContext(); // Ensure that Actor.enqueue(partialTask:) is implemented within the - // class itself. - if (isEnqueuePartialTask(C, requirement->getName()) && + // actor class itself. + if (FuncDecl::isEnqueuePartialTaskName(C, requirement->getName()) && Proto->isSpecificProtocol(KnownProtocolKind::Actor) && - DC != witness->getDeclContext()) { + DC != witness->getDeclContext() && + Adoptee->getClassOrBoundGenericClass() && + Adoptee->getClassOrBoundGenericClass()->isActor()) { witness->diagnose(diag::enqueue_partial_task_not_in_context, Adoptee); return; } diff --git a/test/SILGen/synthesized_conformance_actor.swift b/test/SILGen/synthesized_conformance_actor.swift index fe0c141e8bdab..365b3fee56e92 100644 --- a/test/SILGen/synthesized_conformance_actor.swift +++ b/test/SILGen/synthesized_conformance_actor.swift @@ -16,20 +16,17 @@ public actor class A1 { extension Int: DefaultInit { } +public actor class A2 { + func f() { } + @actorIndependent public func enqueue(partialTask: PartialAsyncTask) { } +} + func buildIt() { _ = A1() } -// variable initialization expression of A1.$__actor_storage -// CHECK-LABEL: sil hidden [transparent] [ossa] @$s29synthesized_conformance_actor2A1C03$__C8_storage33_550E67F1F00BFF89F882603E5B70A41BLL12_Concurrency17_NativeActorQueueVvpfi : $@convention(thin) () -> @out _NativeActorQueue { -// CHECK: bb0([[PROPERTY:%.*]] : $*_NativeActorQueue): -// CHECK-NEXT: [[META:%.*]] = metatype $@thick A1.Type -// CHECK-NEXT: [[ERASED_META:%.*]] = init_existential_metatype [[META]] : $@thick A1.Type, $@thick AnyObject.Type -// CHECK: [[INIT_FN:%.*]] = function_ref @$s12_Concurrency24_defaultActorQueueCreateyAA07_NativecD0VyXlXpF : $@convention(thin) (@thick AnyObject.Type) -> @out _NativeActorQueue -// CHECK-NEXT: = apply [[INIT_FN]]([[PROPERTY]], [[ERASED_META]]) : $@convention(thin) (@thick AnyObject.Type) -> @out _NativeActorQueue - // A1.enqueue(partialTask:) -// CHECK-LABEL: sil hidden [ossa] @$s29synthesized_conformance_actor2A1C7enqueue11partialTasky12_Concurrency012PartialAsyncG0V_tF : $@convention(method) (@in_guaranteed PartialAsyncTask, @guaranteed A1) -> () { +// CHECK-LABEL: sil [ossa] @$s29synthesized_conformance_actor2A1C7enqueue11partialTasky12_Concurrency012PartialAsyncG0V_tF : $@convention(method) (@in_guaranteed PartialAsyncTask, @guaranteed A1) -> () { // CHECK: bb0([[PARTIAL_TASK:%.*]] : $*PartialAsyncTask, [[SELF:%.*]] : @guaranteed $A1): // CHECK: [[SELF_COPY:%.*]] = copy_value [[SELF]] : $A1 // CHECK-NEXT: [[SELF_ANY_OBJECT:%.*]] = init_existential_ref [[SELF_COPY]] : $A1 : $A1, $AnyObject @@ -39,3 +36,18 @@ func buildIt() { // CHECK: [[ENQUEUE_FN:%.*]] = function_ref @$s12_Concurrency36_defaultActorQueueEnqueuePartialTask5actor5queue07partialG0yyXl_AA07_NativecD0VzAA0f5AsyncG0VtF : $@convention(thin) (@guaranteed AnyObject, @inout _NativeActorQueue, @in_guaranteed PartialAsyncTask) -> () // CHECK-NEXT: apply [[ENQUEUE_FN]]([[SELF_ANY_OBJECT]], [[DYNAMIC_ACCESS]], [[PARTIAL_TASK]]) : $@convention(thin) (@guaranteed AnyObject, @inout _NativeActorQueue, @in_guaranteed PartialAsyncTask) -> () // CHECK-NEXT: end_access [[DYNAMIC_ACCESS]] : $*_NativeActorQueue + +// variable initialization expression of A1.$__actor_storage +// CHECK-LABEL: sil [transparent] [ossa] @$s29synthesized_conformance_actor2A1C03$__C8_storage33{{.*}}12_Concurrency17_NativeActorQueueVvpfi : $@convention(thin) () -> @out _NativeActorQueue { +// CHECK: bb0([[PROPERTY:%.*]] : $*_NativeActorQueue): +// CHECK-NEXT: [[META:%.*]] = metatype $@thick A1.Type +// CHECK-NEXT: [[ERASED_META:%.*]] = init_existential_metatype [[META]] : $@thick A1.Type, $@thick AnyObject.Type +// CHECK: [[INIT_FN:%.*]] = function_ref @$s12_Concurrency24_defaultActorQueueCreateyAA07_NativecD0VyXlXpF : $@convention(thin) (@thick AnyObject.Type) -> @out _NativeActorQueue +// CHECK-NEXT: = apply [[INIT_FN]]([[PROPERTY]], [[ERASED_META]]) : $@convention(thin) (@thick AnyObject.Type) -> @out _NativeActorQueue + +// Ensure that enqueue(partialTask:) is the first slot in the vtable. +// CHECK-LABEL: sil_vtable [serialized] A1 { +// CHECK-NEXT: #A1.enqueue: (A1) -> (PartialAsyncTask) -> () : @$s29synthesized_conformance_actor2A1C7enqueue11partialTasky12_Concurrency012PartialAsyncG0V_tF + +// CHECK-LABEL: sil_vtable [serialized] A2 { +// CHECK-NEXT: #A2.enqueue: (A2) -> (PartialAsyncTask) -> () : @$s29synthesized_conformance_actor2A2C7enqueue11partialTasky12_Concurrency012PartialAsyncG0V_tF // A2.enqueue(partialTask:) From 445d74762264a5ecd41897caaf31b0d1922129aa Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 29 Sep 2020 19:06:40 -0400 Subject: [PATCH 117/745] AST: Move GenericParamList and friends to GenericParamList.{h,cpp} --- include/swift/AST/Decl.h | 360 +----------------- include/swift/AST/GenericEnvironment.h | 3 +- include/swift/AST/GenericParamList.h | 392 ++++++++++++++++++++ include/swift/AST/GenericSignature.h | 1 + include/swift/AST/GenericSignatureBuilder.h | 1 + include/swift/AST/TypeAlignments.h | 23 +- include/swift/AST/TypeCheckRequests.h | 1 + include/swift/Parse/Parser.h | 1 + lib/AST/ASTDumper.cpp | 1 + lib/AST/ASTScopeCreation.cpp | 1 + lib/AST/ASTScopeLookup.cpp | 1 + lib/AST/ASTScopePrinting.cpp | 1 + lib/AST/ASTScopeSourceRange.cpp | 1 + lib/AST/ASTWalker.cpp | 1 + lib/AST/CMakeLists.txt | 1 + lib/AST/ConformanceLookupTable.cpp | 1 + lib/AST/Decl.cpp | 109 ------ lib/AST/GenericParamList.cpp | 129 +++++++ lib/AST/GenericSignatureBuilder.cpp | 1 + lib/AST/NameLookup.cpp | 1 + lib/AST/NameLookupRequests.cpp | 1 + lib/AST/TypeRepr.cpp | 1 + lib/AST/USRGeneration.cpp | 1 + lib/IDE/Formatting.cpp | 1 + lib/Parse/ParseDecl.cpp | 1 + lib/Parse/ParseGeneric.cpp | 1 + lib/Parse/ParseType.cpp | 1 + lib/Sema/TypeChecker.h | 1 + unittests/AST/TestContext.cpp | 1 + 29 files changed, 569 insertions(+), 470 deletions(-) create mode 100644 include/swift/AST/GenericParamList.h create mode 100644 lib/AST/GenericParamList.cpp diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 588ef350f497d..0b9800d0a6b68 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -70,6 +70,7 @@ namespace swift { class BraceStmt; class DeclAttributes; class GenericContext; + class GenericParamList; class GenericSignature; class GenericTypeParamDecl; class GenericTypeParamType; @@ -88,6 +89,7 @@ namespace swift { class ProtocolType; struct RawComment; enum class ResilienceExpansion : unsigned; + class TrailingWhereClause; class TypeAliasDecl; class Stmt; class SubscriptDecl; @@ -1011,364 +1013,6 @@ void *allocateMemoryForDecl(AllocatorTy &allocator, size_t baseSize, return mem; } -enum class RequirementReprKind : unsigned { - /// A type bound T : P, where T is a type that depends on a generic - /// parameter and P is some type that should bound T, either as a concrete - /// supertype or a protocol to which T must conform. - TypeConstraint, - - /// A same-type requirement T == U, where T and U are types that shall be - /// equivalent. - SameType, - - /// A layout bound T : L, where T is a type that depends on a generic - /// parameter and L is some layout specification that should bound T. - LayoutConstraint, - - // Note: there is code that packs this enum in a 2-bit bitfield. Audit users - // when adding enumerators. -}; - -/// A single requirement in a 'where' clause, which places additional -/// restrictions on the generic parameters or associated types of a generic -/// function, type, or protocol. -/// -/// This always represents a requirement spelled in the source code. It is -/// never generated implicitly. -/// -/// \c GenericParamList assumes these are POD-like. -class RequirementRepr { - SourceLoc SeparatorLoc; - RequirementReprKind Kind : 2; - bool Invalid : 1; - TypeRepr *FirstType; - - /// The second element represents the right-hand side of the constraint. - /// It can be e.g. a type or a layout constraint. - union { - TypeRepr *SecondType; - LayoutConstraintLoc SecondLayout; - }; - - /// Set during deserialization; used to print out the requirements accurately - /// for the generated interface. - StringRef AsWrittenString; - - RequirementRepr(SourceLoc SeparatorLoc, RequirementReprKind Kind, - TypeRepr *FirstType, TypeRepr *SecondType) - : SeparatorLoc(SeparatorLoc), Kind(Kind), Invalid(false), - FirstType(FirstType), SecondType(SecondType) { } - - RequirementRepr(SourceLoc SeparatorLoc, RequirementReprKind Kind, - TypeRepr *FirstType, LayoutConstraintLoc SecondLayout) - : SeparatorLoc(SeparatorLoc), Kind(Kind), Invalid(false), - FirstType(FirstType), SecondLayout(SecondLayout) { } - - void printImpl(ASTPrinter &OS) const; - -public: - /// Construct a new type-constraint requirement. - /// - /// \param Subject The type that must conform to the given protocol or - /// composition, or be a subclass of the given class type. - /// \param ColonLoc The location of the ':', or an invalid location if - /// this requirement was implied. - /// \param Constraint The protocol or protocol composition to which the - /// subject must conform, or superclass from which the subject must inherit. - static RequirementRepr getTypeConstraint(TypeRepr *Subject, - SourceLoc ColonLoc, - TypeRepr *Constraint) { - return { ColonLoc, RequirementReprKind::TypeConstraint, Subject, Constraint }; - } - - /// Construct a new same-type requirement. - /// - /// \param FirstType The first type. - /// \param EqualLoc The location of the '==' in the same-type constraint, or - /// an invalid location if this requirement was implied. - /// \param SecondType The second type. - static RequirementRepr getSameType(TypeRepr *FirstType, - SourceLoc EqualLoc, - TypeRepr *SecondType) { - return { EqualLoc, RequirementReprKind::SameType, FirstType, SecondType }; - } - - /// Construct a new layout-constraint requirement. - /// - /// \param Subject The type that must conform to the given layout - /// requirement. - /// \param ColonLoc The location of the ':', or an invalid location if - /// this requirement was implied. - /// \param Layout The layout requirement to which the - /// subject must conform. - static RequirementRepr getLayoutConstraint(TypeRepr *Subject, - SourceLoc ColonLoc, - LayoutConstraintLoc Layout) { - return {ColonLoc, RequirementReprKind::LayoutConstraint, Subject, - Layout}; - } - - /// Determine the kind of requirement - RequirementReprKind getKind() const { return Kind; } - - /// Determine whether this requirement is invalid. - bool isInvalid() const { return Invalid; } - - /// Mark this requirement invalid. - void setInvalid() { Invalid = true; } - - /// For a type-bound requirement, return the subject of the - /// conformance relationship. - TypeRepr *getSubjectRepr() const { - assert(getKind() == RequirementReprKind::TypeConstraint || - getKind() == RequirementReprKind::LayoutConstraint); - return FirstType; - } - - /// For a type-bound requirement, return the protocol or to which - /// the subject conforms or superclass it inherits. - TypeRepr *getConstraintRepr() const { - assert(getKind() == RequirementReprKind::TypeConstraint); - return SecondType; - } - - LayoutConstraint getLayoutConstraint() const { - assert(getKind() == RequirementReprKind::LayoutConstraint); - return SecondLayout.getLayoutConstraint(); - } - - LayoutConstraintLoc &getLayoutConstraintLoc() { - assert(getKind() == RequirementReprKind::LayoutConstraint); - return SecondLayout; - } - - const LayoutConstraintLoc &getLayoutConstraintLoc() const { - assert(getKind() == RequirementReprKind::LayoutConstraint); - return SecondLayout; - } - - /// Retrieve the first type of a same-type requirement. - TypeRepr *getFirstTypeRepr() const { - assert(getKind() == RequirementReprKind::SameType); - return FirstType; - } - - /// Retrieve the second type of a same-type requirement. - TypeRepr *getSecondTypeRepr() const { - assert(getKind() == RequirementReprKind::SameType); - return SecondType; - } - - /// Retrieve the location of the ':' or '==' in an explicitly-written - /// conformance or same-type requirement respectively. - SourceLoc getSeparatorLoc() const { - return SeparatorLoc; - } - - SourceRange getSourceRange() const; - - /// Retrieve the first or subject type representation from the \c repr, - /// or \c nullptr if \c repr is null. - static TypeRepr *getFirstTypeRepr(const RequirementRepr *repr) { - if (!repr) return nullptr; - return repr->FirstType; - } - - /// Retrieve the second or constraint type representation from the \c repr, - /// or \c nullptr if \c repr is null. - static TypeRepr *getSecondTypeRepr(const RequirementRepr *repr) { - if (!repr) return nullptr; - assert(repr->getKind() == RequirementReprKind::TypeConstraint || - repr->getKind() == RequirementReprKind::SameType); - return repr->SecondType; - } - - SWIFT_DEBUG_DUMP; - void print(raw_ostream &OS) const; - void print(ASTPrinter &Printer) const; -}; - -using GenericParamSource = PointerUnion; - -/// GenericParamList - A list of generic parameters that is part of a generic -/// function or type, along with extra requirements placed on those generic -/// parameters and types derived from them. -class GenericParamList final : - private llvm::TrailingObjects { - friend TrailingObjects; - - SourceRange Brackets; - unsigned NumParams; - SourceLoc WhereLoc; - MutableArrayRef Requirements; - - GenericParamList *OuterParameters; - - GenericParamList(SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc WhereLoc, - MutableArrayRef Requirements, - SourceLoc RAngleLoc); - - // Don't copy. - GenericParamList(const GenericParamList &) = delete; - GenericParamList &operator=(const GenericParamList &) = delete; - -public: - /// create - Create a new generic parameter list within the given AST context. - /// - /// \param Context The ASTContext in which the generic parameter list will - /// be allocated. - /// \param LAngleLoc The location of the opening angle bracket ('<') - /// \param Params The list of generic parameters, which will be copied into - /// ASTContext-allocated memory. - /// \param RAngleLoc The location of the closing angle bracket ('>') - static GenericParamList *create(ASTContext &Context, - SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc RAngleLoc); - - /// create - Create a new generic parameter list and "where" clause within - /// the given AST context. - /// - /// \param Context The ASTContext in which the generic parameter list will - /// be allocated. - /// \param LAngleLoc The location of the opening angle bracket ('<') - /// \param Params The list of generic parameters, which will be copied into - /// ASTContext-allocated memory. - /// \param WhereLoc The location of the 'where' keyword, if any. - /// \param Requirements The list of requirements, which will be copied into - /// ASTContext-allocated memory. - /// \param RAngleLoc The location of the closing angle bracket ('>') - static GenericParamList *create(const ASTContext &Context, - SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc WhereLoc, - ArrayRef Requirements, - SourceLoc RAngleLoc); - - MutableArrayRef getParams() { - return {getTrailingObjects(), NumParams}; - } - - ArrayRef getParams() const { - return {getTrailingObjects(), NumParams}; - } - - using iterator = GenericTypeParamDecl **; - using const_iterator = const GenericTypeParamDecl * const *; - - unsigned size() const { return NumParams; } - iterator begin() { return getParams().begin(); } - iterator end() { return getParams().end(); } - const_iterator begin() const { return getParams().begin(); } - const_iterator end() const { return getParams().end(); } - - /// Retrieve the location of the 'where' keyword, or an invalid - /// location if 'where' was not present. - SourceLoc getWhereLoc() const { return WhereLoc; } - - /// Retrieve the set of additional requirements placed on these - /// generic parameters and types derived from them. - /// - /// This list may contain both explicitly-written requirements as well as - /// implicitly-generated requirements, and may be non-empty even if no - /// 'where' keyword is present. - MutableArrayRef getRequirements() { return Requirements; } - - /// Retrieve the set of additional requirements placed on these - /// generic parameters and types derived from them. - /// - /// This list may contain both explicitly-written requirements as well as - /// implicitly-generated requirements, and may be non-empty even if no - /// 'where' keyword is present. - ArrayRef getRequirements() const { return Requirements; } - - /// Retrieve the outer generic parameter list. - /// - /// This is used for extensions of nested types, and in SIL mode, where a - /// single lexical context can have multiple logical generic parameter - /// lists. - GenericParamList *getOuterParameters() const { return OuterParameters; } - - /// Set the outer generic parameter list. See \c getOuterParameters - /// for more information. - void setOuterParameters(GenericParamList *Outer) { OuterParameters = Outer; } - - void setDeclContext(DeclContext *dc); - - SourceLoc getLAngleLoc() const { return Brackets.Start; } - SourceLoc getRAngleLoc() const { return Brackets.End; } - - SourceRange getSourceRange() const { return Brackets; } - - /// Retrieve the source range covering the where clause. - SourceRange getWhereClauseSourceRange() const { - if (WhereLoc.isInvalid()) - return SourceRange(); - - auto endLoc = Requirements.back().getSourceRange().End; - return SourceRange(WhereLoc, endLoc); - } - - /// Configure the depth of the generic parameters in this list. - void setDepth(unsigned depth); - - /// Create a copy of the generic parameter list and all of its generic - /// parameter declarations. The copied generic parameters are re-parented - /// to the given DeclContext. - GenericParamList *clone(DeclContext *dc) const; - - void print(raw_ostream &OS) const; - SWIFT_DEBUG_DUMP; - - bool walk(ASTWalker &walker); - - /// Finds a generic parameter declaration by name. This should only - /// be used from the SIL parser. - GenericTypeParamDecl *lookUpGenericParam(Identifier name) const; -}; - -/// A trailing where clause. -class alignas(RequirementRepr) TrailingWhereClause final : - private llvm::TrailingObjects { - friend TrailingObjects; - - SourceLoc WhereLoc; - - /// The number of requirements. The actual requirements are tail-allocated. - unsigned NumRequirements; - - TrailingWhereClause(SourceLoc whereLoc, - ArrayRef requirements); - -public: - /// Create a new trailing where clause with the given set of requirements. - static TrailingWhereClause *create(ASTContext &ctx, SourceLoc whereLoc, - ArrayRef requirements); - - /// Retrieve the location of the 'where' keyword. - SourceLoc getWhereLoc() const { return WhereLoc; } - - /// Retrieve the set of requirements. - MutableArrayRef getRequirements() { - return {getTrailingObjects(), NumRequirements}; - } - - /// Retrieve the set of requirements. - ArrayRef getRequirements() const { - return {getTrailingObjects(), NumRequirements}; - } - - /// Compute the source range containing this trailing where clause. - SourceRange getSourceRange() const { - return SourceRange(WhereLoc, - getRequirements().back().getSourceRange().End); - } - - void print(llvm::raw_ostream &OS, bool printWhereKeyword) const; -}; - // A private class for forcing exact field layout. class alignas(8) _GenericContext { // Not really public. See GenericContext. diff --git a/include/swift/AST/GenericEnvironment.h b/include/swift/AST/GenericEnvironment.h index a47067b940fa7..3c269644ea980 100644 --- a/include/swift/AST/GenericEnvironment.h +++ b/include/swift/AST/GenericEnvironment.h @@ -18,8 +18,9 @@ #define SWIFT_AST_GENERIC_ENVIRONMENT_H #include "swift/AST/SubstitutionMap.h" -#include "swift/AST/GenericSignature.h" #include "swift/AST/GenericParamKey.h" +#include "swift/AST/GenericParamList.h" +#include "swift/AST/GenericSignature.h" #include "swift/Basic/Compiler.h" #include "swift/Basic/Debug.h" #include "llvm/ADT/ArrayRef.h" diff --git a/include/swift/AST/GenericParamList.h b/include/swift/AST/GenericParamList.h new file mode 100644 index 0000000000000..2d3b8c34475c3 --- /dev/null +++ b/include/swift/AST/GenericParamList.h @@ -0,0 +1,392 @@ +//===--- GenericParamList.h - Generic parameter list AST --------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines the GenericParamList class, and related classes. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_GENERIC_PARAM_LIST_H +#define SWIFT_GENERIC_PARAM_LIST_H + +#include "swift/AST/Decl.h" +#include "swift/AST/LayoutConstraint.h" +#include "swift/Basic/SourceLoc.h" +#include "llvm/ADT/StringRef.h" + +namespace swift { + +class ASTPrinter; +class TypeRepr; + +enum class RequirementReprKind : unsigned { + /// A type bound T : P, where T is a type that depends on a generic + /// parameter and P is some type that should bound T, either as a concrete + /// supertype or a protocol to which T must conform. + TypeConstraint, + + /// A same-type requirement T == U, where T and U are types that shall be + /// equivalent. + SameType, + + /// A layout bound T : L, where T is a type that depends on a generic + /// parameter and L is some layout specification that should bound T. + LayoutConstraint, + + // Note: there is code that packs this enum in a 2-bit bitfield. Audit users + // when adding enumerators. +}; + +/// A single requirement in a 'where' clause, which places additional +/// restrictions on the generic parameters or associated types of a generic +/// function, type, or protocol. +/// +/// This always represents a requirement spelled in the source code. It is +/// never generated implicitly. +/// +/// \c GenericParamList assumes these are POD-like. + +class RequirementRepr { + SourceLoc SeparatorLoc; + RequirementReprKind Kind : 2; + bool Invalid : 1; + TypeRepr *FirstType; + + /// The second element represents the right-hand side of the constraint. + /// It can be e.g. a type or a layout constraint. + union { + TypeRepr *SecondType; + LayoutConstraintLoc SecondLayout; + }; + + /// Set during deserialization; used to print out the requirements accurately + /// for the generated interface. + StringRef AsWrittenString; + + RequirementRepr(SourceLoc SeparatorLoc, RequirementReprKind Kind, + TypeRepr *FirstType, TypeRepr *SecondType) + : SeparatorLoc(SeparatorLoc), Kind(Kind), Invalid(false), + FirstType(FirstType), SecondType(SecondType) { } + + RequirementRepr(SourceLoc SeparatorLoc, RequirementReprKind Kind, + TypeRepr *FirstType, LayoutConstraintLoc SecondLayout) + : SeparatorLoc(SeparatorLoc), Kind(Kind), Invalid(false), + FirstType(FirstType), SecondLayout(SecondLayout) { } + + void printImpl(ASTPrinter &OS) const; + +public: + /// Construct a new type-constraint requirement. + /// + /// \param Subject The type that must conform to the given protocol or + /// composition, or be a subclass of the given class type. + /// \param ColonLoc The location of the ':', or an invalid location if + /// this requirement was implied. + /// \param Constraint The protocol or protocol composition to which the + /// subject must conform, or superclass from which the subject must inherit. + static RequirementRepr getTypeConstraint(TypeRepr *Subject, + SourceLoc ColonLoc, + TypeRepr *Constraint) { + return { ColonLoc, RequirementReprKind::TypeConstraint, Subject, Constraint }; + } + + /// Construct a new same-type requirement. + /// + /// \param FirstType The first type. + /// \param EqualLoc The location of the '==' in the same-type constraint, or + /// an invalid location if this requirement was implied. + /// \param SecondType The second type. + static RequirementRepr getSameType(TypeRepr *FirstType, + SourceLoc EqualLoc, + TypeRepr *SecondType) { + return { EqualLoc, RequirementReprKind::SameType, FirstType, SecondType }; + } + + /// Construct a new layout-constraint requirement. + /// + /// \param Subject The type that must conform to the given layout + /// requirement. + /// \param ColonLoc The location of the ':', or an invalid location if + /// this requirement was implied. + /// \param Layout The layout requirement to which the + /// subject must conform. + static RequirementRepr getLayoutConstraint(TypeRepr *Subject, + SourceLoc ColonLoc, + LayoutConstraintLoc Layout) { + return {ColonLoc, RequirementReprKind::LayoutConstraint, Subject, + Layout}; + } + + /// Determine the kind of requirement + RequirementReprKind getKind() const { return Kind; } + + /// Determine whether this requirement is invalid. + bool isInvalid() const { return Invalid; } + + /// Mark this requirement invalid. + void setInvalid() { Invalid = true; } + + /// For a type-bound requirement, return the subject of the + /// conformance relationship. + TypeRepr *getSubjectRepr() const { + assert(getKind() == RequirementReprKind::TypeConstraint || + getKind() == RequirementReprKind::LayoutConstraint); + return FirstType; + } + + /// For a type-bound requirement, return the protocol or to which + /// the subject conforms or superclass it inherits. + TypeRepr *getConstraintRepr() const { + assert(getKind() == RequirementReprKind::TypeConstraint); + return SecondType; + } + + LayoutConstraint getLayoutConstraint() const { + assert(getKind() == RequirementReprKind::LayoutConstraint); + return SecondLayout.getLayoutConstraint(); + } + + LayoutConstraintLoc &getLayoutConstraintLoc() { + assert(getKind() == RequirementReprKind::LayoutConstraint); + return SecondLayout; + } + + const LayoutConstraintLoc &getLayoutConstraintLoc() const { + assert(getKind() == RequirementReprKind::LayoutConstraint); + return SecondLayout; + } + + /// Retrieve the first type of a same-type requirement. + TypeRepr *getFirstTypeRepr() const { + assert(getKind() == RequirementReprKind::SameType); + return FirstType; + } + + /// Retrieve the second type of a same-type requirement. + TypeRepr *getSecondTypeRepr() const { + assert(getKind() == RequirementReprKind::SameType); + return SecondType; + } + + /// Retrieve the location of the ':' or '==' in an explicitly-written + /// conformance or same-type requirement respectively. + SourceLoc getSeparatorLoc() const { + return SeparatorLoc; + } + + SourceRange getSourceRange() const; + + /// Retrieve the first or subject type representation from the \c repr, + /// or \c nullptr if \c repr is null. + static TypeRepr *getFirstTypeRepr(const RequirementRepr *repr) { + if (!repr) return nullptr; + return repr->FirstType; + } + + /// Retrieve the second or constraint type representation from the \c repr, + /// or \c nullptr if \c repr is null. + static TypeRepr *getSecondTypeRepr(const RequirementRepr *repr) { + if (!repr) return nullptr; + assert(repr->getKind() == RequirementReprKind::TypeConstraint || + repr->getKind() == RequirementReprKind::SameType); + return repr->SecondType; + } + + SWIFT_DEBUG_DUMP; + void print(raw_ostream &OS) const; + void print(ASTPrinter &Printer) const; +}; + +using GenericParamSource = PointerUnion; + +/// GenericParamList - A list of generic parameters that is part of a generic +/// function or type, along with extra requirements placed on those generic +/// parameters and types derived from them. +class GenericParamList final : + private llvm::TrailingObjects { + friend TrailingObjects; + + SourceRange Brackets; + unsigned NumParams; + SourceLoc WhereLoc; + MutableArrayRef Requirements; + + GenericParamList *OuterParameters; + + GenericParamList(SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc WhereLoc, + MutableArrayRef Requirements, + SourceLoc RAngleLoc); + + // Don't copy. + GenericParamList(const GenericParamList &) = delete; + GenericParamList &operator=(const GenericParamList &) = delete; + +public: + /// create - Create a new generic parameter list within the given AST context. + /// + /// \param Context The ASTContext in which the generic parameter list will + /// be allocated. + /// \param LAngleLoc The location of the opening angle bracket ('<') + /// \param Params The list of generic parameters, which will be copied into + /// ASTContext-allocated memory. + /// \param RAngleLoc The location of the closing angle bracket ('>') + static GenericParamList *create(ASTContext &Context, + SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc RAngleLoc); + + /// create - Create a new generic parameter list and "where" clause within + /// the given AST context. + /// + /// \param Context The ASTContext in which the generic parameter list will + /// be allocated. + /// \param LAngleLoc The location of the opening angle bracket ('<') + /// \param Params The list of generic parameters, which will be copied into + /// ASTContext-allocated memory. + /// \param WhereLoc The location of the 'where' keyword, if any. + /// \param Requirements The list of requirements, which will be copied into + /// ASTContext-allocated memory. + /// \param RAngleLoc The location of the closing angle bracket ('>') + static GenericParamList *create(const ASTContext &Context, + SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc WhereLoc, + ArrayRef Requirements, + SourceLoc RAngleLoc); + + MutableArrayRef getParams() { + return {getTrailingObjects(), NumParams}; + } + + ArrayRef getParams() const { + return {getTrailingObjects(), NumParams}; + } + + using iterator = GenericTypeParamDecl **; + using const_iterator = const GenericTypeParamDecl * const *; + + unsigned size() const { return NumParams; } + iterator begin() { return getParams().begin(); } + iterator end() { return getParams().end(); } + const_iterator begin() const { return getParams().begin(); } + const_iterator end() const { return getParams().end(); } + + /// Retrieve the location of the 'where' keyword, or an invalid + /// location if 'where' was not present. + SourceLoc getWhereLoc() const { return WhereLoc; } + + /// Retrieve the set of additional requirements placed on these + /// generic parameters and types derived from them. + /// + /// This list may contain both explicitly-written requirements as well as + /// implicitly-generated requirements, and may be non-empty even if no + /// 'where' keyword is present. + MutableArrayRef getRequirements() { return Requirements; } + + /// Retrieve the set of additional requirements placed on these + /// generic parameters and types derived from them. + /// + /// This list may contain both explicitly-written requirements as well as + /// implicitly-generated requirements, and may be non-empty even if no + /// 'where' keyword is present. + ArrayRef getRequirements() const { return Requirements; } + + /// Retrieve the outer generic parameter list. + /// + /// This is used for extensions of nested types, and in SIL mode, where a + /// single lexical context can have multiple logical generic parameter + /// lists. + GenericParamList *getOuterParameters() const { return OuterParameters; } + + /// Set the outer generic parameter list. See \c getOuterParameters + /// for more information. + void setOuterParameters(GenericParamList *Outer) { OuterParameters = Outer; } + + void setDeclContext(DeclContext *dc); + + SourceLoc getLAngleLoc() const { return Brackets.Start; } + SourceLoc getRAngleLoc() const { return Brackets.End; } + + SourceRange getSourceRange() const { return Brackets; } + + /// Retrieve the source range covering the where clause. + SourceRange getWhereClauseSourceRange() const { + if (WhereLoc.isInvalid()) + return SourceRange(); + + auto endLoc = Requirements.back().getSourceRange().End; + return SourceRange(WhereLoc, endLoc); + } + + /// Configure the depth of the generic parameters in this list. + void setDepth(unsigned depth); + + /// Create a copy of the generic parameter list and all of its generic + /// parameter declarations. The copied generic parameters are re-parented + /// to the given DeclContext. + GenericParamList *clone(DeclContext *dc) const; + + void print(raw_ostream &OS) const; + SWIFT_DEBUG_DUMP; + + bool walk(ASTWalker &walker); + + /// Finds a generic parameter declaration by name. This should only + /// be used from the SIL parser. + GenericTypeParamDecl *lookUpGenericParam(Identifier name) const; +}; + +/// A trailing where clause. +class alignas(RequirementRepr) TrailingWhereClause final : + private llvm::TrailingObjects { + friend TrailingObjects; + + SourceLoc WhereLoc; + + /// The number of requirements. The actual requirements are tail-allocated. + unsigned NumRequirements; + + TrailingWhereClause(SourceLoc whereLoc, + ArrayRef requirements); + +public: + /// Create a new trailing where clause with the given set of requirements. + static TrailingWhereClause *create(ASTContext &ctx, SourceLoc whereLoc, + ArrayRef requirements); + + /// Retrieve the location of the 'where' keyword. + SourceLoc getWhereLoc() const { return WhereLoc; } + + /// Retrieve the set of requirements. + MutableArrayRef getRequirements() { + return {getTrailingObjects(), NumRequirements}; + } + + /// Retrieve the set of requirements. + ArrayRef getRequirements() const { + return {getTrailingObjects(), NumRequirements}; + } + + /// Compute the source range containing this trailing where clause. + SourceRange getSourceRange() const { + return SourceRange(WhereLoc, + getRequirements().back().getSourceRange().End); + } + + void print(llvm::raw_ostream &OS, bool printWhereKeyword) const; + +}; + +} // namespace swift + +#endif // SWIFT_GENERIC_PARAM_LIST_H \ No newline at end of file diff --git a/include/swift/AST/GenericSignature.h b/include/swift/AST/GenericSignature.h index 3a29d21fbd890..fccbe06bde122 100644 --- a/include/swift/AST/GenericSignature.h +++ b/include/swift/AST/GenericSignature.h @@ -20,6 +20,7 @@ #include "swift/AST/PrintOptions.h" #include "swift/AST/Requirement.h" #include "swift/AST/Type.h" +#include "swift/AST/TypeAlignments.h" #include "swift/Basic/AnyValue.h" #include "swift/Basic/Debug.h" #include "llvm/ADT/ArrayRef.h" diff --git a/include/swift/AST/GenericSignatureBuilder.h b/include/swift/AST/GenericSignatureBuilder.h index e91543b476539..6b8c507569a69 100644 --- a/include/swift/AST/GenericSignatureBuilder.h +++ b/include/swift/AST/GenericSignatureBuilder.h @@ -22,6 +22,7 @@ #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Identifier.h" #include "swift/AST/ProtocolConformanceRef.h" diff --git a/include/swift/AST/TypeAlignments.h b/include/swift/AST/TypeAlignments.h index f0081a80dacf4..6097cf9a29fc5 100644 --- a/include/swift/AST/TypeAlignments.h +++ b/include/swift/AST/TypeAlignments.h @@ -35,9 +35,11 @@ namespace swift { class CaptureListExpr; class Decl; class DeclContext; + class DifferentiableAttr; class Expr; class ExtensionDecl; class GenericEnvironment; + class GenericParamList; class GenericTypeParamDecl; class NominalTypeDecl; class NormalProtocolConformance; @@ -46,9 +48,12 @@ namespace swift { class Pattern; class ProtocolDecl; class ProtocolConformance; + class RequirementRepr; class SILFunction; class SILFunctionType; + class SpecializeAttr; class Stmt; + class TrailingWhereClause; class TypeVariableType; class TypeBase; class TypeDecl; @@ -63,9 +68,15 @@ namespace swift { constexpr size_t StmtAlignInBits = 3; constexpr size_t TypeAlignInBits = 3; constexpr size_t PatternAlignInBits = 3; + constexpr size_t TypeReprAlignInBits = 3; + constexpr size_t SILFunctionAlignInBits = 2; + constexpr size_t ASTContextAlignInBits = 2; constexpr size_t TypeVariableAlignInBits = 4; - constexpr size_t TypeReprAlignInBits = 3; + + // Well, this is the *minimum* pointer alignment; it's going to be 3 on + // 64-bit targets, but that doesn't matter. + constexpr size_t PointerAlignInBits = 2; } namespace llvm { @@ -110,8 +121,9 @@ LLVM_DECLARE_TYPE_ALIGNMENT(swift::SILFunctionType, LLVM_DECLARE_TYPE_ALIGNMENT(swift::Stmt, swift::StmtAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::BraceStmt, swift::StmtAlignInBits) -LLVM_DECLARE_TYPE_ALIGNMENT(swift::ASTContext, 2); +LLVM_DECLARE_TYPE_ALIGNMENT(swift::ASTContext, swift::ASTContextAlignInBits); LLVM_DECLARE_TYPE_ALIGNMENT(swift::DeclContext, swift::DeclContextAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::DifferentiableAttr, swift::PointerAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::Expr, swift::ExprAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::CaptureListExpr, swift::ExprAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::AbstractClosureExpr, swift::ExprAlignInBits) @@ -121,10 +133,17 @@ LLVM_DECLARE_TYPE_ALIGNMENT(swift::NormalProtocolConformance, swift::DeclAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::GenericEnvironment, swift::DeclAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::GenericParamList, + swift::PointerAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::Pattern, swift::PatternAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::RequirementRepr, + swift::PointerAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::SILFunction, swift::SILFunctionAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::SpecializeAttr, swift::PointerAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::TrailingWhereClause, + swift::PointerAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::AttributeBase, swift::AttrAlignInBits) diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index ed84c49bbfa8e..045efb7179e42 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -19,6 +19,7 @@ #include "swift/AST/ActorIsolation.h" #include "swift/AST/AnyFunctionRef.h" #include "swift/AST/ASTTypeIDs.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Type.h" #include "swift/AST/Evaluator.h" diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 57c18ef074c6b..3cae352ef73dd 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -50,6 +50,7 @@ namespace swift { class Lexer; class ParsedTypeSyntax; class PersistentParserState; + class RequirementRepr; class SILParserStateBase; class ScopeInfo; class SourceManager; diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 91b809e881ed5..dd12a1570f09b 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -21,6 +21,7 @@ #include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 3a76406d8ea4e..0c6d22b4bca5c 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -20,6 +20,7 @@ #include "swift/AST/Attr.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index e848e6c12c52c..20eea9d67101c 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" diff --git a/lib/AST/ASTScopePrinting.cpp b/lib/AST/ASTScopePrinting.cpp index f68e4f582628e..2dafad62d3946 100644 --- a/lib/AST/ASTScopePrinting.cpp +++ b/lib/AST/ASTScopePrinting.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index fc86ba1c2f84b..48a59b2f3ac59 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index bc5998914d1b3..900afa1baf7aa 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -55,6 +55,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/ASTVisitor.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PrettyStackTrace.h" using namespace swift; diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 62993edae03ea..6762297a9f098 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -49,6 +49,7 @@ add_swift_host_library(swiftAST STATIC FineGrainedDependencyFormat.cpp FrontendSourceFileDepGraphFactory.cpp GenericEnvironment.cpp + GenericParamList.cpp GenericSignature.cpp GenericSignatureBuilder.cpp Identifier.cpp diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index 45a249601a7ac..547fa1bd4b4d0 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -18,6 +18,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/ExistentialLayout.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a330b63faedcb..6a153ef671abd 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -891,115 +891,6 @@ bool Decl::isWeakImported(ModuleDecl *fromModule) const { return !fromContext.isContainedIn(containingContext); } - -SourceRange RequirementRepr::getSourceRange() const { - if (getKind() == RequirementReprKind::LayoutConstraint) - return SourceRange(FirstType->getSourceRange().Start, - SecondLayout.getSourceRange().End); - return SourceRange(FirstType->getSourceRange().Start, - SecondType->getSourceRange().End); -} - -GenericParamList::GenericParamList(SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc WhereLoc, - MutableArrayRef Requirements, - SourceLoc RAngleLoc) - : Brackets(LAngleLoc, RAngleLoc), NumParams(Params.size()), - WhereLoc(WhereLoc), Requirements(Requirements), - OuterParameters(nullptr) -{ - std::uninitialized_copy(Params.begin(), Params.end(), - getTrailingObjects()); -} - -GenericParamList * -GenericParamList::create(ASTContext &Context, - SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc RAngleLoc) { - unsigned Size = totalSizeToAlloc(Params.size()); - void *Mem = Context.Allocate(Size, alignof(GenericParamList)); - return new (Mem) GenericParamList(LAngleLoc, Params, SourceLoc(), - MutableArrayRef(), - RAngleLoc); -} - -GenericParamList * -GenericParamList::create(const ASTContext &Context, - SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc WhereLoc, - ArrayRef Requirements, - SourceLoc RAngleLoc) { - unsigned Size = totalSizeToAlloc(Params.size()); - void *Mem = Context.Allocate(Size, alignof(GenericParamList)); - return new (Mem) GenericParamList(LAngleLoc, Params, - WhereLoc, - Context.AllocateCopy(Requirements), - RAngleLoc); -} - -GenericParamList * -GenericParamList::clone(DeclContext *dc) const { - auto &ctx = dc->getASTContext(); - SmallVector params; - for (auto param : getParams()) { - auto *newParam = new (ctx) GenericTypeParamDecl( - dc, param->getName(), SourceLoc(), - GenericTypeParamDecl::InvalidDepth, - param->getIndex()); - newParam->setImplicit(true); - params.push_back(newParam); - } - - return GenericParamList::create(ctx, SourceLoc(), params, SourceLoc()); -} - -void GenericParamList::setDepth(unsigned depth) { - for (auto param : *this) - param->setDepth(depth); -} - -void GenericParamList::setDeclContext(DeclContext *dc) { - for (auto param : *this) - param->setDeclContext(dc); -} - -GenericTypeParamDecl *GenericParamList::lookUpGenericParam( - Identifier name) const { - for (const auto *innerParams = this; - innerParams != nullptr; - innerParams = innerParams->getOuterParameters()) { - for (auto *paramDecl : *innerParams) { - if (name == paramDecl->getName()) { - return const_cast(paramDecl); - } - } - } - - return nullptr; -} - -TrailingWhereClause::TrailingWhereClause( - SourceLoc whereLoc, - ArrayRef requirements) - : WhereLoc(whereLoc), - NumRequirements(requirements.size()) -{ - std::uninitialized_copy(requirements.begin(), requirements.end(), - getTrailingObjects()); -} - -TrailingWhereClause *TrailingWhereClause::create( - ASTContext &ctx, - SourceLoc whereLoc, - ArrayRef requirements) { - unsigned size = totalSizeToAlloc(requirements.size()); - void *mem = ctx.Allocate(size, alignof(TrailingWhereClause)); - return new (mem) TrailingWhereClause(whereLoc, requirements); -} - GenericContext::GenericContext(DeclContextKind Kind, DeclContext *Parent, GenericParamList *Params) : _GenericContext(), DeclContext(Kind, Parent) { diff --git a/lib/AST/GenericParamList.cpp b/lib/AST/GenericParamList.cpp new file mode 100644 index 0000000000000..426833e151997 --- /dev/null +++ b/lib/AST/GenericParamList.cpp @@ -0,0 +1,129 @@ +//===--- GenericParamList.cpp - Swift Language Decl ASTs ------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements the GenericParamList class and related classes. +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/GenericParamList.h" + +#include "swift/AST/ASTContext.h" +#include "swift/AST/TypeRepr.h" + +using namespace swift; +SourceRange RequirementRepr::getSourceRange() const { + if (getKind() == RequirementReprKind::LayoutConstraint) + return SourceRange(FirstType->getSourceRange().Start, + SecondLayout.getSourceRange().End); + return SourceRange(FirstType->getSourceRange().Start, + SecondType->getSourceRange().End); +} + +GenericParamList::GenericParamList(SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc WhereLoc, + MutableArrayRef Requirements, + SourceLoc RAngleLoc) + : Brackets(LAngleLoc, RAngleLoc), NumParams(Params.size()), + WhereLoc(WhereLoc), Requirements(Requirements), + OuterParameters(nullptr) +{ + std::uninitialized_copy(Params.begin(), Params.end(), + getTrailingObjects()); +} + +GenericParamList * +GenericParamList::create(ASTContext &Context, + SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc RAngleLoc) { + unsigned Size = totalSizeToAlloc(Params.size()); + void *Mem = Context.Allocate(Size, alignof(GenericParamList)); + return new (Mem) GenericParamList(LAngleLoc, Params, SourceLoc(), + MutableArrayRef(), + RAngleLoc); +} + +GenericParamList * +GenericParamList::create(const ASTContext &Context, + SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc WhereLoc, + ArrayRef Requirements, + SourceLoc RAngleLoc) { + unsigned Size = totalSizeToAlloc(Params.size()); + void *Mem = Context.Allocate(Size, alignof(GenericParamList)); + return new (Mem) GenericParamList(LAngleLoc, Params, + WhereLoc, + Context.AllocateCopy(Requirements), + RAngleLoc); +} + +GenericParamList * +GenericParamList::clone(DeclContext *dc) const { + auto &ctx = dc->getASTContext(); + SmallVector params; + for (auto param : getParams()) { + auto *newParam = new (ctx) GenericTypeParamDecl( + dc, param->getName(), SourceLoc(), + GenericTypeParamDecl::InvalidDepth, + param->getIndex()); + newParam->setImplicit(true); + params.push_back(newParam); + } + + return GenericParamList::create(ctx, SourceLoc(), params, SourceLoc()); +} + +void GenericParamList::setDepth(unsigned depth) { + for (auto param : *this) + param->setDepth(depth); +} + +void GenericParamList::setDeclContext(DeclContext *dc) { + for (auto param : *this) + param->setDeclContext(dc); +} + +GenericTypeParamDecl *GenericParamList::lookUpGenericParam( + Identifier name) const { + for (const auto *innerParams = this; + innerParams != nullptr; + innerParams = innerParams->getOuterParameters()) { + for (auto *paramDecl : *innerParams) { + if (name == paramDecl->getName()) { + return const_cast(paramDecl); + } + } + } + + return nullptr; +} + +TrailingWhereClause::TrailingWhereClause( + SourceLoc whereLoc, + ArrayRef requirements) + : WhereLoc(whereLoc), + NumRequirements(requirements.size()) +{ + std::uninitialized_copy(requirements.begin(), requirements.end(), + getTrailingObjects()); +} + +TrailingWhereClause *TrailingWhereClause::create( + ASTContext &ctx, + SourceLoc whereLoc, + ArrayRef requirements) { + unsigned size = totalSizeToAlloc(requirements.size()); + void *mem = ctx.Allocate(size, alignof(TrailingWhereClause)); + return new (mem) TrailingWhereClause(whereLoc, requirements); +} diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index bff5a8688291d..09b02ec88d227 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -24,6 +24,7 @@ #include "swift/AST/ExistentialLayout.h" #include "swift/AST/FileUnit.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index fb7cbf7a19568..e3ce6eb170076 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -20,6 +20,7 @@ #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/DebuggerClient.h" #include "swift/AST/ExistentialLayout.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/ImportCache.h" #include "swift/AST/Initializer.h" diff --git a/lib/AST/NameLookupRequests.cpp b/lib/AST/NameLookupRequests.cpp index 379a2748fd9dc..62b6484f5c547 100644 --- a/lib/AST/NameLookupRequests.cpp +++ b/lib/AST/NameLookupRequests.cpp @@ -14,6 +14,7 @@ #include "swift/AST/NameLookupRequests.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/Evaluator.h" #include "swift/AST/Module.h" diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index 6490258e3b922..24253def0d94f 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/Expr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Module.h" #include "swift/AST/Types.h" #include "swift/Basic/Defer.h" diff --git a/lib/AST/USRGeneration.cpp b/lib/AST/USRGeneration.cpp index 30716b9f498c8..c3fe2322c21c9 100644 --- a/lib/AST/USRGeneration.cpp +++ b/lib/AST/USRGeneration.cpp @@ -12,6 +12,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/ClangModuleLoader.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Module.h" #include "swift/AST/USRGeneration.h" #include "swift/AST/ASTMangler.h" diff --git a/lib/IDE/Formatting.cpp b/lib/IDE/Formatting.cpp index 6100a21d41669..a35bb306e10cf 100644 --- a/lib/IDE/Formatting.cpp +++ b/lib/IDE/Formatting.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "swift/AST/ASTWalker.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/TypeRepr.h" #include "swift/IDE/SourceEntityWalker.h" #include "swift/Parse/Parser.h" diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index fa9ace4441e27..da749109d0fd9 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -23,6 +23,7 @@ #include "swift/Subsystems.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/Attr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/DebuggerClient.h" #include "swift/AST/DiagnosticsParse.h" diff --git a/lib/Parse/ParseGeneric.cpp b/lib/Parse/ParseGeneric.cpp index f980de64d56c8..969635bdd3cb6 100644 --- a/lib/Parse/ParseGeneric.cpp +++ b/lib/Parse/ParseGeneric.cpp @@ -16,6 +16,7 @@ #include "swift/Parse/Parser.h" #include "swift/AST/DiagnosticsParse.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/TypeRepr.h" #include "swift/Parse/CodeCompletionCallbacks.h" #include "swift/Parse/SyntaxParsingContext.h" diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index 016281ab3d6b0..a1ceca394ac08 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -17,6 +17,7 @@ #include "swift/Parse/Parser.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/Attr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/TypeRepr.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/CodeCompletionCallbacks.h" diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index dbd5f63e86b05..fa8f01f211e7c 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -22,6 +22,7 @@ #include "swift/AST/AnyFunctionRef.h" #include "swift/AST/Availability.h" #include "swift/AST/DiagnosticsSema.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/KnownProtocols.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/NameLookup.h" diff --git a/unittests/AST/TestContext.cpp b/unittests/AST/TestContext.cpp index 174cf90630097..1cee32305944f 100644 --- a/unittests/AST/TestContext.cpp +++ b/unittests/AST/TestContext.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "TestContext.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Module.h" #include "swift/AST/ParseRequests.h" #include "swift/Strings.h" From 82ead3e8b5d4433ae60a205f18a77ab0beee02c8 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Tue, 29 Sep 2020 17:17:27 -0700 Subject: [PATCH 118/745] [NFC] Add test for (incorrect) behavior of swift-demangle. --- test/Demangle/Inputs/manglings.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index 7f410eec10faa..c0326ef954197 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -357,6 +357,8 @@ $s3red4testyAA3ResOyxSayq_GAEs5ErrorAAq_sAFHD1__HCg_GADyxq_GsAFR_r0_lF ---> red. $s3red4testyAA7OurTypeOy4them05TheirD0Vy5AssocQzGAjE0F8ProtocolAAxAA0c7DerivedH0HD1_AA0c4BaseH0HI1_AieKHA2__HCg_GxmAaLRzlF ---> red.test(A.Type) -> red.OurType> $s17property_wrappers10WithTuplesV9fractionsSd_S2dtvpfP ---> property wrapper backing initializer of property_wrappers.WithTuples.fractions : (Swift.Double, Swift.Double, Swift.Double) $sSo17OS_dispatch_queueC4sync7executeyyyXE_tFTOTA ---> {T:$sSo17OS_dispatch_queueC4sync7executeyyyXE_tFTO} partial apply forwarder for @nonobjc __C.OS_dispatch_queue.sync(execute: () -> ()) -> () +$s4main1gyySiXCvp ---> main.g : (Swift.Int) -> () +$s4main1gyySiXBvp ---> main.g : (Swift.Int) -> () $sxq_Idgnr_D ---> @differentiable @callee_guaranteed (@in_guaranteed A) -> (@out B) $sxq_Ilgnr_D ---> @differentiable(linear) @callee_guaranteed (@in_guaranteed A) -> (@out B) $sS3fIedgyywd_D ---> @escaping @differentiable @callee_guaranteed (@unowned Swift.Float, @unowned @noDerivative Swift.Float) -> (@unowned Swift.Float) From 26d828395803aacee1a85dd40e221d51f225e507 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Tue, 29 Sep 2020 17:27:12 -0700 Subject: [PATCH 119/745] [Demangler] Print convention attributes consistently. --- lib/Demangling/NodePrinter.cpp | 85 +++++++++++++----------------- test/Demangle/Inputs/manglings.txt | 4 +- 2 files changed, 38 insertions(+), 51 deletions(-) diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 3a7fc824699b2..5287239caffc5 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -18,6 +18,7 @@ #include "swift/Demangling/Demangle.h" #include "swift/AST/Ownership.h" #include "swift/Strings.h" +#include #include #include @@ -753,6 +754,35 @@ class NodePrinter { setInvalid(); return; } + + switch (node->getKind()) { + case Node::Kind::FunctionType: + case Node::Kind::UncurriedFunctionType: + case Node::Kind::NoEscapeFunctionType: + break; + case Node::Kind::AutoClosureType: + case Node::Kind::EscapingAutoClosureType: + Printer << "@autoclosure "; break; + case Node::Kind::ThinFunctionType: + Printer << "@convention(thin) "; break; + case Node::Kind::CFunctionPointer: + Printer << "@convention(c) "; break; + case Node::Kind::ObjCBlock: + Printer << "@convention(block) "; break; + case Node::Kind::EscapingObjCBlock: + Printer << "@escaping @convention(block) "; break; + case Node::Kind::DifferentiableFunctionType: + Printer << "@differentiable "; break; + case Node::Kind::EscapingDifferentiableFunctionType: + Printer << "@escaping @differentiable "; break; + case Node::Kind::LinearFunctionType: + Printer << "@differentiable(linear) "; break; + case Node::Kind::EscapingLinearFunctionType: + Printer << "@escaping @differentiable(linear) "; break; + default: + assert(false && "Unhandled function type in printFunctionType!"); + } + unsigned startIndex = 0; bool isAsync = false, isThrows = false; if (node->getChild(startIndex)->getKind() == Node::Kind::ThrowsAnnotation) { @@ -1256,39 +1286,19 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::UnknownIndex: Printer << "unknown index"; return nullptr; + case Node::Kind::FunctionType: + case Node::Kind::UncurriedFunctionType: case Node::Kind::NoEscapeFunctionType: - printFunctionType(nullptr, Node); - return nullptr; - case Node::Kind::EscapingAutoClosureType: - Printer << "@autoclosure "; - printFunctionType(nullptr, Node); - return nullptr; case Node::Kind::AutoClosureType: - Printer << "@autoclosure "; - printFunctionType(nullptr, Node); - return nullptr; + case Node::Kind::EscapingAutoClosureType: case Node::Kind::ThinFunctionType: - Printer << "@convention(thin) "; - printFunctionType(nullptr, Node); - return nullptr; + case Node::Kind::CFunctionPointer: + case Node::Kind::ObjCBlock: + case Node::Kind::EscapingObjCBlock: case Node::Kind::DifferentiableFunctionType: - Printer << "@differentiable "; - printFunctionType(nullptr, Node); - return nullptr; case Node::Kind::EscapingDifferentiableFunctionType: - Printer << "@escaping @differentiable "; - printFunctionType(nullptr, Node); - return nullptr; case Node::Kind::LinearFunctionType: - Printer << "@differentiable(linear) "; - printFunctionType(nullptr, Node); - return nullptr; case Node::Kind::EscapingLinearFunctionType: - Printer << "@escaping @differentiable(linear) "; - printFunctionType(nullptr, Node); - return nullptr; - case Node::Kind::FunctionType: - case Node::Kind::UncurriedFunctionType: printFunctionType(nullptr, Node); return nullptr; case Node::Kind::ArgumentTuple: @@ -1881,21 +1891,6 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::DynamicSelf: Printer << "Self"; return nullptr; - case Node::Kind::CFunctionPointer: { - Printer << "@convention(c) "; - printFunctionType(nullptr, Node); - return nullptr; - } - case Node::Kind::ObjCBlock: { - Printer << "@convention(block) "; - printFunctionType(nullptr, Node); - return nullptr; - } - case Node::Kind::EscapingObjCBlock: { - Printer << "@escaping @convention(block) "; - printFunctionType(nullptr, Node); - return nullptr; - } case Node::Kind::SILBoxType: { Printer << "@box "; NodePointer type = Node->getChild(0); @@ -2648,14 +2643,6 @@ void NodePrinter::printEntityType(NodePointer Entity, NodePointer type, Printer << ' '; type = dependentType->getFirstChild(); } - if (type->getKind() == Node::Kind::DifferentiableFunctionType) - Printer << "@differentiable "; - else if (type->getKind() == Node::Kind::EscapingDifferentiableFunctionType) - Printer << "@escaping @differentiable "; - else if (type->getKind() == Node::Kind::LinearFunctionType) - Printer << "@differentiable(linear) "; - else if (type->getKind() == Node::Kind::EscapingLinearFunctionType) - Printer << "@escaping @differentiable(linear) "; printFunctionType(labelList, type); } else { print(type); diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index c0326ef954197..294e7a08a34c2 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -357,8 +357,8 @@ $s3red4testyAA3ResOyxSayq_GAEs5ErrorAAq_sAFHD1__HCg_GADyxq_GsAFR_r0_lF ---> red. $s3red4testyAA7OurTypeOy4them05TheirD0Vy5AssocQzGAjE0F8ProtocolAAxAA0c7DerivedH0HD1_AA0c4BaseH0HI1_AieKHA2__HCg_GxmAaLRzlF ---> red.test(A.Type) -> red.OurType> $s17property_wrappers10WithTuplesV9fractionsSd_S2dtvpfP ---> property wrapper backing initializer of property_wrappers.WithTuples.fractions : (Swift.Double, Swift.Double, Swift.Double) $sSo17OS_dispatch_queueC4sync7executeyyyXE_tFTOTA ---> {T:$sSo17OS_dispatch_queueC4sync7executeyyyXE_tFTO} partial apply forwarder for @nonobjc __C.OS_dispatch_queue.sync(execute: () -> ()) -> () -$s4main1gyySiXCvp ---> main.g : (Swift.Int) -> () -$s4main1gyySiXBvp ---> main.g : (Swift.Int) -> () +$s4main1gyySiXCvp ---> main.g : @convention(c) (Swift.Int) -> () +$s4main1gyySiXBvp ---> main.g : @convention(block) (Swift.Int) -> () $sxq_Idgnr_D ---> @differentiable @callee_guaranteed (@in_guaranteed A) -> (@out B) $sxq_Ilgnr_D ---> @differentiable(linear) @callee_guaranteed (@in_guaranteed A) -> (@out B) $sS3fIedgyywd_D ---> @escaping @differentiable @callee_guaranteed (@unowned Swift.Float, @unowned @noDerivative Swift.Float) -> (@unowned Swift.Float) From 4ddf6e565a8e6bc5483983befe4513fe737940aa Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 17:29:41 -0700 Subject: [PATCH 120/745] Teach SuperclassDeclRequest to always diagnose circularity. Rather than relying on clients to cope with the potential for circular inheritance of superclass declarations, teach SuperclassDeclRequest to establish whether circular inheritance has occurred and produce "null" in such cases. This allows other clients to avoid having to think about To benefit from this, have SuperclassTypeRequest evaluate SuperclassDeclRequest first and, if null, produce a Type(). This ensures that we don't get into an inconsistent situation where there is a superclass type but no superclass declaration. --- include/swift/AST/NameLookupRequests.h | 4 ++++ lib/AST/Decl.cpp | 2 +- lib/AST/NameLookup.cpp | 27 +++++++++++++++++++--- lib/AST/NameLookupRequests.cpp | 13 +++++++++++ lib/SILGen/SILGen.cpp | 4 ++-- lib/Sema/CodeSynthesis.cpp | 7 +++--- lib/Sema/TypeCheckRequestFunctions.cpp | 6 +++++ test/decl/class/circular_inheritance.swift | 23 +++++++----------- 8 files changed, 61 insertions(+), 25 deletions(-) diff --git a/include/swift/AST/NameLookupRequests.h b/include/swift/AST/NameLookupRequests.h index e02441cdac004..7ed09f1e10b87 100644 --- a/include/swift/AST/NameLookupRequests.h +++ b/include/swift/AST/NameLookupRequests.h @@ -160,6 +160,10 @@ class SuperclassDeclRequest : evaluate(Evaluator &evaluator, NominalTypeDecl *subject) const; public: + // Cycle handling + void diagnoseCycle(DiagnosticEngine &diags) const; + void noteCycleStep(DiagnosticEngine &diags) const; + // Caching bool isCached() const { return true; } Optional getCachedResult() const; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a330b63faedcb..a8c9ef918a30b 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4276,7 +4276,7 @@ bool ClassDecl::isIncompatibleWithWeakReferences() const { bool ClassDecl::inheritsSuperclassInitializers() const { // If there's no superclass, there's nothing to inherit. - if (!getSuperclass()) + if (!getSuperclassDecl()) return false; auto &ctx = getASTContext(); diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index fb7cbf7a19568..7ba550c9f8cfd 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -2290,12 +2290,33 @@ SuperclassDeclRequest::evaluate(Evaluator &evaluator, inheritedTypes, modulesFound, anyObject); // Look for a class declaration. + ClassDecl *superclass = nullptr; for (auto inheritedNominal : inheritedNominalTypes) { - if (auto classDecl = dyn_cast(inheritedNominal)) - return classDecl; + if (auto classDecl = dyn_cast(inheritedNominal)) { + superclass = classDecl; + break; + } } - } + // If we found a superclass, ensure that we don't have a circular + // inheritance hierarchy by evaluating its superclass. This forces the + // diagnostic at this point and then suppresses the superclass failure. + if (superclass) { + auto result = Ctx.evaluator(SuperclassDeclRequest{superclass}); + bool hadCycle = false; + if (auto err = result.takeError()) { + llvm::handleAllErrors(std::move(err), + [&hadCycle](const CyclicalRequestError &E) { + hadCycle = true; + }); + + if (hadCycle) + return nullptr; + } + + return superclass; + } + } return nullptr; } diff --git a/lib/AST/NameLookupRequests.cpp b/lib/AST/NameLookupRequests.cpp index 379a2748fd9dc..7914ee8094bd3 100644 --- a/lib/AST/NameLookupRequests.cpp +++ b/lib/AST/NameLookupRequests.cpp @@ -45,6 +45,19 @@ SourceLoc InheritedDeclsReferencedRequest::getNearestLoc() const { //----------------------------------------------------------------------------// // Superclass declaration computation. //----------------------------------------------------------------------------// +void SuperclassDeclRequest::diagnoseCycle(DiagnosticEngine &diags) const { + // FIXME: Improve this diagnostic. + auto nominalDecl = std::get<0>(getStorage()); + diags.diagnose(nominalDecl, diag::circular_class_inheritance, + nominalDecl->getName()); +} + +void SuperclassDeclRequest::noteCycleStep(DiagnosticEngine &diags) const { + auto decl = std::get<0>(getStorage()); + diags.diagnose(decl, diag::kind_declname_declared_here, + decl->getDescriptiveKind(), decl->getName()); +} + Optional SuperclassDeclRequest::getCachedResult() const { auto nominalDecl = std::get<0>(getStorage()); diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index df9f0e13b97fd..4233f9dd8e7f8 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1288,8 +1288,8 @@ bool SILGenModule::requiresIVarDestroyer(ClassDecl *cd) { // Only needed if we have non-trivial ivars, we're not a root class, and // the superclass is not @objc. return (hasNonTrivialIVars(cd) && - cd->getSuperclass() && - !cd->getSuperclass()->getClassOrBoundGenericClass()->hasClangNode()); + cd->getSuperclassDecl() && + !cd->getSuperclassDecl()->hasClangNode()); } /// TODO: This needs a better name. diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 60676e4c241e6..25e6f207b8d17 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -1076,12 +1076,11 @@ InheritsSuperclassInitializersRequest::evaluate(Evaluator &eval, if (decl->getAttrs().hasAttribute()) return true; - auto superclass = decl->getSuperclass(); - assert(superclass); + auto superclassDecl = decl->getSuperclassDecl(); + assert(superclassDecl); // If the superclass has known-missing designated initializers, inheriting // is unsafe. - auto *superclassDecl = superclass->getClassOrBoundGenericClass(); if (superclassDecl->getModuleContext() != decl->getParentModule() && superclassDecl->hasMissingDesignatedInitializers()) return false; @@ -1357,7 +1356,7 @@ HasDefaultInitRequest::evaluate(Evaluator &evaluator, // Don't synthesize a default for a subclass, it will attempt to inherit its // initializers from its superclass. if (auto *cd = dyn_cast(decl)) - if (cd->getSuperclass()) + if (cd->getSuperclassDecl()) return false; // If the user has already defined a designated initializer, then don't diff --git a/lib/Sema/TypeCheckRequestFunctions.cpp b/lib/Sema/TypeCheckRequestFunctions.cpp index f841882dc3893..e8bad702d48a1 100644 --- a/lib/Sema/TypeCheckRequestFunctions.cpp +++ b/lib/Sema/TypeCheckRequestFunctions.cpp @@ -93,6 +93,12 @@ SuperclassTypeRequest::evaluate(Evaluator &evaluator, return proto->getGenericSignature() ->getSuperclassBound(proto->getSelfInterfaceType()); } + + if (!proto->getSuperclassDecl()) + return Type(); + } else if (auto classDecl = dyn_cast(nominalDecl)) { + if (!classDecl->getSuperclassDecl()) + return Type(); } for (unsigned int idx : indices(nominalDecl->getInherited())) { diff --git a/test/decl/class/circular_inheritance.swift b/test/decl/class/circular_inheritance.swift index edfdf396c569b..e3da9551e645d 100644 --- a/test/decl/class/circular_inheritance.swift +++ b/test/decl/class/circular_inheritance.swift @@ -3,12 +3,12 @@ // RUN: %target-typecheck-verify-swift // RUN: not %target-swift-frontend -typecheck -debug-cycles %s -build-request-dependency-graph -output-request-graphviz %t.dot -stats-output-dir %t/stats-dir 2> %t.cycles -class Left // expected-error {{circular reference}} expected-note {{through reference here}} +class Left // expected-error {{'Left' inherits from itself}} expected-note {{through reference here}} : Right.Hand { // expected-note {{through reference here}} class Hand {} } -class Right // expected-note 2 {{through reference here}} +class Right // expected-note {{through reference here}} expected-note{{class 'Right' declared here}} : Left.Hand { // expected-note {{through reference here}} class Hand {} } @@ -23,20 +23,19 @@ protocol P : P {} // expected-error {{protocol 'P' refines itself}} class Isomorphism : Automorphism { } class Automorphism : Automorphism { } // expected-error{{'Automorphism' inherits from itself}} -// FIXME: Useless error -let _ = A() // expected-error{{'A' cannot be constructed because it has no accessible initializers}} +let _ = A() class Outer { class Inner : Outer {} } -class Outer2 // expected-error {{circular reference}} expected-note {{through reference here}} +class Outer2 // expected-error {{'Outer2' inherits from itself}} expected-note {{through reference here}} : Outer2.Inner { // expected-note {{through reference here}} class Inner {} } -class Outer3 // expected-error {{circular reference}} expected-note {{through reference here}} +class Outer3 // expected-error {{'Outer3' inherits from itself}} expected-note {{through reference here}} : Outer3.Inner { // expected-note {{through reference here}} class Inner {} } @@ -54,27 +53,21 @@ class Outer3 // expected-error {{circular reference}} expected-note {{through re protocol Initable { init() - // expected-note@-1 {{protocol requires initializer 'init()' with type '()'; do you want to add a stub?}} - // expected-note@-2 {{did you mean 'init'?}} } protocol Shape : Circle {} class Circle : Initable & Circle {} // expected-error@-1 {{'Circle' inherits from itself}} -// expected-error@-2 {{type 'Circle' does not conform to protocol 'Initable'}} +// expected-error@-2 {{initializer requirement 'init()' can only be satisfied by a 'required' initializer in non-final class 'Circle'}} func crash() { - Circle() - // expected-error@-1 {{'Circle' cannot be constructed because it has no accessible initializers}} + _ = Circle() } // FIXME: We shouldn't emit the redundant "circular reference" diagnostics here. class WithDesignatedInit : WithDesignatedInit { // expected-error@-1 {{'WithDesignatedInit' inherits from itself}} - // expected-error@-2 {{circular reference}} - // expected-note@-3 {{through reference here}} - // expected-note@-4 2 {{through reference here}} - init(x: Int) {} // expected-error {{circular reference}} + init(x: Int) {} } From 6af690aaba470b379ccf45fecfa7fe2b72ec028e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 17:39:43 -0700 Subject: [PATCH 121/745] Eliminate HasCircularInheritanceRequest. This is now reliably detected by SuperclassDeclRequest. --- include/swift/AST/Decl.h | 3 -- include/swift/AST/TypeCheckRequests.h | 23 ----------- include/swift/AST/TypeCheckerTypeIDZone.def | 2 - lib/AST/Decl.cpp | 7 ---- lib/AST/TypeCheckRequests.cpp | 17 -------- lib/Sema/DerivedConformanceCodable.cpp | 9 +++- lib/Sema/TypeCheckDecl.cpp | 46 --------------------- lib/Sema/TypeCheckDeclPrimary.cpp | 2 +- test/decl/class/circular_inheritance.swift | 10 +---- 9 files changed, 9 insertions(+), 110 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 588ef350f497d..87568606192d5 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3920,9 +3920,6 @@ class ClassDecl final : public NominalTypeDecl { /// Set the superclass of this class. void setSuperclass(Type superclass); - /// Whether this class has a circular reference in its inheritance hierarchy. - bool hasCircularInheritance() const; - /// Walk this class and all of the superclasses of this class, transitively, /// invoking the callback function for each class. /// diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index ed84c49bbfa8e..5c7b15f5a9158 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -1986,29 +1986,6 @@ class PreCheckFunctionBuilderRequest bool isCached() const { return true; } }; -/// Computes whether a class has a circular reference in its inheritance -/// hierarchy. -class HasCircularInheritanceRequest - : public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - // Evaluation. - bool evaluate(Evaluator &evaluator, ClassDecl *decl) const; - -public: - // Cycle handling. - void diagnoseCycle(DiagnosticEngine &diags) const; - void noteCycleStep(DiagnosticEngine &diags) const; - - // Cached. - bool isCached() const { return true; } -}; - /// Computes whether a protocol has a circular reference in its list of /// inherited protocols. class HasCircularInheritedProtocolsRequest diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index ea2a99e3ca459..7b3f269555979 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -95,8 +95,6 @@ SWIFT_REQUEST(TypeChecker, FunctionOperatorRequest, OperatorDecl *(FuncDecl *), SWIFT_REQUEST(NameLookup, GenericSignatureRequest, GenericSignature (GenericContext *), SeparatelyCached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, HasCircularInheritanceRequest, - bool(ClassDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, HasCircularInheritedProtocolsRequest, bool(ProtocolDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, HasCircularRawValueRequest, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a8c9ef918a30b..ef57f0160b45a 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4148,13 +4148,6 @@ void NominalTypeDecl::synthesizeSemanticMembersIfNeeded(DeclName member) { } } -bool ClassDecl::hasCircularInheritance() const { - auto &ctx = getASTContext(); - auto *mutableThis = const_cast(this); - return evaluateOrDefault(ctx.evaluator, - HasCircularInheritanceRequest{mutableThis}, true); -} - ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc, MutableArrayRef Inherited, GenericParamList *GenericParams, DeclContext *Parent) diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 0b6c3eb544d1c..da08ab9057d25 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1136,23 +1136,6 @@ void swift::simple_display(llvm::raw_ostream &out, } } -//----------------------------------------------------------------------------// -// HasCircularInheritanceRequest computation. -//----------------------------------------------------------------------------// - -void HasCircularInheritanceRequest::diagnoseCycle( - DiagnosticEngine &diags) const { - auto *decl = std::get<0>(getStorage()); - diags.diagnose(decl, diag::circular_class_inheritance, decl->getName()); -} - -void HasCircularInheritanceRequest::noteCycleStep( - DiagnosticEngine &diags) const { - auto *decl = std::get<0>(getStorage()); - diags.diagnose(decl, diag::kind_declname_declared_here, - decl->getDescriptiveKind(), decl->getName()); -} - //----------------------------------------------------------------------------// // HasCircularInheritedProtocolsRequest computation. //----------------------------------------------------------------------------// diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index 337afe9dd9946..61fa5b46d98b1 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -30,10 +30,15 @@ using namespace swift; /// Returns whether the type represented by the given ClassDecl inherits from a /// type which conforms to the given protocol. static bool superclassConformsTo(ClassDecl *target, KnownProtocolKind kpk) { - if (!target || !target->getSuperclass() || target->hasCircularInheritance()) { + if (!target) { return false; } - return !target->getSuperclassDecl() + + auto superclass = target->getSuperclassDecl(); + if (!superclass) + return false; + + return !superclass ->getModuleContext() ->lookupConformance(target->getSuperclass(), target->getASTContext().getProtocol(kpk)) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 3a1f7aaf921af..aba91c8154516 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -233,52 +233,6 @@ static bool canSkipCircularityCheck(NominalTypeDecl *decl) { return decl->hasClangNode() || decl->wasDeserialized(); } -bool -HasCircularInheritanceRequest::evaluate(Evaluator &evaluator, - ClassDecl *decl) const { - if (canSkipCircularityCheck(decl) || !decl->hasSuperclass()) - return false; - - auto *superclass = decl->getSuperclassDecl(); - auto result = evaluator(HasCircularInheritanceRequest{superclass}); - - // If we have a cycle, handle it and return true. - if (!result) { - using Error = CyclicalRequestError; - llvm::handleAllErrors(result.takeError(), [](const Error &E) {}); - return true; - } - return result.get(); -} - -bool -HasCircularInheritedProtocolsRequest::evaluate(Evaluator &evaluator, - ProtocolDecl *decl) const { - if (canSkipCircularityCheck(decl)) - return false; - - bool anyObject = false; - auto inherited = getDirectlyInheritedNominalTypeDecls(decl, anyObject); - for (auto &found : inherited) { - auto *protoDecl = dyn_cast(found.Item); - if (!protoDecl) - continue; - - // If we have a cycle, handle it and return true. - auto result = evaluator(HasCircularInheritedProtocolsRequest{protoDecl}); - if (!result) { - using Error = CyclicalRequestError; - llvm::handleAllErrors(result.takeError(), [](const Error &E) {}); - return true; - } - - // If the underlying request handled a cycle and returned true, bail. - if (*result) - return true; - } - return false; -} - bool HasCircularRawValueRequest::evaluate(Evaluator &evaluator, EnumDecl *decl) const { diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index ead29eca35c4b..bb7a11096c2a5 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -2011,7 +2011,7 @@ class DeclChecker : public DeclVisitor { checkGenericParams(CD); // Check for circular inheritance. - (void)CD->hasCircularInheritance(); + (void)CD->getSuperclassDecl(); // Force lowering of stored properties. (void) CD->getStoredProperties(); diff --git a/test/decl/class/circular_inheritance.swift b/test/decl/class/circular_inheritance.swift index e3da9551e645d..1ce97f8f216b6 100644 --- a/test/decl/class/circular_inheritance.swift +++ b/test/decl/class/circular_inheritance.swift @@ -2,6 +2,7 @@ // RUN: mkdir -p %t/stats-dir // RUN: %target-typecheck-verify-swift // RUN: not %target-swift-frontend -typecheck -debug-cycles %s -build-request-dependency-graph -output-request-graphviz %t.dot -stats-output-dir %t/stats-dir 2> %t.cycles +// RUN: %FileCheck -check-prefix CHECK-DOT %s < %t.dot class Left // expected-error {{'Left' inherits from itself}} expected-note {{through reference here}} : Right.Hand { // expected-note {{through reference here}} @@ -40,14 +41,6 @@ class Outer3 // expected-error {{'Outer3' inherits from itself}} expected-note { class Inner {} } -// CHECK: ===CYCLE DETECTED=== -// CHECK-LABEL: `--{{.*}}HasCircularInheritanceRequest(circular_inheritance.(file).Left@ -// CHECK-NEXT: `--{{.*}}SuperclassDeclRequest({{.*Left}} -// CHECK: `--{{.*}}InheritedDeclsReferencedRequest(circular_inheritance.(file).Left@ -// CHECK: `--{{.*}}SuperclassDeclRequest -// CHECK: `--{{.*}}InheritedDeclsReferencedRequest(circular_inheritance.(file).Right@ -// CHECK: `--{{.*}}SuperclassDeclRequest{{.*(cyclic dependency)}} - // CHECK-DOT: digraph Dependencies // CHECK-DOT: label="InheritedTypeRequest @@ -65,7 +58,6 @@ func crash() { _ = Circle() } -// FIXME: We shouldn't emit the redundant "circular reference" diagnostics here. class WithDesignatedInit : WithDesignatedInit { // expected-error@-1 {{'WithDesignatedInit' inherits from itself}} From d57a8d2c5ac1b8eb22e96b4a05025e6de312253f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 17:40:33 -0700 Subject: [PATCH 122/745] Simplify ClassDecl::walkSuperclasses(). It no longer needs to worry about circular inheritance. --- lib/AST/Decl.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index ef57f0160b45a..95710135ff5d0 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4489,10 +4489,9 @@ ClassDecl::findImplementingMethod(const AbstractFunctionDecl *Method) const { bool ClassDecl::walkSuperclasses( llvm::function_ref fn) const { - SmallPtrSet seen; auto *cls = const_cast(this); - while (cls && seen.insert(cls).second) { + while (cls) { switch (fn(cls)) { case TypeWalker::Action::Stop: return true; From f6b9d144a27860a34c677f1ffef2571ffe2284c9 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 29 Sep 2020 18:08:57 -0700 Subject: [PATCH 123/745] [update_checkout] Improved validation failure diagnostic. Previously, if an alias were defined for multiple schemes, the produced diagnostic would only say that there was a collision, but did not specify where that collision was. Here, the diagnostic is improved to read RuntimeError: Configuration file defines the alias ALIAS in both the SCHEME_NAME_1 scheme and the SCHEME_NAME_2 scheme?! --- .../update_checkout/update_checkout.py | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/utils/update_checkout/update_checkout/update_checkout.py b/utils/update_checkout/update_checkout/update_checkout.py index 5ee33d8975542..5a51b927a67b3 100755 --- a/utils/update_checkout/update_checkout/update_checkout.py +++ b/utils/update_checkout/update_checkout/update_checkout.py @@ -17,7 +17,6 @@ import re import sys import traceback -from functools import reduce from multiprocessing import Lock, Pool, cpu_count, freeze_support from build_swift.build_swift.constants import SWIFT_SOURCE_ROOT @@ -408,19 +407,16 @@ def validate_config(config): 'too.'.format(scheme_name)) # Then make sure the alias names used by our branches are unique. - # - # We do this by constructing a list consisting of len(names), - # set(names). Then we reduce over that list summing the counts and taking - # the union of the sets. We have uniqueness if the length of the union - # equals the length of the sum of the counts. - data = [(len(v['aliases']), set(v['aliases'])) - for v in config['branch-schemes'].values()] - result = reduce(lambda acc, x: (acc[0] + x[0], acc[1] | x[1]), data, - (0, set([]))) - if result[0] == len(result[1]): - return - raise RuntimeError('Configuration file has schemes with duplicate ' - 'aliases?!') + seen = dict() + for (scheme_name, scheme) in config['branch-schemes'].items(): + aliases = scheme['aliases'] + for alias in aliases: + if alias in seen: + raise RuntimeError('Configuration file defines the alias {0} ' + 'in both the {1} scheme and the {2} scheme?!' + .format(alias, seen[alias], scheme_name)) + else: + seen[alias] = scheme_name def full_target_name(repository, target): From ad20b8a6f8de4afa10a152665a95bd9f94887d4f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 18:35:03 -0700 Subject: [PATCH 124/745] Put back HasCircularInheritedProtocolsRequest::evaluate. --- lib/Sema/TypeCheckDecl.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index aba91c8154516..cb3d9cafd1683 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -233,6 +233,34 @@ static bool canSkipCircularityCheck(NominalTypeDecl *decl) { return decl->hasClangNode() || decl->wasDeserialized(); } +bool +HasCircularInheritedProtocolsRequest::evaluate(Evaluator &evaluator, + ProtocolDecl *decl) const { + if (canSkipCircularityCheck(decl)) + return false; + + bool anyObject = false; + auto inherited = getDirectlyInheritedNominalTypeDecls(decl, anyObject); + for (auto &found : inherited) { + auto *protoDecl = dyn_cast(found.Item); + if (!protoDecl) + continue; + + // If we have a cycle, handle it and return true. + auto result = evaluator(HasCircularInheritedProtocolsRequest{protoDecl}); + if (!result) { + using Error = CyclicalRequestError; + llvm::handleAllErrors(result.takeError(), [](const Error &E) {}); + return true; + } + + // If the underlying request handled a cycle and returned true, bail. + if (*result) + return true; + } + return false; +} + bool HasCircularRawValueRequest::evaluate(Evaluator &evaluator, EnumDecl *decl) const { From 2dc8a13b62bef1497ceebf1527339b63a553085b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 18:35:40 -0700 Subject: [PATCH 125/745] [AST] Simplify ClassAncestryFlagsRequest. --- lib/AST/Decl.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 95710135ff5d0..2f1fb7ba627b1 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4287,19 +4287,11 @@ AncestryOptions ClassDecl::checkAncestry() const { AncestryFlags ClassAncestryFlagsRequest::evaluate(Evaluator &evaluator, ClassDecl *value) const { - llvm::SmallPtrSet visited; - AncestryOptions result; const ClassDecl *CD = value; auto *M = value->getParentModule(); do { - // If we hit circularity, we will diagnose at some point in typeCheckDecl(). - // However we have to explicitly guard against that here because we get - // called as part of the interface type request. - if (!visited.insert(CD).second) - break; - if (CD->isGenericContext()) result |= AncestryFlags::Generic; From dec9bf680e00eac0e1ca3074bc3967f5e582c068 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 29 Sep 2020 20:11:55 -0400 Subject: [PATCH 126/745] AST: Remove unused UnqualifiedLookupFactory::Consumer field --- lib/AST/UnqualifiedLookup.cpp | 41 +---------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 790feda75fc1f..b7c4b39aa8dbd 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -88,32 +88,6 @@ namespace { SmallVectorImpl &results) const; }; - enum class AddGenericParameters { Yes, No }; - -#ifndef NDEBUG - /// A consumer for debugging that lets the UnqualifiedLookupFactory know when - /// finding something. - class InstrumentedNamedDeclConsumer : public NamedDeclConsumer { - virtual void anchor() override; - UnqualifiedLookupFactory *factory; - - public: - InstrumentedNamedDeclConsumer(UnqualifiedLookupFactory *factory, - DeclNameRef name, - SmallVectorImpl &results, - bool isTypeLookup) - : NamedDeclConsumer(name, results, isTypeLookup), factory(factory) {} - - virtual void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason, - DynamicLookupInfo dynamicLookupInfo = {}) override { - unsigned before = results.size(); - NamedDeclConsumer::foundDecl(VD, Reason, dynamicLookupInfo); - unsigned after = results.size(); - if (after > before) - factory->addedResult(results.back()); - } - }; -#endif // Inputs const DeclNameRef Name; DeclContext *const DC; @@ -128,12 +102,7 @@ namespace { const Options options; const bool isOriginallyTypeLookup; const NLOptions baseNLOptions; - // Transputs -#ifndef NDEBUG - InstrumentedNamedDeclConsumer Consumer; -#else - NamedDeclConsumer Consumer; -#endif + // Outputs SmallVectorImpl &Results; size_t &IndexOfFirstOuterResult; @@ -279,11 +248,6 @@ UnqualifiedLookupFactory::UnqualifiedLookupFactory( options(options), isOriginallyTypeLookup(options.contains(Flags::TypeLookup)), baseNLOptions(computeBaseNLOptions(options, isOriginallyTypeLookup)), - #ifdef NDEBUG - Consumer(Name, Results, isOriginallyTypeLookup), - #else - Consumer(this, Name, Results, isOriginallyTypeLookup), - #endif Results(Results), IndexOfFirstOuterResult(IndexOfFirstOuterResult) {} @@ -675,9 +639,6 @@ UnqualifiedLookupRequest::evaluate(Evaluator &evaluator, } #pragma mark debugging -#ifndef NDEBUG -void UnqualifiedLookupFactory::InstrumentedNamedDeclConsumer::anchor() {} -#endif void UnqualifiedLookupFactory::ResultFinderForTypeContext::dump() const { (void)factory; From ba4d6ede07919fe141eb134dc2ab8aee8ea1aae6 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 29 Sep 2020 20:12:58 -0400 Subject: [PATCH 127/745] AST: Remove unused NamedDeclConsumer class --- include/swift/AST/NameLookup.h | 25 ------------------------- lib/AST/NameLookup.cpp | 1 - 2 files changed, 26 deletions(-) diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 6f5af437fcc9d..8306fe934f110 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -396,31 +396,6 @@ class VectorDeclConsumer : public VisibleDeclConsumer { } }; -/// A consumer that inserts found decls with a matching name into an -/// externally-owned SmallVector. -class NamedDeclConsumer : public VisibleDeclConsumer { - virtual void anchor() override; -public: - DeclNameRef name; - SmallVectorImpl &results; - bool isTypeLookup; - - NamedDeclConsumer(DeclNameRef name, - SmallVectorImpl &results, - bool isTypeLookup) - : name(name), results(results), isTypeLookup(isTypeLookup) {} - - virtual void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason, - DynamicLookupInfo dynamicLookupInfo = {}) override { - // Give clients an opportunity to filter out non-type declarations early, - // to avoid circular validation. - if (isTypeLookup && !isa(VD)) - return; - if (VD->getName().matchesRef(name.getFullName())) - results.push_back(LookupResultEntry(VD)); - } -}; - /// A consumer that filters out decls that are not accessible from a given /// DeclContext. class AccessFilteringDeclConsumer final : public VisibleDeclConsumer { diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index fb7cbf7a19568..4c245ccbf89d2 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -44,7 +44,6 @@ using namespace swift::namelookup; void VisibleDeclConsumer::anchor() {} void VectorDeclConsumer::anchor() {} -void NamedDeclConsumer::anchor() {} ValueDecl *LookupResultEntry::getBaseDecl() const { if (BaseDC == nullptr) From 17a0b41a40cc5d8e2c20aca07d96c096bab2bffc Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 29 Sep 2020 21:38:41 -0400 Subject: [PATCH 128/745] Sema: Remove dead code from CheckRedeclarationRequest::evaluate() --- lib/Sema/TypeCheckDeclPrimary.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index ead29eca35c4b..277f33a87df9e 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -573,18 +573,6 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const { &wouldBeSwift5Redeclaration); // If there is another conflict, complain. if (isRedeclaration || wouldBeSwift5Redeclaration) { - // If the two declarations occur in the same source file, make sure - // we get the diagnostic ordering to be sensible. - if (auto otherFile = other->getDeclContext()->getParentSourceFile()) { - if (currentFile == otherFile && - current->getLoc().isValid() && - other->getLoc().isValid() && - ctx.SourceMgr.isBeforeInBuffer(current->getLoc(), - other->getLoc())) { - std::swap(current, other); - } - } - // If we're currently looking at a .sil and the conflicting declaration // comes from a .sib, don't error since we won't be considering the sil // from the .sib. So it's fine for the .sil to shadow it, since that's the @@ -798,11 +786,6 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const { } } - // Make sure we don't do this checking again for the same decl. We also - // set this at the beginning of the function, but we might have swapped - // the decls for diagnostics; so ensure we also set this for the actual - // decl we diagnosed on. - current->setCheckedRedeclaration(); break; } } From c56ab07c77184229d215213ec7a9e0b3c360fa36 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 29 Sep 2020 20:08:14 -0400 Subject: [PATCH 129/745] ASTScope: Add finishLookupInBraceStmt parameter to ASTScope::lookupLocalDecls() This will be used to implement re-declaration checking for local declarations. Currently this is handled by parse-time lookup. To make it work with ASTScope, we need to perform lookups that look into the innermost local scope only; for example, this is an invalid redeclaration: do { let x = 321 let x = 123 } But the following is fine, even though both VarDecls are in the same *DeclContext*: do { let x = 321 do { let x = 123 } } --- include/swift/AST/NameLookup.h | 25 ++++++++++++++++++++++--- lib/AST/ASTScopeLookup.cpp | 8 +++++++- lib/AST/UnqualifiedLookup.cpp | 23 +++++++++++++++++------ 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 8306fe934f110..d57cd07ea8b4c 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -594,18 +594,33 @@ class AbstractASTScopeDeclConsumer { virtual ~AbstractASTScopeDeclConsumer() = default; /// Called for every ValueDecl visible from the lookup. - /// Returns true if the lookup can be stopped at this point. - /// BaseDC is per legacy + /// /// Takes an array in order to batch the consumption before setting /// IndexOfFirstOuterResult when necessary. + /// + /// \param baseDC either a type context or the local context of a + /// `self` parameter declaration. See LookupResult for a discussion + /// of type -vs- instance lookup results. + /// + /// \return true if the lookup should be stopped at this point. virtual bool consume(ArrayRef values, DeclVisibilityKind vis, NullablePtr baseDC = nullptr) = 0; - /// Eventually this functionality should move into ASTScopeLookup + /// Look for members of a nominal type or extension scope. + /// + /// \return true if the lookup should be stopped at this point. virtual bool lookInMembers(DeclContext *const scopeDC, NominalTypeDecl *const nominal) = 0; + /// Called right before looking at the parent scope of a BraceStmt. + /// + /// \return true if the lookup should be stopped at this point. + virtual bool + finishLookupInBraceStmt(BraceStmt *stmt) { + return false; + } + #ifndef NDEBUG virtual void startingNextLookupStep() = 0; virtual void finishingLookup(std::string) const = 0; @@ -661,7 +676,11 @@ class ASTScope { /// Lookup that only finds local declarations and does not trigger /// interface type computation. + /// + /// \param stopAfterInnermostBraceStmt If lookup should consider + /// local declarations inside the innermost syntactic scope only. static void lookupLocalDecls(SourceFile *, DeclName, SourceLoc, + bool stopAfterInnermostBraceStmt, SmallVectorImpl &); /// Returns the result if there is exactly one, nullptr otherwise. diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index e848e6c12c52c..d62b59605490f 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -383,7 +383,13 @@ bool BraceStmtScope::lookupLocalsOrMembers(DeclConsumer consumer) const { localBindings.push_back(vd); } } - return consumer.consume(localBindings, DeclVisibilityKind::LocalVariable); + if (consumer.consume(localBindings, DeclVisibilityKind::LocalVariable)) + return true; + + if (consumer.finishLookupInBraceStmt(stmt)) + return true; + + return false; } bool PatternEntryInitializerScope::lookupLocalsOrMembers( diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index b7c4b39aa8dbd..20b5aad9311c1 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -736,13 +736,16 @@ namespace { class ASTScopeDeclConsumerForLocalLookup : public AbstractASTScopeDeclConsumer { - SmallVectorImpl &results; DeclName name; + bool stopAfterInnermostBraceStmt; + SmallVectorImpl &results; public: ASTScopeDeclConsumerForLocalLookup( - SmallVectorImpl &results, DeclName name) - : results(results), name(name) {} + DeclName name, bool stopAfterInnermostBraceStmt, + SmallVectorImpl &results) + : name(name), stopAfterInnermostBraceStmt(stopAfterInnermostBraceStmt), + results(results) {} bool consume(ArrayRef values, DeclVisibilityKind vis, NullablePtr baseDC) override { @@ -753,7 +756,7 @@ class ASTScopeDeclConsumerForLocalLookup results.push_back(value); } - return !results.empty(); + return (!stopAfterInnermostBraceStmt && !results.empty()); } bool lookInMembers(DeclContext *const, @@ -761,6 +764,10 @@ class ASTScopeDeclConsumerForLocalLookup return true; } + bool finishLookupInBraceStmt(BraceStmt *stmt) override { + return stopAfterInnermostBraceStmt; + } + #ifndef NDEBUG void startingNextLookupStep() override {} void finishingLookup(std::string) const override {} @@ -773,15 +780,19 @@ class ASTScopeDeclConsumerForLocalLookup /// Lookup that only finds local declarations and does not trigger /// interface type computation. void ASTScope::lookupLocalDecls(SourceFile *sf, DeclName name, SourceLoc loc, + bool stopAfterInnermostBraceStmt, SmallVectorImpl &results) { - ASTScopeDeclConsumerForLocalLookup consumer(results, name); + ASTScopeDeclConsumerForLocalLookup consumer(name, stopAfterInnermostBraceStmt, + results); ASTScope::unqualifiedLookup(sf, loc, consumer); } ValueDecl *ASTScope::lookupSingleLocalDecl(SourceFile *sf, DeclName name, SourceLoc loc) { SmallVector result; - ASTScope::lookupLocalDecls(sf, name, loc, result); + ASTScope::lookupLocalDecls(sf, name, loc, + /*finishLookupInBraceStmt=*/false, + result); if (result.size() != 1) return nullptr; return result[0]; From d58583850d323cc8538f4508d4fcc16395556439 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 29 Sep 2020 21:38:30 -0400 Subject: [PATCH 130/745] Sema: Check for re-declarations in local context in CheckRedeclarationRequest --- lib/Sema/TypeCheckDeclPrimary.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 277f33a87df9e..7acca6caf4626 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -489,9 +489,11 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const { // FIXME: Should restrict this to the source file we care about. DeclContext *currentDC = current->getDeclContext(); SourceFile *currentFile = currentDC->getParentSourceFile(); - if (!currentFile || currentDC->isLocalContext()) + if (!currentFile) return std::make_tuple<>(); + auto &ctx = current->getASTContext(); + // Find other potential definitions. SmallVector otherDefinitions; if (currentDC->isTypeContext()) { @@ -500,7 +502,17 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const { auto found = nominal->lookupDirect(current->getBaseName()); otherDefinitions.append(found.begin(), found.end()); } + } else if (currentDC->isLocalContext()) { + if (ctx.LangOpts.DisableParserLookup) { + if (!current->isImplicit()) { + ASTScope::lookupLocalDecls(currentFile, current->getBaseName(), + current->getLoc(), + /*stopAfterInnermostBraceStmt=*/true, + otherDefinitions); + } + } } else { + assert(currentDC->isModuleScopeContext()); // Look within a module context. currentFile->getParentModule()->lookupValue(current->getBaseName(), NLKind::QualifiedLookup, @@ -512,7 +524,6 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const { OverloadSignature currentSig = current->getOverloadSignature(); CanType currentSigType = current->getOverloadSignatureType(); ModuleDecl *currentModule = current->getModuleContext(); - auto &ctx = current->getASTContext(); for (auto other : otherDefinitions) { // Skip invalid declarations and ourselves. // @@ -547,7 +558,8 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const { // shadows a non-private one, but only in the file where the shadowing // happens. We will warn on conflicting non-private declarations in both // files. - if (!other->isAccessibleFrom(currentDC)) + if (!currentDC->isLocalContext() && + !other->isAccessibleFrom(currentDC)) continue; // Skip invalid declarations. From 18f5f94ce25a6bdc947a551ba4950626e04d9bb6 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 29 Sep 2020 21:59:13 -0400 Subject: [PATCH 131/745] Sema: Check for duplicate parameter and generic parameter names when parser lookup is off The existing redeclaration checking can be extended for declarations in local scope, but it won't catch these. --- lib/Sema/TypeCheckAttr.cpp | 6 -- lib/Sema/TypeCheckDeclPrimary.cpp | 67 +++++++++++++++--- lib/Sema/TypeCheckStmt.cpp | 8 ++- lib/Sema/TypeChecker.h | 4 +- test/Sema/redeclaration-checking.swift | 96 ++++++++++++++++++++++++++ 5 files changed, 162 insertions(+), 19 deletions(-) create mode 100644 test/Sema/redeclaration-checking.swift diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 309bc313707ae..ff1db56c28832 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -3206,12 +3206,6 @@ void AttributeChecker::visitNonEphemeralAttr(NonEphemeralAttr *attr) { attr->setInvalid(); } -void TypeChecker::checkParameterAttributes(ParameterList *params) { - for (auto param: *params) { - checkDeclAttributes(param); - } -} - void AttributeChecker::checkOriginalDefinedInAttrs(Decl *D, ArrayRef Attrs) { if (Attrs.empty()) diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 7acca6caf4626..19bb45497f825 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -397,6 +397,28 @@ static void checkForEmptyOptionSet(const VarDecl *VD) { .fixItReplace(args->getSourceRange(), "([])"); } +template +static void diagnoseDuplicateDecls(const T &decls) { + llvm::SmallDenseMap names; + for (auto *current : decls) { + if (!current->getASTContext().LangOpts.DisableParserLookup) + return; + + if (!current->hasName() || current->isImplicit()) + continue; + + auto found = names.insert(std::make_pair(current->getBaseName(), current)); + if (!found.second) { + auto *other = found.first->second; + + current->getASTContext().Diags.diagnoseWithNotes( + current->diagnose(diag::invalid_redecl, + current->getName()), [&]() { + other->diagnose(diag::invalid_redecl_prev, other->getName()); + }); + } + } +} /// Check the inheritance clauses generic parameters along with any /// requirements stored within the generic parameter list. @@ -410,10 +432,13 @@ static void checkGenericParams(GenericContext *ownerCtx) { checkInheritanceClause(gp); } - // Force visitation of each of the requirements here. + // Force resolution of interface types written in requirements here. WhereClauseOwner(ownerCtx) .visitRequirements(TypeResolutionStage::Interface, [](Requirement, RequirementRepr *) { return false; }); + + // Check for duplicate generic parameter names. + diagnoseDuplicateDecls(*genericParams); } template @@ -1288,6 +1313,22 @@ static void maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl) { diagnoseClassWithoutInitializers(classDecl); } +void TypeChecker::checkParameterList(ParameterList *params) { + for (auto param: *params) { + checkDeclAttributes(param); + } + + // Check for duplicate parameter names. + diagnoseDuplicateDecls(*params); +} + +void TypeChecker::diagnoseDuplicateBoundVars(Pattern *pattern) { + SmallVector boundVars; + pattern->collectVariables(boundVars); + + diagnoseDuplicateDecls(boundVars); +} + namespace { class DeclChecker : public DeclVisitor { public: @@ -1709,7 +1750,7 @@ class DeclChecker : public DeclVisitor { (void) SD->isSetterMutating(); (void) SD->getImplInfo(); - TypeChecker::checkParameterAttributes(SD->getIndices()); + TypeChecker::checkParameterList(SD->getIndices()); checkDefaultArguments(SD->getIndices()); @@ -1735,6 +1776,7 @@ class DeclChecker : public DeclVisitor { TypeChecker::checkDeclAttributes(TAD); checkAccessControl(TAD); + checkGenericParams(TAD); } void visitOpaqueTypeDecl(OpaqueTypeDecl *OTD) { @@ -2260,15 +2302,18 @@ class DeclChecker : public DeclVisitor { (void) FD->getOperatorDecl(); (void) FD->getDynamicallyReplacedDecl(); - if (!FD->isInvalid()) { - checkGenericParams(FD); - TypeChecker::checkReferencedGenericParams(FD); - TypeChecker::checkProtocolSelfRequirements(FD); - } + if (!isa(FD)) { + if (!FD->isInvalid()) { + checkGenericParams(FD); + TypeChecker::checkReferencedGenericParams(FD); + TypeChecker::checkProtocolSelfRequirements(FD); + } - checkAccessControl(FD); + checkAccessControl(FD); + + TypeChecker::checkParameterList(FD->getParameters()); + } - TypeChecker::checkParameterAttributes(FD->getParameters()); TypeChecker::checkDeclAttributes(FD); if (!checkOverrides(FD)) { @@ -2376,7 +2421,7 @@ class DeclChecker : public DeclVisitor { TypeChecker::checkDeclAttributes(EED); if (auto *PL = EED->getParameterList()) { - TypeChecker::checkParameterAttributes(PL); + TypeChecker::checkParameterList(PL); checkDefaultArguments(PL); } @@ -2532,7 +2577,7 @@ class DeclChecker : public DeclVisitor { } TypeChecker::checkDeclAttributes(CD); - TypeChecker::checkParameterAttributes(CD->getParameters()); + TypeChecker::checkParameterList(CD->getParameters()); // Check whether this initializer overrides an initializer in its // superclass. diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 06860ed87c7ca..d68b37d5e2de6 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -504,6 +504,8 @@ static bool typeCheckConditionForStatement(LabeledConditionalStmt *stmt, } elt.setPattern(pattern); + TypeChecker::diagnoseDuplicateBoundVars(pattern); + // Check the pattern, it allows unspecified types because the pattern can // provide type information. auto contextualPattern = ContextualPattern::forRawPattern(pattern, dc); @@ -956,6 +958,8 @@ class StmtChecker : public StmtVisitor { if (TypeChecker::typeCheckForEachBinding(DC, S)) return nullptr; + TypeChecker::diagnoseDuplicateBoundVars(S->getPattern()); + // Type-check the body of the loop. auto sourceFile = DC->getParentSourceFile(); checkLabeledStmtShadowing(getASTContext(), sourceFile, S); @@ -1032,6 +1036,8 @@ class StmtChecker : public StmtVisitor { } labelItem.setPattern(pattern, /*resolved=*/true); + TypeChecker::diagnoseDuplicateBoundVars(pattern); + // Otherwise for each variable in the pattern, make sure its type is // identical to the initial case decl and stash the previous case decl as // the parent of the decl. @@ -2039,7 +2045,7 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator, } bool TypeChecker::typeCheckClosureBody(ClosureExpr *closure) { - checkParameterAttributes(closure->getParameters()); + TypeChecker::checkParameterList(closure->getParameters()); BraceStmt *body = closure->getBody(); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index dbd5f63e86b05..d423d2cad2430 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -497,7 +497,9 @@ void typeCheckDecl(Decl *D); void addImplicitDynamicAttribute(Decl *D); void checkDeclAttributes(Decl *D); -void checkParameterAttributes(ParameterList *params); +void checkParameterList(ParameterList *params); + +void diagnoseDuplicateBoundVars(Pattern *pattern); Type checkReferenceOwnershipAttr(VarDecl *D, Type interfaceType, ReferenceOwnershipAttr *attr); diff --git a/test/Sema/redeclaration-checking.swift b/test/Sema/redeclaration-checking.swift new file mode 100644 index 0000000000000..9445d612f94b1 --- /dev/null +++ b/test/Sema/redeclaration-checking.swift @@ -0,0 +1,96 @@ +// RUN: %target-typecheck-verify-swift -disable-parser-lookup + +// Test redeclaration checking in local context. + +func test1() { + let x = 123 // expected-note{{'x' previously declared here}} + func f() {} // expected-note{{'f()' previously declared here}} + struct S {} // expected-note{{'S' previously declared here}} + let x = 321 // expected-error{{invalid redeclaration of 'x'}} + func f() {} // expected-error{{invalid redeclaration of 'f()'}} + struct S {} // expected-error{{invalid redeclaration of 'S'}} +} + +func test2() { + let x = 123 // expected-warning {{never used}} + func f() {} + struct S {} + do { + let x = 321 // expected-warning {{never used}} + func f() {} + struct S {} + } +} + +func test3(_: T, _: T) {} +// expected-note@-1 {{'T' previously declared here}} +// expected-error@-2 {{invalid redeclaration of 'T'}} +// expected-error@-3 {{generic parameter 'T' is not used in function signature}} + +func test4(x: Int, x: Int) {} +// expected-note@-1 {{'x' previously declared here}} +// expected-error@-2 {{invalid redeclaration of 'x'}} + +struct Test4 {} +// expected-note@-1 {{'T' previously declared here}} +// expected-error@-2 {{invalid redeclaration of 'T'}} + +typealias Test5 = () +// expected-note@-1 {{'T' previously declared here}} +// expected-error@-2 {{invalid redeclaration of 'T'}} + +enum E { + case test6(x: Int, x: Int) + // expected-note@-1 {{'x' previously declared here}} + // expected-error@-2 {{invalid redeclaration of 'x'}} + + subscript(x: Int, x: Int) -> Int { return 0 } + // expected-note@-1 {{'x' previously declared here}} + // expected-error@-2 {{invalid redeclaration of 'x'}} +} + +_ = { (x: Int, x: Int) in } +// expected-note@-1 {{'x' previously declared here}} +// expected-error@-2 {{invalid redeclaration of 'x'}} + +enum MyError : Error { + case error(Int, Int) +} + +func stmtTest() { + let n: (Int, Int)? = nil + + if case (let x, let x)? = n {} + // expected-note@-1 {{'x' previously declared here}} + // expected-error@-2 {{invalid redeclaration of 'x'}} + // expected-warning@-3 2{{never used}} + + for case (let x, let x) in [(Int, Int)]() {} + // expected-note@-1 {{'x' previously declared here}} + // expected-error@-2 {{invalid redeclaration of 'x'}} + // expected-warning@-3 2{{never used}} + + switch n { + case (let x, let x)?: _ = () + // expected-note@-1 {{'x' previously declared here}} + // expected-error@-2 {{invalid redeclaration of 'x'}} + // expected-warning@-3 2{{never used}} + case nil: _ = () + } + + while case (let x, let x)? = n {} + // expected-note@-1 {{'x' previously declared here}} + // expected-error@-2 {{invalid redeclaration of 'x'}} + // expected-warning@-3 2{{never used}} + + guard case (let x, let x)? = n else {} + // expected-note@-1 {{'x' previously declared here}} + // expected-error@-2 {{invalid redeclaration of 'x'}} + // expected-warning@-3 2{{never used}} + + do {} catch MyError.error(let x, let x) {} + // expected-note@-1 {{'x' previously declared here}} + // expected-error@-2 {{invalid redeclaration of 'x'}} + // expected-warning@-3 2{{never used}} + // expected-warning@-4 {{unreachable}} +} \ No newline at end of file From 34996bf9a6b58127c3e4a706329c53be3ba6dd0f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 21:32:27 -0700 Subject: [PATCH 132/745] [Concurrency] Give Actor enqueue(partialTask:) its own ptrauth discriminator. For actor class's implementations of `enqueue(partialTask:)`, use a fixed ptrauth discriminator. Paired with the fixed vtable location, this allows one to invoke this operation on any native actor instance without knowing the specific type. --- include/swift/ABI/MetadataValues.h | 3 +++ lib/IRGen/GenPointerAuth.cpp | 11 +++++++++++ test/IRGen/ptrauth-actor.swift | 24 ++++++++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 test/IRGen/ptrauth-actor.swift diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index 54232972ca9fd..e45629dce240a 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -1177,6 +1177,9 @@ namespace SpecialPointerAuthDiscriminators { /// Resilient class stub initializer callback const uint16_t ResilientClassStubInitCallback = 0xC671; + + /// Actor enqueue(partialTask:). + const uint16_t ActorEnqueuePartialTask = 0x8f3d; } /// The number of arguments that will be passed directly to a generic diff --git a/lib/IRGen/GenPointerAuth.cpp b/lib/IRGen/GenPointerAuth.cpp index fa6821b267900..6f2a7d2c17fc7 100644 --- a/lib/IRGen/GenPointerAuth.cpp +++ b/lib/IRGen/GenPointerAuth.cpp @@ -400,6 +400,17 @@ PointerAuthEntity::getDeclDiscriminator(IRGenModule &IGM) const { assert(!constant.isForeign && "discriminator for foreign declaration not supported yet!"); + // Special case: methods that are witnesses to Actor.enqueue(partialTask:) + // have their own descriminator, which is shared across all actor classes. + if (constant.hasFuncDecl()) { + auto func = dyn_cast(constant.getFuncDecl()); + if (func->isActorEnqueuePartialTaskWitness()) { + cache = IGM.getSize( + Size(SpecialPointerAuthDiscriminators::ActorEnqueuePartialTask)); + return cache; + } + } + auto mangling = constant.mangle(); cache = getDiscriminatorForString(IGM, mangling); return cache; diff --git a/test/IRGen/ptrauth-actor.swift b/test/IRGen/ptrauth-actor.swift new file mode 100644 index 0000000000000..4cd1f44ccb567 --- /dev/null +++ b/test/IRGen/ptrauth-actor.swift @@ -0,0 +1,24 @@ +// RUN: %swift -swift-version 5 -target arm64e-apple-macos11.0 -parse-stdlib %s -emit-ir -disable-llvm-optzns -enable-experimental-concurrency -o - | %FileCheck %s + +// REQUIRES: CODEGENERATOR=ARM +// REQUIRES: concurrency +// REQUIRES: CPU=arm64e +// REQUIRES: OS=macosx + +import _Concurrency +import Swift + +public actor class A1 { + var x: Int = 17 +} + +open actor class A3: Actor { + open func f() { } +} + +// enqueue(partialTask:) has the same discriminator across all classes. +// CHECK: s4main2A1C7enqueue11partialTasky12_Concurrency012PartialAsyncE0V_tF.ptrauth{{.*}}i64 36669 + +// CHECK: @"$s4main2A3CyxG12_Concurrency5ActorAaeFP7enqueue11partialTaskyAE012PartialAsyncG0V_tFTW" +// CHECK-NOT: ret void +// CHECK: call i64 @llvm.ptrauth.blend.i64(i64 %{{[0-9]+}}, i64 36669) From e5c1491c6a77a21c9a6c2465de782d4777e31903 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 22:11:59 -0700 Subject: [PATCH 133/745] [Concurrency] Ban actor-isolated operations from being @objc. Actor-isolated operations must not be directly accessible from anywhere that is not already guaranteed to be running within the actor context. Prevent such operations from being `@objc`, because that would allow Objective-C code to violate actor isolation. --- include/swift/AST/DiagnosticsSema.def | 3 +++ lib/Sema/TypeCheckDeclObjC.cpp | 39 +++++++++++++++++++++++++-- test/attr/attr_objc_async.swift | 29 ++++++++++++++++++-- 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index f353c230f813d..6bd1a32a6eb76 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4101,6 +4101,9 @@ ERROR(not_objc_function_async,none, "'async' function cannot be represented in Objective-C", ()) NOTE(not_objc_function_type_async,none, "'async' function types cannot be represented in Objective-C", ()) +ERROR(actor_isolated_objc,none, + "actor-isolated %0 %1 cannot be @objc", + (DescriptiveDeclKind, DeclName)) NOTE(protocol_witness_async_conflict,none, "candidate is %select{not |}0'async', but protocol requirement is%select{| not}0", (bool)) diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 1d99cc1b9cc91..2659e564d6d92 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "TypeCheckObjC.h" #include "TypeChecker.h" +#include "TypeCheckConcurrency.h" #include "TypeCheckProtocol.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" @@ -368,6 +369,32 @@ static bool checkObjCInForeignClassContext(const ValueDecl *VD, return true; } +/// Actor-isolated declarations cannot be @objc. +static bool checkObjCActorIsolation(const ValueDecl *VD, + ObjCReason Reason) { + // Check actor isolation. + bool Diagnose = shouldDiagnoseObjCReason(Reason, VD->getASTContext()); + + switch (getActorIsolation(const_cast(VD))) { + case ActorIsolation::ActorInstance: + // Actor-isolated functions cannot be @objc. + if (Diagnose) { + VD->diagnose( + diag::actor_isolated_objc, VD->getDescriptiveKind(), VD->getName()); + describeObjCReason(VD, Reason); + if (auto FD = dyn_cast(VD)) { + addAsyncNotes(const_cast(FD)); + } + } + return true; + + case ActorIsolation::ActorPrivileged: + case ActorIsolation::Independent: + case ActorIsolation::Unspecified: + return false; + } +} + static VersionRange getMinOSVersionForClassStubs(const llvm::Triple &target) { if (target.isMacOSX()) return VersionRange::allGTE(llvm::VersionTuple(10, 15, 0)); @@ -512,6 +539,8 @@ bool swift::isRepresentableInObjC( return false; if (checkObjCInExtensionContext(AFD, Diagnose)) return false; + if (checkObjCActorIsolation(AFD, Reason)) + return false; if (AFD->isOperator()) { AFD->diagnose((isa(AFD->getDeclContext()) @@ -686,10 +715,12 @@ bool swift::isRepresentableInObjC( Type resultType = FD->mapTypeIntoContext(FD->getResultInterfaceType()); if (auto tupleType = resultType->getAs()) { for (const auto &tupleElt : tupleType->getElements()) { - addCompletionHandlerParam(tupleElt.getType()); + if (addCompletionHandlerParam(tupleElt.getType())) + return false; } } else { - addCompletionHandlerParam(resultType); + if (addCompletionHandlerParam(resultType)) + return false; } // For a throwing asynchronous function, an Error? parameter is added @@ -939,6 +970,8 @@ bool swift::isRepresentableInObjC(const VarDecl *VD, ObjCReason Reason) { if (checkObjCInForeignClassContext(VD, Reason)) return false; + if (checkObjCActorIsolation(VD, Reason)) + return false; if (!Diagnose || Result) return Result; @@ -967,6 +1000,8 @@ bool swift::isRepresentableInObjC(const SubscriptDecl *SD, ObjCReason Reason) { return false; if (checkObjCWithGenericParams(SD, Reason)) return false; + if (checkObjCActorIsolation(SD, Reason)) + return false; // ObjC doesn't support class subscripts. if (!SD->isInstanceMember()) { diff --git a/test/attr/attr_objc_async.swift b/test/attr/attr_objc_async.swift index a3a2aad0c459d..fb8e571dc9a09 100644 --- a/test/attr/attr_objc_async.swift +++ b/test/attr/attr_objc_async.swift @@ -3,10 +3,11 @@ // RUN: not %target-swift-frontend -typecheck -dump-ast -disable-objc-attr-requires-foundation-module %s -swift-version 5 -enable-source-import -I %S/Inputs -enable-experimental-concurrency > %t.ast // RUN: %FileCheck -check-prefix CHECK-DUMP %s < %t.ast // REQUIRES: objc_interop +// REQUIRES: concurrency import Foundation -// CHECK: class Concurrency -class Concurrency { +// CHECK: class MyClass +class MyClass { // CHECK: @objc func doBigJob() async -> Int // CHECK-DUMP: func_decl{{.*}}doBigJob{{.*}}foreign_async=@convention(block) (Int) -> (),completion_handler_param=0 @objc func doBigJob() async -> Int { return 0 } @@ -21,3 +22,27 @@ class Concurrency { @objc class func createAsynchronously() async -> Self? { nil } // expected-error@-1{{asynchronous method returning 'Self' cannot be '@objc'}} } + +// Actor class exporting Objective-C entry points. + +// CHECK: class MyActor +actor class MyActor { + // CHECK: @objc func doBigJob() async -> Int + // CHECK-DUMP: func_decl{{.*}}doBigJob{{.*}}foreign_async=@convention(block) (Int) -> (),completion_handler_param=0 + @objc func doBigJob() async -> Int { return 0 } + + // CHECK: @objc func doBigJobOrFail(_: Int) async throws -> (AnyObject, Int) + // CHECK-DUMP: func_decl{{.*}}doBigJobOrFail{{.*}}foreign_async=@convention(block) (Optional, Int, Optional) -> (),completion_handler_param=1,error_param=2 + @objc func doBigJobOrFail(_: Int) async throws -> (AnyObject, Int) { return (self, 0) } + + // Actor-isolated entities cannot be exposed to Objective-C. + @objc func synchronousBad() { } // expected-error{{actor-isolated instance method 'synchronousBad()' cannot be @objc}} + // expected-note@-1{{add 'async' to function 'synchronousBad()' to make it asynchronous}} + // expected-note@-2{{add '@asyncHandler' to function 'synchronousBad()' to create an implicit asynchronous context}} + + @objc var badProp: AnyObject { self } // expected-error{{actor-isolated property 'badProp' cannot be @objc}} + @objc subscript(index: Int) -> AnyObject { self } // expected-error{{actor-isolated subscript 'subscript(_:)' cannot be @objc}} + + // CHECK: @objc @actorIndependent func synchronousGood() + @objc @actorIndependent func synchronousGood() { } +} From 1e040ac3235c7cb0d68a41460aafcf6ffd76ea6c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 22:34:15 -0700 Subject: [PATCH 134/745] [Concurrency] Note that 'actor' is a decl modifier, not an attribute. --- include/swift/AST/Attr.def | 2 +- test/decl/class/actor/noconcurrency.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 93d8308bb34cc..bd0af44538512 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -567,7 +567,7 @@ SIMPLE_DECL_ATTR(asyncHandler, AsyncHandler, 101) CONTEXTUAL_SIMPLE_DECL_ATTR(actor, Actor, - OnClass | ConcurrencyOnly | + DeclModifier | OnClass | ConcurrencyOnly | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, 102) diff --git a/test/decl/class/actor/noconcurrency.swift b/test/decl/class/actor/noconcurrency.swift index 48e4c7d018bc5..2773bafa44d6f 100644 --- a/test/decl/class/actor/noconcurrency.swift +++ b/test/decl/class/actor/noconcurrency.swift @@ -1,4 +1,4 @@ // RUN: %target-typecheck-verify-swift -actor class C { } // expected-error{{'actor' attribute is only valid when experimental concurrency is enabled}} +actor class C { } // expected-error{{'actor' modifier is only valid when experimental concurrency is enabled}} From 72bf83653a8d4af599538ac53af0e7dd85c24722 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 22:34:27 -0700 Subject: [PATCH 135/745] [Concurrency] Allow actor classes to inherit from NSObject. NSObject is guaranteed to have no state and no Swift vtable, and is necessary for Swift classes to implement the NSObject protocol. Allow it (and only it) as the superclass of an actor class, so that actor classes can be exposed to Objective-C. --- lib/Sema/TypeCheckConcurrency.cpp | 8 ++++++++ test/attr/attr_objc_async.swift | 3 +++ 2 files changed, 11 insertions(+) diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index b5c19ab93c570..a7543e8265f6e 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -180,6 +180,14 @@ bool IsActorRequest::evaluate( if (superclassDecl->isActor()) return true; + // The superclass is 'NSObject', which is known to have no state and no + // superclass. + if (superclassDecl->hasClangNode() && + superclassDecl->getName().is("NSObject") && + superclassDecl->getModuleContext()->getName().is("ObjectiveC") && + actorAttr != nullptr) + return true; + // This class cannot be an actor; complain if the 'actor' modifier was // provided. if (actorAttr) { diff --git a/test/attr/attr_objc_async.swift b/test/attr/attr_objc_async.swift index fb8e571dc9a09..59300a20cdb09 100644 --- a/test/attr/attr_objc_async.swift +++ b/test/attr/attr_objc_async.swift @@ -46,3 +46,6 @@ actor class MyActor { // CHECK: @objc @actorIndependent func synchronousGood() @objc @actorIndependent func synchronousGood() { } } + +// CHECK: @objc actor class MyObjCActor +@objc actor class MyObjCActor: NSObject { } From 28220ff259b1d796ac7d9fdd3a53654cab847fe9 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Mon, 28 Sep 2020 18:20:01 +0200 Subject: [PATCH 136/745] SILCombine: make the alloc_stack-enum optimization a bit more tolerant. When eliminating an enum from an alloc_stack, accept "dead" inject_enum_addr instructions, which inject different enum cases. --- .../SILCombiner/SILCombinerMiscVisitors.cpp | 51 +++++++++++++++--- ...cast_optimizer_conditional_conformance.sil | 2 - test/SILOptimizer/sil_combine_enums.sil | 54 +++++++++++++++++++ 3 files changed, 98 insertions(+), 9 deletions(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp index d47dca47efee1..144c1d30a04e7 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp @@ -453,6 +453,10 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { return false; EnumElementDecl *element = nullptr; + unsigned numInits =0; + unsigned numTakes = 0; + SILBasicBlock *initBlock = nullptr; + SILBasicBlock *takeBlock = nullptr; SILType payloadType; // First step: check if the stack location is only used to hold one specific @@ -463,6 +467,8 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { case SILInstructionKind::DebugValueAddrInst: case SILInstructionKind::DestroyAddrInst: case SILInstructionKind::DeallocStackInst: + case SILInstructionKind::InjectEnumAddrInst: + // We'll check init_enum_addr below. break; case SILInstructionKind::InitEnumDataAddrInst: { auto *ieda = cast(user); @@ -472,13 +478,8 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { element = el; assert(!payloadType || payloadType == ieda->getType()); payloadType = ieda->getType(); - break; - } - case SILInstructionKind::InjectEnumAddrInst: { - auto *el = cast(user)->getElement(); - if (element && el != element) - return false; - element = el; + numInits++; + initBlock = user->getParent(); break; } case SILInstructionKind::UncheckedTakeEnumDataAddrInst: { @@ -486,6 +487,8 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { if (element && el != element) return false; element = el; + numTakes++; + takeBlock = user->getParent(); break; } default: @@ -495,6 +498,24 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { if (!element || !payloadType) return false; + // If the enum has a single init-take pair in a single block, we know that + // the enum cannot contain any valid payload outside that init-take pair. + // + // This also means that we can ignore any inject_enum_addr of another enum + // case, because this can only inject a case without a payload. + bool singleInitTakePair = + (numInits == 1 && numTakes == 1 && initBlock == takeBlock); + if (!singleInitTakePair) { + // No single init-take pair: We cannot ignore inject_enum_addrs with a + // mismatching case. + for (auto *use : AS->getUses()) { + if (auto *inject = dyn_cast(use->getUser())) { + if (inject->getElement() != element) + return false; + } + } + } + // Second step: replace the enum alloc_stack with a payload alloc_stack. auto *newAlloc = Builder.createAllocStack( AS->getLoc(), payloadType, AS->getVarInfo(), AS->hasDynamicLifetime()); @@ -508,6 +529,22 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { eraseInstFromFunction(*user); break; case SILInstructionKind::DestroyAddrInst: + if (singleInitTakePair) { + // It's not possible that the enum has a payload at the destroy_addr, + // because it must have already been taken by the take of the + // single init-take pair. + // We _have_ to remove the destroy_addr, because we also remove all + // inject_enum_addrs which might inject a payload-less case before + // the destroy_addr. + eraseInstFromFunction(*user); + } else { + // The enum payload can still be valid at the destroy_addr, so we have + // to keep the destroy_addr. Just replace the enum with the payload + // (and because it's not a singleInitTakePair, we can be sure that the + // enum cannot have any other case than the payload case). + use->set(newAlloc); + } + break; case SILInstructionKind::DeallocStackInst: use->set(newAlloc); break; diff --git a/test/SILOptimizer/cast_optimizer_conditional_conformance.sil b/test/SILOptimizer/cast_optimizer_conditional_conformance.sil index 87688a532e593..355dc0d053251 100644 --- a/test/SILOptimizer/cast_optimizer_conditional_conformance.sil +++ b/test/SILOptimizer/cast_optimizer_conditional_conformance.sil @@ -98,8 +98,6 @@ bb6: // CHECK: [[IEDA:%.*]] = init_enum_data_addr [[OPT]] : $*Optional, #Optional.some!enumelt // CHECK: checked_cast_addr_br take_always S1 in [[VAL]] : $*S1 to HasFoo in [[IEDA]] : $*HasFoo, bb1, bb2 // bbs... -// CHECK: switch_enum_addr [[OPT]] : $*Optional, case #Optional.some!enumelt: bb4, case #Optional.none!enumelt: bb5 -// bbs... // CHECK: [[UNWRAP:%.*]] = unchecked_take_enum_data_addr [[OPT]] : $*Optional, #Optional.some!enumelt // CHECK: copy_addr [take] [[UNWRAP]] to [initialization] [[EXIS]] : $*HasFoo // CHECK: [[OPEN:%.*]] = open_existential_addr immutable_access [[EXIS]] : $*HasFoo to $*@opened("4E16CBC0-FD9F-11E8-A311-D0817AD9F6DD") HasFoo diff --git a/test/SILOptimizer/sil_combine_enums.sil b/test/SILOptimizer/sil_combine_enums.sil index 03378b8025f95..89a82f48777d8 100644 --- a/test/SILOptimizer/sil_combine_enums.sil +++ b/test/SILOptimizer/sil_combine_enums.sil @@ -504,6 +504,7 @@ enum MP { } sil @take_s : $@convention(thin) (@in S) -> () +sil @take_t : $@convention(thin) (@in T) -> () sil @use_mp : $@convention(thin) (@in_guaranteed MP) -> () // CHECK-LABEL: sil @expand_alloc_stack_of_enum1 @@ -568,6 +569,37 @@ bb3: return %11 : $() } +// CHECK-LABEL: sil @expand_alloc_stack_of_enum_multiple_cases +// CHECK: [[A:%[0-9]+]] = alloc_stack $T +// CHECK: bb1: +// CHECK-NEXT: store %0 to [[A]] +// CHECK: apply {{%[0-9]+}}([[A]]) +// CHECK: bb2: +// CHECK-NEXT: br bb3 +// CHECK: bb3: +// CHECK: } // end sil function 'expand_alloc_stack_of_enum_multiple_cases' +sil @expand_alloc_stack_of_enum_multiple_cases : $@convention(method) (T) -> () { +bb0(%0 : $T): + %1 = alloc_stack $X + cond_br undef, bb1, bb2 +bb1: + %2 = init_enum_data_addr %1 : $*X, #X.some!enumelt + store %0 to %2 : $*T + inject_enum_addr %1 : $*X, #X.some!enumelt + %7 = unchecked_take_enum_data_addr %1 : $*X, #X.some!enumelt + %8 = function_ref @take_t : $@convention(thin) (@in T) -> () + %9 = apply %8(%7) : $@convention(thin) (@in T) -> () + br bb3 +bb2: + inject_enum_addr %1 : $*X, #X.none!enumelt + destroy_addr %1 : $*X + br bb3 +bb3: + dealloc_stack %1 : $*X + %11 = tuple () + return %11 : $() +} + // CHECK-LABEL: sil @dont_expand_alloc_stack_of_enum_multiple_cases // CHECK: alloc_stack $MP // CHECK: } // end sil function 'dont_expand_alloc_stack_of_enum_multiple_cases' @@ -592,6 +624,28 @@ bb3: return %11 : $() } +// CHECK-LABEL: sil @dont_expand_alloc_stack_of_enum_multiple_cases2 +// CHECK: alloc_stack $X +// CHECK: } // end sil function 'dont_expand_alloc_stack_of_enum_multiple_cases2' +sil @dont_expand_alloc_stack_of_enum_multiple_cases2 : $@convention(method) (T) -> () { +bb0(%0 : $T): + %1 = alloc_stack $X + cond_br undef, bb1, bb2 +bb1: + %2 = init_enum_data_addr %1 : $*X, #X.some!enumelt + store %0 to %2 : $*T + inject_enum_addr %1 : $*X, #X.some!enumelt + br bb3 +bb2: + inject_enum_addr %1 : $*X, #X.none!enumelt + br bb3 +bb3: + destroy_addr %1 : $*X + dealloc_stack %1 : $*X + %11 = tuple () + return %11 : $() +} + // CHECK-LABEL: sil @dont_expand_alloc_stack_of_enum_unknown_use // CHECK: alloc_stack $MP // CHECK: } // end sil function 'dont_expand_alloc_stack_of_enum_unknown_use' From 0a71d0fbea7cafd35d35bf2a4c0bd2be73a36e47 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Mon, 28 Sep 2020 18:23:12 +0200 Subject: [PATCH 137/745] SimplifyCFG: allow jump-threading for switch_enum_data_addr instructions. If the branch-block injects a certain enum case and the destination switches on that enum, it's worth jump threading. E.g. inject_enum_addr %enum : $*Optional, #Optional.some ... // no memory writes here br DestBB DestBB: ... // no memory writes here switch_enum_addr %enum : $*Optional, case #Optional.some ... This enables removing all code with optionals in a loop, which iterates over an array of address-only elements, e.g. func test(_ items: [T]) { for i in items { print(i) } } --- lib/SILOptimizer/Transforms/SimplifyCFG.cpp | 74 +++++++++++++++-- test/SILOptimizer/enum_jump_thread.sil | 80 +++++++++++++++++++ test/SILOptimizer/generic_loop.swift | 17 ++++ .../sil_combine_concrete_existential.swift | 7 +- 4 files changed, 167 insertions(+), 11 deletions(-) create mode 100644 test/SILOptimizer/generic_loop.swift diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index 44ee8e01f3998..176588416d0dd 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -916,16 +916,57 @@ static bool couldRemoveRelease(SILBasicBlock *SrcBB, SILValue SrcV, return IsReleaseOfDest; } +/// Returns true if any instruction in \p block may write memory. +static bool blockMayWriteMemory(SILBasicBlock *block) { + for (auto instAndIdx : llvm::enumerate(*block)) { + if (instAndIdx.value().mayWriteToMemory()) + return true; + // Only look at the first 20 instructions to avoid compile time problems for + // corner cases of very large blocks without memory writes. + // 20 instructions is more than enough. + if (instAndIdx.index() > 50) + return true; + } + return false; +} + +// Returns true if \p block contains an injected an enum case into \p enumAddr +// which is valid at the end of the block. +static bool hasInjectedEnumAtEndOfBlock(SILBasicBlock *block, SILValue enumAddr) { + for (auto instAndIdx : llvm::enumerate(llvm::reverse(*block))) { + SILInstruction &inst = instAndIdx.value(); + if (auto *injectInst = dyn_cast(&inst)) { + return injectInst->getOperand() == enumAddr; + } + if (inst.mayWriteToMemory()) + return false; + // Only look at the first 20 instructions to avoid compile time problems for + // corner cases of very large blocks without memory writes. + // 20 instructions is more than enough. + if (instAndIdx.index() > 50) + return false; + } + return false; +} + /// tryJumpThreading - Check to see if it looks profitable to duplicate the /// destination of an unconditional jump into the bottom of this block. bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { auto *DestBB = BI->getDestBB(); auto *SrcBB = BI->getParent(); + TermInst *destTerminator = DestBB->getTerminator(); // If the destination block ends with a return, we don't want to duplicate it. // We want to maintain the canonical form of a single return where possible. - if (DestBB->getTerminator()->isFunctionExiting()) + if (destTerminator->isFunctionExiting()) return false; + // Jump threading only makes sense if there is an argument on the branch + // (which is reacted on in the DestBB), or if this goes through a memory + // location (switch_enum_addr is the only adress-instruction which we + // currently handle). + if (BI->getArgs().empty() && !isa(destTerminator)) + return false; + // We don't have a great cost model at the SIL level, so we don't want to // blissly duplicate tons of code with a goal of improved performance (we'll // leave that to LLVM). However, doing limited code duplication can lead to @@ -956,11 +997,29 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { } } - if (ThreadingBudget == 0 && isa(DestBB->getTerminator())) { - for (auto V : BI->getArgs()) { - if (isa(V) || isa(V)) { + if (ThreadingBudget == 0) { + if (isa(destTerminator)) { + for (auto V : BI->getArgs()) { + if (isa(V) || isa(V)) { + ThreadingBudget = 4; + break; + } + } + } else if (auto *SEA = dyn_cast(destTerminator)) { + // If the branch-block injects a certain enum case and the destination + // switches on that enum, it's worth jump threading. E.g. + // + // inject_enum_addr %enum : $*Optional, #Optional.some + // ... // no memory writes here + // br DestBB + // DestBB: + // ... // no memory writes here + // switch_enum_addr %enum : $*Optional, case #Optional.some ... + // + SILValue enumAddr = SEA->getOperand(); + if (!blockMayWriteMemory(DestBB) && + hasInjectedEnumAtEndOfBlock(SrcBB, enumAddr)) { ThreadingBudget = 4; - break; } } } @@ -976,7 +1035,7 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { // control flow. Still, we make an exception for switch_enum. bool DestIsLoopHeader = (LoopHeaders.count(DestBB) != 0); if (DestIsLoopHeader) { - if (!isa(DestBB->getTerminator())) + if (!isa(destTerminator)) return false; } @@ -1286,8 +1345,7 @@ bool SimplifyCFG::simplifyBranchBlock(BranchInst *BI) { // If this unconditional branch has BBArgs, check to see if duplicating the // destination would allow it to be simplified. This is a simple form of jump // threading. - if (!isVeryLargeFunction && !BI->getArgs().empty() && - tryJumpThreading(BI)) + if (!isVeryLargeFunction && tryJumpThreading(BI)) return true; return Simplified; diff --git a/test/SILOptimizer/enum_jump_thread.sil b/test/SILOptimizer/enum_jump_thread.sil index 22d76c7a9142f..3ce09bbe2112a 100644 --- a/test/SILOptimizer/enum_jump_thread.sil +++ b/test/SILOptimizer/enum_jump_thread.sil @@ -49,3 +49,83 @@ bb5(%7 : $E2): return %7 : $E2 } +// CHECK-LABEL: sil @test_enum_addr +sil @test_enum_addr : $@convention(thin) () -> Builtin.Int32 { +bb0: + %2 = alloc_stack $E + cond_br undef, bb1, bb2 + +// CHECK: bb1: +// CHECK-NEXT: inject_enum_addr +// CHECK-NEXT: switch_enum_addr +bb1: + inject_enum_addr %2 : $*E, #E.A!enumelt + br bb3 + +// CHECK: bb2: +// CHECK-NEXT: inject_enum_addr +// CHECK-NEXT: switch_enum_addr +bb2: + inject_enum_addr %2 : $*E, #E.B!enumelt + br bb3 + +bb3: + switch_enum_addr %2 : $*E, case #E.A!enumelt: bb4, case #E.B!enumelt: bb5 + +bb4: + %10 = integer_literal $Builtin.Int32, 1 + br bb6(%10 : $Builtin.Int32) + +bb5: + %11 = integer_literal $Builtin.Int32, 2 + br bb6(%11 : $Builtin.Int32) + +bb6(%12 : $Builtin.Int32): + dealloc_stack %2 : $*E + return %12 : $Builtin.Int32 +// CHECK: } // end sil function 'test_enum_addr' +} + +// CHECK-LABEL: sil @dont_jumpthread_enum_addr +sil @dont_jumpthread_enum_addr : $@convention(thin) (E) -> Builtin.Int32 { +bb0(%0 : $E): + %2 = alloc_stack $E + %3 = alloc_stack $E + cond_br undef, bb1, bb2 + +// CHECK: bb1: +// CHECK-NEXT: inject_enum_addr +// CHECK-NEXT: store +// CHECK-NEXT: br bb3 +bb1: + inject_enum_addr %2 : $*E, #E.A!enumelt + store %0 to %2 : $*E + br bb3 + +// CHECK: bb2: +// CHECK-NEXT: inject_enum_addr +// CHECK-NEXT: br bb3 +bb2: + inject_enum_addr %3 : $*E, #E.A!enumelt + br bb3 + +// CHECK: bb3: +// CHECK-NEXT: switch_enum_addr +bb3: + switch_enum_addr %2 : $*E, case #E.A!enumelt: bb4, case #E.B!enumelt: bb5 + +bb4: + %10 = integer_literal $Builtin.Int32, 1 + br bb6(%10 : $Builtin.Int32) + +bb5: + %11 = integer_literal $Builtin.Int32, 2 + br bb6(%11 : $Builtin.Int32) + +bb6(%12 : $Builtin.Int32): + dealloc_stack %3 : $*E + dealloc_stack %2 : $*E + return %12 : $Builtin.Int32 +// CHECK: } // end sil function 'dont_jumpthread_enum_addr' +} + diff --git a/test/SILOptimizer/generic_loop.swift b/test/SILOptimizer/generic_loop.swift new file mode 100644 index 0000000000000..55facdb5ab61f --- /dev/null +++ b/test/SILOptimizer/generic_loop.swift @@ -0,0 +1,17 @@ +// RUN: %target-swift-frontend -primary-file %s -O -sil-verify-all -module-name=test -emit-sil | %FileCheck %s + +// REQUIRES: swift_stdlib_no_asserts,optimized_stdlib + + +// Check that we can eliminate all optionals from a loop which is iterating +// over an array of address-only elements. + +// CHECK-LABEL: sil @$s4test0A18_no_optionals_usedyySayxGlF : $@convention(thin) (@guaranteed Array) -> () { +// CHECK-NOT: Optional +// CHECK: } // end sil function '$s4test0A18_no_optionals_usedyySayxGlF' +public func test_no_optionals_used(_ items: [T]) { + for i in items { + print(i) + } +} + diff --git a/test/SILOptimizer/sil_combine_concrete_existential.swift b/test/SILOptimizer/sil_combine_concrete_existential.swift index 3fe0399faa9d7..5a840d7ffbc67 100644 --- a/test/SILOptimizer/sil_combine_concrete_existential.swift +++ b/test/SILOptimizer/sil_combine_concrete_existential.swift @@ -95,10 +95,11 @@ struct SS: PPP { // The first apply has been devirtualized and inlined. The second remains unspecialized. // CHECK-LABEL: sil @$s32sil_combine_concrete_existential37testWitnessReturnOptionalIndirectSelfyyF : $@convention(thin) () -> () { +// CHECK: [[O1:%.*]] = open_existential_addr immutable_access %{{.*}} : $*PPP to $*@opened("{{.*}}") PPP // CHECK: switch_enum_addr %{{.*}} : $*Optional<@opened("{{.*}}") PPP>, case #Optional.some!enumelt: bb{{.*}}, case #Optional.none!enumelt: bb{{.*}} -// CHECK: [[O:%.*]] = open_existential_addr immutable_access %{{.*}} : $*PPP to $*@opened("{{.*}}") PPP -// CHECK: [[W:%.*]] = witness_method $@opened("{{.*}}") PPP, #PPP.returnsOptionalIndirect : (Self) -> () -> Self?, [[O]] : $*@opened("{{.*}}") PPP : $@convention(witness_method: PPP) <τ_0_0 where τ_0_0 : PPP> (@in_guaranteed τ_0_0) -> @out Optional<τ_0_0> -// CHECK: apply [[W]]<@opened("{{.*}}") PPP>(%{{.*}}, [[O]]) : $@convention(witness_method: PPP) <τ_0_0 where τ_0_0 : PPP> (@in_guaranteed τ_0_0) -> @out Optional<τ_0_0> +// CHECK: [[O2:%.*]] = open_existential_addr immutable_access %{{.*}} : $*PPP to $*@opened("{{.*}}") PPP +// CHECK: [[W:%.*]] = witness_method $@opened("{{.*}}") PPP, #PPP.returnsOptionalIndirect : (Self) -> () -> Self?, [[O1]] : $*@opened("{{.*}}") PPP : $@convention(witness_method: PPP) <τ_0_0 where τ_0_0 : PPP> (@in_guaranteed τ_0_0) -> @out Optional<τ_0_0> +// CHECK: apply [[W]]<@opened("{{.*}}") PPP>(%{{.*}}, [[O2]]) : $@convention(witness_method: PPP) <τ_0_0 where τ_0_0 : PPP> (@in_guaranteed τ_0_0) -> @out Optional<τ_0_0> // CHECK-LABEL: } // end sil function '$s32sil_combine_concrete_existential37testWitnessReturnOptionalIndirectSelfyyF' public func testWitnessReturnOptionalIndirectSelf() { let p: PPP = S() From 592a5e1ce3e202c0d3d17aab8e5e2ce4c4e7c628 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 30 Sep 2020 08:07:28 -0700 Subject: [PATCH 138/745] [Concurrency] Actor is not an attribute --- test/IDE/complete_decl_attribute.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/IDE/complete_decl_attribute.swift b/test/IDE/complete_decl_attribute.swift index 752ba9e08b5f1..23606dacfa21e 100644 --- a/test/IDE/complete_decl_attribute.swift +++ b/test/IDE/complete_decl_attribute.swift @@ -3,7 +3,6 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD2 | %FileCheck %s -check-prefix=KEYWORD2 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD3 | %FileCheck %s -check-prefix=KEYWORD3 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD3_2 | %FileCheck %s -check-prefix=KEYWORD3 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD3 -enable-experimental-concurrency | %FileCheck %s -check-prefix=KEYWORD3_ASYNC // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD4 | %FileCheck %s -check-prefix=KEYWORD4 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD5 | %FileCheck %s -check-prefix=KEYWORD5 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ON_GLOBALVAR | %FileCheck %s -check-prefix=ON_GLOBALVAR @@ -92,8 +91,6 @@ struct MyStruct {} // KEYWORD3-NEXT: Keyword/None: propertyWrapper[#Class Attribute#]; name=propertyWrapper // KEYWORD3-NEXT: End completions -// KEYWORD3_ASYNC: Keyword/None: actor[#Class Attribute#]; name=actor - @#^KEYWORD3_2^#IB class C2 {} // Same as KEYWORD3. From 9b9e46b63a0bb47c926b5e7ff86366d0f4c3e993 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 30 Sep 2020 11:41:49 -0500 Subject: [PATCH 139/745] [templvalueopt] Fix up tests to be more robust. --- test/SILOptimizer/templvalueopt.sil | 20 ++++++++++---------- test/SILOptimizer/templvalueopt_ossa.sil | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/test/SILOptimizer/templvalueopt.sil b/test/SILOptimizer/templvalueopt.sil index 81f57e2d4da75..42b6d63ed236a 100644 --- a/test/SILOptimizer/templvalueopt.sil +++ b/test/SILOptimizer/templvalueopt.sil @@ -3,7 +3,7 @@ import Swift import Builtin -// CHECK-LABEL: sil @test_enum_with_initialize +// CHECK-LABEL: sil @test_enum_with_initialize : // CHECK: bb0(%0 : $*Optional, %1 : $*Any): // CHECK-NEXT: [[E:%[0-9]+]] = init_enum_data_addr %0 // CHECK-NEXT: copy_addr [take] %1 to [initialization] [[E]] @@ -22,7 +22,7 @@ bb0(%0 : $*Optional, %1 : $*Any): return %6 : $() } -// CHECK-LABEL: sil @test_enum_without_initialize +// CHECK-LABEL: sil @test_enum_without_initialize : // CHECK: bb0(%0 : $*Optional, %1 : $*Any): // CHECK-NEXT: destroy_addr %0 // CHECK-NEXT: [[E:%[0-9]+]] = init_enum_data_addr %0 @@ -48,7 +48,7 @@ struct ConformingStruct : Proto { @_hasStorage let a: Any } -// CHECK-LABEL: sil @test_existential +// CHECK-LABEL: sil @test_existential : // CHECK: bb0(%0 : $*Proto, %1 : $*ConformingStruct): // CHECK-NEXT: [[E:%[0-9]+]] = init_existential_addr %0 // CHECK-NEXT: copy_addr [take] %1 to [initialization] [[E]] @@ -80,7 +80,7 @@ struct AddressOnlyPayload { // There should only be a single copy_addr after optimization. // -// CHECK-LABEL: sil @test_initialize_struct +// CHECK-LABEL: sil @test_initialize_struct : // CHECK: bb0(%0 : $*TestStruct, %1 : $*Any, %2 : $Int): // CHECK-NEXT: [[E:%[0-9]+]] = struct_element_addr %0 // CHECK-NEXT: [[LEFT:%[0-9]+]] = init_enum_data_addr [[E]] @@ -114,7 +114,7 @@ bb0(%0 : $*TestStruct, %1 : $*Any, %2 : $Int): return %20 : $() } -// CHECK-LABEL: sil @bail_on_write_to_dest +// CHECK-LABEL: sil @bail_on_write_to_dest : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: copy_addr @@ -132,7 +132,7 @@ bb0(%0 : $*Optional, %1 : $*Any): return %6 : $() } -// CHECK-LABEL: sil @write_to_dest_ok_if_before_liferange +// CHECK-LABEL: sil @write_to_dest_ok_if_before_liferange : // CHECK: bb0(%0 : $*Optional, %1 : $*Any): // CHECK-NEXT: destroy_addr // CHECK-NEXT: init_enum_data_addr @@ -161,7 +161,7 @@ struct StructWithEnum : Proto { @_hasStorage let e: Enum } -// CHECK-LABEL: sil @move_projections +// CHECK-LABEL: sil @move_projections : // CHECK: bb0(%0 : $*Proto, %1 : $*Any): // CHECK-NEXT: [[S:%[0-9]+]] = init_existential_addr %0 : $*Proto, $StructWithEnum // CHECK-NEXT: [[E:%[0-9]+]] = struct_element_addr [[S]] : $*StructWithEnum, #StructWithEnum.e @@ -188,7 +188,7 @@ bb0(%0 : $*Proto, %1 : $*Any): return %10 : $() } -// CHECK-LABEL: sil @cant_move_projections +// CHECK-LABEL: sil @cant_move_projections : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: load @@ -210,7 +210,7 @@ bb0(%0 : $*Any, %1 : $*Builtin.RawPointer): sil @init_optional : $@convention(thin) () -> @out Optional -// CHECK-LABEL: sil @instructions_after_copy_addr +// CHECK-LABEL: sil @instructions_after_copy_addr : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: copy_addr @@ -231,7 +231,7 @@ bb0(%0 : $*Optional, %1 : $*Any): return %6 : $() } -// CHECK-LABEL: sil @dont_optimize_swap +// CHECK-LABEL: sil @dont_optimize_swap : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: copy_addr diff --git a/test/SILOptimizer/templvalueopt_ossa.sil b/test/SILOptimizer/templvalueopt_ossa.sil index 9e0a5562c5103..e37a777091b80 100644 --- a/test/SILOptimizer/templvalueopt_ossa.sil +++ b/test/SILOptimizer/templvalueopt_ossa.sil @@ -3,7 +3,7 @@ import Swift import Builtin -// CHECK-LABEL: sil [ossa] @test_enum_with_initialize +// CHECK-LABEL: sil [ossa] @test_enum_with_initialize : // CHECK: bb0(%0 : $*Optional, %1 : $*Any): // CHECK-NEXT: [[E:%[0-9]+]] = init_enum_data_addr %0 // CHECK-NEXT: copy_addr [take] %1 to [initialization] [[E]] @@ -22,7 +22,7 @@ bb0(%0 : $*Optional, %1 : $*Any): return %6 : $() } -// CHECK-LABEL: sil [ossa] @test_enum_without_initialize +// CHECK-LABEL: sil [ossa] @test_enum_without_initialize : // CHECK: bb0(%0 : $*Optional, %1 : $*Any): // CHECK-NEXT: destroy_addr %0 // CHECK-NEXT: [[E:%[0-9]+]] = init_enum_data_addr %0 @@ -48,7 +48,7 @@ struct ConformingStruct : Proto { @_hasStorage let a: Any } -// CHECK-LABEL: sil [ossa] @test_existential +// CHECK-LABEL: sil [ossa] @test_existential : // CHECK: bb0(%0 : $*Proto, %1 : $*ConformingStruct): // CHECK-NEXT: [[E:%[0-9]+]] = init_existential_addr %0 // CHECK-NEXT: copy_addr [take] %1 to [initialization] [[E]] @@ -80,7 +80,7 @@ struct AddressOnlyPayload { // There should only be a single copy_addr after optimization. // -// CHECK-LABEL: sil [ossa] @test_initialize_struct +// CHECK-LABEL: sil [ossa] @test_initialize_struct : // CHECK: bb0(%0 : $*TestStruct, %1 : $*Any, %2 : $Int): // CHECK-NEXT: [[E:%[0-9]+]] = struct_element_addr %0 // CHECK-NEXT: [[LEFT:%[0-9]+]] = init_enum_data_addr [[E]] @@ -114,7 +114,7 @@ bb0(%0 : $*TestStruct, %1 : $*Any, %2 : $Int): return %20 : $() } -// CHECK-LABEL: sil [ossa] @bail_on_write_to_dest +// CHECK-LABEL: sil [ossa] @bail_on_write_to_dest : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: copy_addr @@ -132,7 +132,7 @@ bb0(%0 : $*Optional, %1 : $*Any): return %6 : $() } -// CHECK-LABEL: sil [ossa] @write_to_dest_ok_if_before_liferange +// CHECK-LABEL: sil [ossa] @write_to_dest_ok_if_before_liferange : // CHECK: bb0(%0 : $*Optional, %1 : $*Any): // CHECK-NEXT: destroy_addr // CHECK-NEXT: init_enum_data_addr @@ -161,7 +161,7 @@ struct StructWithEnum : Proto { @_hasStorage let e: Enum } -// CHECK-LABEL: sil [ossa] @move_projections +// CHECK-LABEL: sil [ossa] @move_projections : // CHECK: bb0(%0 : $*Proto, %1 : $*Any): // CHECK-NEXT: [[S:%[0-9]+]] = init_existential_addr %0 : $*Proto, $StructWithEnum // CHECK-NEXT: [[E:%[0-9]+]] = struct_element_addr [[S]] : $*StructWithEnum, #StructWithEnum.e @@ -188,7 +188,7 @@ bb0(%0 : $*Proto, %1 : $*Any): return %10 : $() } -// CHECK-LABEL: sil [ossa] @cant_move_projections +// CHECK-LABEL: sil [ossa] @cant_move_projections : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: load @@ -210,7 +210,7 @@ bb0(%0 : $*Any, %1 : $*Builtin.RawPointer): sil [ossa] @init_optional : $@convention(thin) () -> @out Optional -// CHECK-LABEL: sil [ossa] @instructions_after_copy_addr +// CHECK-LABEL: sil [ossa] @instructions_after_copy_addr : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: copy_addr @@ -231,7 +231,7 @@ bb0(%0 : $*Optional, %1 : $*Any): return %6 : $() } -// CHECK-LABEL: sil [ossa] @dont_optimize_swap +// CHECK-LABEL: sil [ossa] @dont_optimize_swap : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: copy_addr From c3bc8e8ef93f4ad5d7eb39bb0334bc00bef8de74 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 29 Sep 2020 16:37:59 -0500 Subject: [PATCH 140/745] [ownership] Move ownership elimination on the stdlib passed lower aggregate instrs. --- lib/SILOptimizer/PassManager/PassPipeline.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 8f6fcc0a52e8b..4a17701499818 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -286,13 +286,13 @@ void addFunctionPasses(SILPassPipelinePlan &P, // Optimize copies from a temporary (an "l-value") to a destination. P.addTempLValueOpt(); + // Split up opaque operations (copy_addr, retain_value, etc.). + P.addLowerAggregateInstrs(); + // We earlier eliminated ownership if we are not compiling the stdlib. Now // handle the stdlib functions. P.addNonTransparentFunctionOwnershipModelEliminator(); - // Split up opaque operations (copy_addr, retain_value, etc.). - P.addLowerAggregateInstrs(); - // Split up operations on stack-allocated aggregates (struct, tuple). if (OpLevel == OptimizationLevelKind::HighLevel) { P.addEarlySROA(); From 9e373a240540f4bf90ed26981a9e29958445d3c6 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 29 Sep 2020 13:37:57 -0700 Subject: [PATCH 141/745] [Name Lookup] Remove property wrapper name lookup flags and generalize unqualified lookup of auxiliary decl names. --- include/swift/AST/NameLookup.h | 2 -- lib/AST/UnqualifiedLookup.cpp | 28 +++++++++++----------------- lib/Sema/TypeCheckConstraints.cpp | 4 ---- lib/Sema/TypeCheckNameLookup.cpp | 2 -- lib/Sema/TypeChecker.h | 2 -- 5 files changed, 11 insertions(+), 27 deletions(-) diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 0549d0715e9d2..80c3d72aa1051 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -225,8 +225,6 @@ enum class UnqualifiedLookupFlags { /// This lookup should include results from outside the innermost scope with /// results. IncludeOuterResults = 1 << 4, - /// Includes property wrapper name lookup results - IncludePropertyWrapperResults = 1 << 5, }; using UnqualifiedLookupOptions = OptionSet; diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 05dd839ec6e07..a46177d49022c 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -605,25 +605,19 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::consume( auto fullName = factory.Name.getFullName(); if (!value->getName().matchesRef(fullName)) { - if (!factory.options.contains(UnqualifiedLookupFlags::IncludePropertyWrapperResults)) - continue; - - auto *varDecl = dyn_cast(value); - if (!varDecl || !varDecl->hasAttachedPropertyWrapper()) - continue; - - auto wrapperInfo = varDecl->getPropertyWrapperBackingPropertyInfo(); - if (!wrapperInfo) - continue; + bool foundMatch = false; + if (auto *varDecl = dyn_cast(value)) { + // Check if the name matches any auxiliary decls not in the AST + varDecl->visitAuxiliaryDecls([&](VarDecl *auxiliaryVar) { + if (auxiliaryVar->ValueDecl::getName().matchesRef(fullName)) { + value = auxiliaryVar; + foundMatch = true; + } + }); + } - if (wrapperInfo.backingVar->ValueDecl::getName().matchesRef(fullName)) { - value = wrapperInfo.backingVar; - } else if (wrapperInfo.projectionVar && - wrapperInfo.projectionVar->ValueDecl::getName().matchesRef(fullName)) { - value = wrapperInfo.projectionVar; - } else { + if (!foundMatch) continue; - } } // In order to preserve the behavior of the existing context-based lookup, diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 9d691d374277e..5e3728222ca5b 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -538,10 +538,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, // name/module qualifier to access top-level name. lookupOptions |= NameLookupFlags::IncludeOuterResults; - // Include property wrapper results in case we have a reference to a backing - // property wrapper or projected value - lookupOptions |= NameLookupFlags::IncludePropertyWrapperResults; - if (Loc.isInvalid()) DC = DC->getModuleScopeContext(); diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 6a474310aa4b3..62347f886bbec 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -211,8 +211,6 @@ convertToUnqualifiedLookupOptions(NameLookupOptions options) { newOptions |= UnqualifiedLookupFlags::IgnoreAccessControl; if (options.contains(NameLookupFlags::IncludeOuterResults)) newOptions |= UnqualifiedLookupFlags::IncludeOuterResults; - if (options.contains(NameLookupFlags::IncludePropertyWrapperResults)) - newOptions |= UnqualifiedLookupFlags::IncludePropertyWrapperResults; return newOptions; } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index c0ae437b097b5..dbd5f63e86b05 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -192,8 +192,6 @@ enum class NameLookupFlags { /// Whether to include results from outside the innermost scope that has a /// result. IncludeOuterResults = 1 << 1, - /// Whether to consider property wrapper names - IncludePropertyWrapperResults = 1 << 2, }; /// A set of options that control name lookup. From 4bb98baf1364ce63e410c381aed7f9690dc5119f Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 29 Sep 2020 17:54:47 -0700 Subject: [PATCH 142/745] [SILGen] Add a new CaptureEmission kind specifically for emitting captured local variables for the assign_by_wrapper setter. Since assign_by_wrapper will always be re-written to initialization if the captured local variable is uninitialized, it's unnecessary to mark the capture as an escape. This lets us support out-of-line initialization for local property wrappers. --- lib/SILGen/SILGenFunction.cpp | 8 +++++++- lib/SILGen/SILGenFunction.h | 5 +++++ lib/SILGen/SILGenLValue.cpp | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index b9672b021bb89..8811da366b7f4 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -187,6 +187,7 @@ void SILGenFunction::emitCaptures(SILLocation loc, // Partial applications take ownership of the context parameters, so we'll // need to pass ownership rather than merely guaranteeing parameters. bool canGuarantee; + bool captureCanEscape = true; switch (purpose) { case CaptureEmission::PartialApplication: canGuarantee = false; @@ -194,6 +195,10 @@ void SILGenFunction::emitCaptures(SILLocation loc, case CaptureEmission::ImmediateApplication: canGuarantee = true; break; + case CaptureEmission::AssignByWrapper: + canGuarantee = false; + captureCanEscape = false; + break; } auto expansion = getTypeExpansionContext(); @@ -381,7 +386,8 @@ void SILGenFunction::emitCaptures(SILLocation loc, } else { capturedArgs.push_back(emitManagedRetain(loc, Entry.box)); } - escapesToMark.push_back(entryValue); + if (captureCanEscape) + escapesToMark.push_back(entryValue); } else { // Address only 'let' values are passed by box. This isn't great, in // that a variable captured by multiple closures will be boxed for each diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index b5e98c78bda87..1b474d092345e 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -115,6 +115,11 @@ enum class CaptureEmission { /// Captures are being emitted for partial application to form a closure /// value. PartialApplication, + /// Captures are being emitted for partial application of a local property + /// wrapper setter for assign_by_wrapper. Captures are guaranteed to not + /// escape, because assign_by_wrapper will not use the setter if the captured + /// variable is not initialized. + AssignByWrapper, }; /// Different ways in which an l-value can be emitted. diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 65c7aadf56668..6bcef2767ebb2 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1490,7 +1490,7 @@ namespace { auto captureInfo = SGF.SGM.Types.getLoweredLocalCaptures(setter); if (!captureInfo.getCaptures().empty()) { SmallVector captures; - SGF.emitCaptures(loc, setter, CaptureEmission::PartialApplication, captures); + SGF.emitCaptures(loc, setter, CaptureEmission::AssignByWrapper, captures); for (auto capture : captures) capturedArgs.push_back(capture.forward(SGF)); From b972bef2ce1908f40a651f4a45e95e63b4e05454 Mon Sep 17 00:00:00 2001 From: Eric Miotto <1094986+edymtt@users.noreply.github.com> Date: Wed, 30 Sep 2020 12:14:35 -0700 Subject: [PATCH 143/745] Add an implementation of Differentiation without tgmath (#34116) Addresses rdar://68471851 --- stdlib/CMakeLists.txt | 4 ++++ stdlib/public/CMakeLists.txt | 10 +++++++++- .../Differentiation_NoTgMath/CMakeLists.txt | 20 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 stdlib/public/Differentiation_NoTgMath/CMakeLists.txt diff --git a/stdlib/CMakeLists.txt b/stdlib/CMakeLists.txt index 6a39ac804c01a..5f8202b406ab2 100644 --- a/stdlib/CMakeLists.txt +++ b/stdlib/CMakeLists.txt @@ -66,6 +66,10 @@ option(SWIFT_ENABLE_MODULE_INTERFACES "Generate .swiftinterface files alongside .swiftmodule files" "${SWIFT_STDLIB_STABLE_ABI}") +option(SWIFT_COMPILE_DIFFERENTIATION_WITHOUT_TGMATH + "Build Differentation without tgmath (and dependency on platform runtime libraries)" + FALSE) + # # End of user-configurable options. # diff --git a/stdlib/public/CMakeLists.txt b/stdlib/public/CMakeLists.txt index 9141a0db2d466..978322b95a1b0 100644 --- a/stdlib/public/CMakeLists.txt +++ b/stdlib/public/CMakeLists.txt @@ -87,7 +87,15 @@ if(SWIFT_BUILD_STDLIB) add_subdirectory(SwiftOnoneSupport) if(SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING) - add_subdirectory(Differentiation) + if(SWIFT_COMPILE_DIFFERENTIATION_WITHOUT_TGMATH) + # Use a different CMakeLists.txt for this configuration + # while sharing the bulk of the code + # This way we will reduce any side effect on the main configuration + # and increase the readability of the CMake code + add_subdirectory(Differentiation_NoTgMath) + else() + add_subdirectory(Differentiation) + endif() endif() if(SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY) diff --git a/stdlib/public/Differentiation_NoTgMath/CMakeLists.txt b/stdlib/public/Differentiation_NoTgMath/CMakeLists.txt new file mode 100644 index 0000000000000..b27e8a207be96 --- /dev/null +++ b/stdlib/public/Differentiation_NoTgMath/CMakeLists.txt @@ -0,0 +1,20 @@ +set(SOURCES_FOLDER ../Differentiation) + +add_swift_target_library(swift_Differentiation ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB + ${SOURCES_FOLDER}/Differentiable.swift + ${SOURCES_FOLDER}/DifferentialOperators.swift + ${SOURCES_FOLDER}/DifferentiationUtilities.swift + ${SOURCES_FOLDER}/AnyDifferentiable.swift + ${SOURCES_FOLDER}/ArrayDifferentiation.swift + ${SOURCES_FOLDER}/OptionalDifferentiation.swift + + GYB_SOURCES + ${SOURCES_FOLDER}/FloatingPointDifferentiation.swift.gyb + ${SOURCES_FOLDER}/SIMDDifferentiation.swift.gyb + + SWIFT_COMPILE_FLAGS + ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + -parse-stdlib + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + DARWIN_INSTALL_NAME_DIR "@rpath" + INSTALL_IN_COMPONENT stdlib) From e01893cb56f989b20ba4309286450105211f031e Mon Sep 17 00:00:00 2001 From: Cassie Jones Date: Wed, 30 Sep 2020 16:49:34 -0400 Subject: [PATCH 144/745] [build-script] Add a flag for sccache This adds a flag to enable sccache in order to simplify the build-script invocation, particularly for new contributors. --- README.md | 2 ++ docs/DevelopmentTips.md | 4 +++- docs/HowToGuides/GettingStarted.md | 6 ++---- utils/build-script | 20 +++++++++++++++++++ utils/build-toolchain | 8 +------- .../build_swift/driver_arguments.py | 3 +++ utils/build_swift/tests/expected_options.py | 2 ++ .../swift_build_support/cmake.py | 4 ++++ .../swift_build_support/toolchain.py | 1 + .../tests/test_toolchain.py | 3 +++ 10 files changed, 41 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 67163d29fdba8..3bc9cd59efa92 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,8 @@ following (non-exhaustive) set of useful options:: - ``--test``: Test the toolchain after it has been compiled. This is off by default. - ``--distcc``: Use distcc to speed up the build by distributing the c++ part of the swift build. This is off by default. +- ``--sccache``: Use sccache to speed up subsequent builds of the compiler by + caching more c++ build artifacts. This is off by default. More options may be added over time. Please pass ``--help`` to ``build-toolchain`` to see the full set of options. diff --git a/docs/DevelopmentTips.md b/docs/DevelopmentTips.md index 41fa0f817ce47..d0462c89f9793 100644 --- a/docs/DevelopmentTips.md +++ b/docs/DevelopmentTips.md @@ -33,9 +33,11 @@ Compilation times for the compiler and the standard library can be agonizing, es ``` $ brew install sccache $ sccache --start-server -$ ./swift/utils/build-script MY_ARGS --cmake-c-launcher $(which sccache) --cmake-cxx-launcher $(which sccache) +$ ./swift/utils/build-script MY_ARGS --sccache ``` +If you want to always use sccache, you can `export USE_SCCACHE=1` and the build script will pick it up. + Given the size of artifacts generated, you might also want to bump the cache size from the default 10GB to something larger, say by putting `export SCCACHE_CACHE_SIZE="50G"` in your dotfile(s). You can run some compiles to see if it is actually doing something by running `sccache --show-stats`. Depending on the exact compilation task you're running, you might see very different cache hit rates. For example, `sccache` is particularly effective if you're rebuilding LLVM, which doesn't change so frequently from the Swift compiler's perspective. On the other hand, if you're changing the compiler's AST, the cache hit rate is likely to be much lower. diff --git a/docs/HowToGuides/GettingStarted.md b/docs/HowToGuides/GettingStarted.md index b438527e859ae..8ee02ea85add9 100644 --- a/docs/HowToGuides/GettingStarted.md +++ b/docs/HowToGuides/GettingStarted.md @@ -246,15 +246,13 @@ Phew, that's a lot to digest! Now let's proceed to the actual build itself! ```sh utils/build-script --skip-build-benchmarks \ --skip-ios --skip-watchos --skip-tvos --swift-darwin-supported-archs "x86_64" \ - --cmake-c-launcher="$(which sccache)" --cmake-cxx-launcher="$(which sccache)" \ - --release-debuginfo --test + --sccache --release-debuginfo --test ``` - Via Xcode: ```sh utils/build-script --skip-build-benchmarks \ --skip-ios --skip-watchos --skip-tvos --swift-darwin-supported-archs "x86_64" \ - --cmake-c-launcher="$(which sccache)" --cmake-cxx-launcher="$(which sccache)" \ - --release-debuginfo --test \ + --sccache --release-debuginfo --test \ --xcode ``` This will create a directory diff --git a/utils/build-script b/utils/build-script index 9282cd84cf6af..e3bd4deb01e05 100755 --- a/utils/build-script +++ b/utils/build-script @@ -187,6 +187,11 @@ def validate_arguments(toolchain, args): fatal_error( "can't find distcc-pump (please install distcc-pump)") + if args.sccache: + if toolchain.sccache is None: + fatal_error( + "can't find sccache (please install sccache)") + if args.host_target is None or args.stdlib_deployment_targets is None: fatal_error("unknown operating system") @@ -523,6 +528,9 @@ class BuildScriptInvocation(object): "--distcc", "--distcc-pump=%s" % toolchain.distcc_pump ] + if args.sccache: + args.cmake_c_launcher = toolchain.sccache + args.cmake_cxx_launcher = toolchain.sccache # *NOTE* We use normal cmake to pass through tsan/ubsan options. We do # NOT pass them to build-script-impl. @@ -1064,6 +1072,12 @@ def parse_preset_args(): action=argparse.actions.StoreTrueAction, nargs=argparse.Nargs.OPTIONAL, default=os.environ.get('USE_DISTCC') == '1') + parser.add_argument( + "--sccache", + help="use sccache", + action=argparse.actions.StoreTrueAction, + nargs=argparse.Nargs.OPTIONAL, + default=os.environ.get('USE_SCCACHE') == '1') parser.add_argument( "--cmake-c-launcher", help="the absolute path to set CMAKE_C_COMPILER_LAUNCHER", @@ -1157,6 +1171,10 @@ def main_preset(): fatal_error( '--distcc can not be used with' + ' --cmake-c-launcher or --cmake-cxx-launcher') + if args.sccache and (args.cmake_c_launcher or args.cmake_cxx_launcher): + fatal_error( + '--sccache can not be used with' + + ' --cmake-c-launcher or --cmake-cxx-launcher') build_script_args = [sys.argv[0]] if args.dry_run: @@ -1167,6 +1185,8 @@ def main_preset(): build_script_args += preset_args if args.distcc: build_script_args += ["--distcc"] + if args.sccache: + build_script_args += ["--sccache"] if args.build_jobs: build_script_args += ["--jobs", str(args.build_jobs)] if args.swiftsyntax_install_prefix: diff --git a/utils/build-toolchain b/utils/build-toolchain index 99e86f434a66c..5af6f66907c1a 100755 --- a/utils/build-toolchain +++ b/utils/build-toolchain @@ -85,13 +85,7 @@ while [ $# -ne 0 ]; do DISTCC_FLAG="--distcc" ;; --sccache) - SCCACHE=$(which sccache) - if [[ -z "${SCCACHE}" ]]; then - echo "Error! Asked to use sccache, but could not find sccache in PATH?!" - usage - exit 1 - fi - SCCACHE_FLAG="--cmake-c-launcher=${SCCACHE} --cmake-cxx-launcher=${SCCACHE}" + SCCACHE_FLAG="--sccache" ;; --preset-file) shift diff --git a/utils/build_swift/build_swift/driver_arguments.py b/utils/build_swift/build_swift/driver_arguments.py index 54280f89e93a3..bc96b354b89dc 100644 --- a/utils/build_swift/build_swift/driver_arguments.py +++ b/utils/build_swift/build_swift/driver_arguments.py @@ -387,6 +387,9 @@ def create_argument_parser(): option('--distcc', toggle_true, default=os.environ.get('USE_DISTCC') == '1', help='use distcc in pump mode') + option('--sccache', toggle_true, + default=os.environ.get('USE_SCCACHE') == '1', + help='use sccache') option('--enable-asan', toggle_true, help='enable Address Sanitizer') option('--enable-ubsan', toggle_true, diff --git a/utils/build_swift/tests/expected_options.py b/utils/build_swift/tests/expected_options.py index e0381cb842fc2..fd1820ce9cf5f 100644 --- a/utils/build_swift/tests/expected_options.py +++ b/utils/build_swift/tests/expected_options.py @@ -138,6 +138,7 @@ defaults.DARWIN_DEPLOYMENT_VERSION_WATCHOS, 'darwin_xcrun_toolchain': None, 'distcc': False, + 'sccache': False, 'dry_run': False, 'enable_asan': False, 'enable_experimental_differentiable_programming': True, @@ -500,6 +501,7 @@ class BuildScriptImplOption(_BaseOption): EnableOption('--build-swift-static-stdlib'), EnableOption('--build-swift-stdlib-unittest-extra'), EnableOption('--distcc'), + EnableOption('--sccache'), EnableOption('--enable-asan'), EnableOption('--enable-experimental-differentiable-programming'), EnableOption('--enable-experimental-concurrency'), diff --git a/utils/swift_build_support/swift_build_support/cmake.py b/utils/swift_build_support/swift_build_support/cmake.py index 85a97dfa747e2..dd023fa946c21 100644 --- a/utils/swift_build_support/swift_build_support/cmake.py +++ b/utils/swift_build_support/swift_build_support/cmake.py @@ -126,6 +126,10 @@ def common_options(self): define("CMAKE_C_COMPILER_LAUNCHER:PATH", toolchain.distcc) define("CMAKE_CXX_COMPILER_LAUNCHER:PATH", toolchain.distcc) + if args.sccache: + define("CMAKE_C_COMPILER_LAUNCHER:PATH", toolchain.sccache) + define("CMAKE_CXX_COMPILER_LAUNCHER:PATH", toolchain.sccache) + if args.cmake_c_launcher: define("CMAKE_C_COMPILER_LAUNCHER:PATH", args.cmake_c_launcher) if args.cmake_cxx_launcher: diff --git a/utils/swift_build_support/swift_build_support/toolchain.py b/utils/swift_build_support/swift_build_support/toolchain.py index c2874c6a4997a..aa79f69fc3826 100644 --- a/utils/swift_build_support/swift_build_support/toolchain.py +++ b/utils/swift_build_support/swift_build_support/toolchain.py @@ -62,6 +62,7 @@ def _getter(self): _register("llvm_cov", "llvm-cov") _register("lipo", "lipo") _register("libtool", "libtool") +_register("sccache", "sccache") _register("swiftc", "swiftc") diff --git a/utils/swift_build_support/tests/test_toolchain.py b/utils/swift_build_support/tests/test_toolchain.py index 4e34428bc745f..34470d578aaa5 100644 --- a/utils/swift_build_support/tests/test_toolchain.py +++ b/utils/swift_build_support/tests/test_toolchain.py @@ -68,6 +68,9 @@ def test_misc_tools(self): self.assertTrue(tc.distcc_pump is None or os.path.basename(tc.distcc_pump) == 'pump' or os.path.basename(tc.distcc_pump) == 'distcc-pump') + # sccache + self.assertTrue(tc.sccache is None or + os.path.basename(tc.sccache) == 'sccache') def test_find_tool(self): tc = host_toolchain() From 176391f18208898f24b5a24c5423b48d4c097ace Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 30 Sep 2020 13:59:42 -0700 Subject: [PATCH 145/745] [Test] Start to add SILGen tests for local property wrappers. --- test/SILGen/property_wrapper_local.swift | 99 ++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 test/SILGen/property_wrapper_local.swift diff --git a/test/SILGen/property_wrapper_local.swift b/test/SILGen/property_wrapper_local.swift new file mode 100644 index 0000000000000..a20d83a0291c0 --- /dev/null +++ b/test/SILGen/property_wrapper_local.swift @@ -0,0 +1,99 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +@propertyWrapper +struct Wrapper { + var wrappedValue: T + var projectedValue: Wrapper { self } + + init(wrappedValue: T) { + self.wrappedValue = wrappedValue + } +} + +func testLocalWrapper() { + // CHECK-LABEL: sil hidden [ossa] @$s22property_wrapper_local16testLocalWrapperyyF : $@convention(thin) () -> () { + + @Wrapper var value: Int + // CHECK: [[A:%.*]] = alloc_box ${ var Wrapper }, var, name "_value" + // CHECK: [[W:%.*]] = mark_uninitialized [var] [[A]] : ${ var Wrapper } + // CHECK: [[P:%.*]] = project_box [[W]] : ${ var Wrapper } + + value = 10 + // CHECK: [[I:%.*]] = function_ref @$s22property_wrapper_local16testLocalWrapperyyF5valueL_SivpfP : $@convention(thin) (Int) -> Wrapper + // CHECK: [[IPA:%.*]] = partial_apply [callee_guaranteed] [[I]]() : $@convention(thin) (Int) -> Wrapper + // CHECK: [[S:%.*]] = function_ref @$s22property_wrapper_local16testLocalWrapperyyF5valueL_Sivs : $@convention(thin) (Int, @guaranteed { var Wrapper }) -> () + // CHECK-NEXT: [[C:%.*]] = copy_value [[W]] : ${ var Wrapper } + // CHECK-NOT: mark_function_escape + // CHECK-NEXT: [[SPA:%.*]] = partial_apply [callee_guaranteed] [[S]]([[C]]) : $@convention(thin) (Int, @guaranteed { var Wrapper }) -> () + // CHECK-NEXT: assign_by_wrapper {{%.*}} : $Int to [[P]] : $*Wrapper, init [[IPA]] : $@callee_guaranteed (Int) -> Wrapper, set [[SPA]] : $@callee_guaranteed (Int) -> () + + _ = value + // CHECK: mark_function_escape [[P]] : $*Wrapper + // CHECK-LABEL: function_ref @$s22property_wrapper_local16testLocalWrapperyyF5valueL_Sivg : $@convention(thin) (@guaranteed { var Wrapper }) -> Int + + _ = $value + // CHECK: mark_function_escape [[P]] : $*Wrapper + // CHECK-LABEL: function_ref @$s22property_wrapper_local16testLocalWrapperyyF6$valueL_AA0F0VySiGvg : $@convention(thin) (@guaranteed { var Wrapper }) -> Wrapper + + + // Check local property wrapper backing initializer and accessors + + // property wrapper backing initializer of value #1 in testLocalWrapper() + // CHECK-LABEL: sil private [ossa] @$s22property_wrapper_local16testLocalWrapperyyF5valueL_SivpfP : $@convention(thin) (Int) -> Wrapper { + + // getter of $value #1 in testLocalWrapper() + // CHECK-LABEL: sil private [ossa] @$s22property_wrapper_local16testLocalWrapperyyF6$valueL_AA0F0VySiGvg : $@convention(thin) (@guaranteed { var Wrapper }) -> Wrapper { + + // getter of value #1 in testLocalWrapper() + // CHECK-LABEL: sil private [ossa] @$s22property_wrapper_local16testLocalWrapperyyF5valueL_Sivg : $@convention(thin) (@guaranteed { var Wrapper }) -> Int { + + // setter of value #1 in testLocalWrapper() + // CHECK-LABEL: sil private [ossa] @$s22property_wrapper_local16testLocalWrapperyyF5valueL_Sivs : $@convention(thin) (Int, @guaranteed { var Wrapper }) -> () { +} + +@propertyWrapper +enum Lazy { + case uninitialized(() -> Value) + case initialized(Value) + + init(wrappedValue initialValue: @autoclosure @escaping () -> Value) { + self = .uninitialized(initialValue) + } + + var wrappedValue: Value { + mutating get { + switch self { + case .uninitialized(let initializer): + let value = initializer() + self = .initialized(value) + return value + case .initialized(let value): + return value + } + } + set { + self = .initialized(newValue) + } + } +} + +func testLocalLazy() { + // CHECK-LABEL: sil hidden [ossa] @$s22property_wrapper_local13testLocalLazyyyF : $@convention(thin) () -> () { + + @Lazy var value = "hello!" + // CHECK: [[C:%.*]] = function_ref @$s22property_wrapper_local13testLocalLazyyyFSSycfu_SSycfu0_ : $@convention(thin) () -> @owned String + // CHECK: [[C2:%.*]] = thin_to_thick_function [[C]] : $@convention(thin) () -> @owned String to $@callee_guaranteed () -> @owned String + // CHECK: [[I:%.*]] = function_ref @$s22property_wrapper_local13testLocalLazyyyF5valueL_SSvpfP : $@convention(thin) (@owned @callee_guaranteed () -> @owned String) -> @owned Lazy + //CHECK: apply [[I]]([[C2]]) + + _ = value + // CHECK: function_ref @$s22property_wrapper_local13testLocalLazyyyF5valueL_SSvg : $@convention(thin) (@guaranteed { var Lazy }) -> @owned String + + + // property wrapper backing initializer of value #1 in testLocalLazy() + // CHECK-LABEL: sil private [ossa] @$s22property_wrapper_local13testLocalLazyyyF5valueL_SSvpfP : $@convention(thin) (@owned @callee_guaranteed () -> @owned String) -> @owned Lazy { + + // getter of value #1 in testLocalLazy() + // CHECK-LABEL: sil private [ossa] @$s22property_wrapper_local13testLocalLazyyyF5valueL_SSvg : $@convention(thin) (@guaranteed { var Lazy }) -> @owned String { + // CHECK: function_ref @$s22property_wrapper_local4LazyO12wrappedValuexvg : $@convention(method) <τ_0_0> (@inout Lazy<τ_0_0>) -> @out τ_0_0 +} From 4c8d09feb3e527569ea9c5ba026bb7ea59138c14 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 30 Sep 2020 16:08:06 -0500 Subject: [PATCH 146/745] [ownership] Move ownership lowering past SROA. I already updated SROA for this and we already have tests/etc. We have just been waiting on some other passes to be moved afterwards. --- lib/SILOptimizer/PassManager/PassPipeline.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 4a17701499818..2c81d1bbcb646 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -289,10 +289,6 @@ void addFunctionPasses(SILPassPipelinePlan &P, // Split up opaque operations (copy_addr, retain_value, etc.). P.addLowerAggregateInstrs(); - // We earlier eliminated ownership if we are not compiling the stdlib. Now - // handle the stdlib functions. - P.addNonTransparentFunctionOwnershipModelEliminator(); - // Split up operations on stack-allocated aggregates (struct, tuple). if (OpLevel == OptimizationLevelKind::HighLevel) { P.addEarlySROA(); @@ -300,6 +296,10 @@ void addFunctionPasses(SILPassPipelinePlan &P, P.addSROA(); } + // We earlier eliminated ownership if we are not compiling the stdlib. Now + // handle the stdlib functions. + P.addNonTransparentFunctionOwnershipModelEliminator(); + // Promote stack allocations to values. P.addMem2Reg(); From fcb51dc9473b6cd75ef69c748981ddfb63db1b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Wed, 30 Sep 2020 14:58:45 -0700 Subject: [PATCH 147/745] [windows] XFAIL crash-in-user-code test in MSVC 2017 See also #33383 where this problem is better explained. MSVC doesn't seem to trigger the exception code when no frame pointers are generated. The only thing missing would be for interpreted code to have a more informative crash message. Add a new LLVM Lit feature with the value of the VisualStudioVersion environment variable (it seems to not change even for minor versions, so it is an easy way to figure out the 2017/2019 difference, even if updates are applied). Use the new feature in a XFAIL check in the test. --- test/Frontend/crash-in-user-code.swift | 2 ++ test/lit.cfg | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/test/Frontend/crash-in-user-code.swift b/test/Frontend/crash-in-user-code.swift index 4a36a0c3b5a4c..d36f642c76e46 100644 --- a/test/Frontend/crash-in-user-code.swift +++ b/test/Frontend/crash-in-user-code.swift @@ -8,6 +8,8 @@ // UNSUPPORTED: OS=tvos // UNSUPPORTED: OS=watchos +// XFAIL: MSVC_VER=15.0 + // CHECK: Stack dump: // CHECK-NEXT: Program arguments: // CHECK-NEXT: Swift version diff --git a/test/lit.cfg b/test/lit.cfg index 0f5d9392297b2..53b3e05dbff63 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -2018,8 +2018,6 @@ if copy_env is not None: for key in copy_env.split(':'): config.environment[key] = os.environ[key] -lit_config.note("Available features: " + ", ".join(sorted(config.available_features))) - # On macOS reflection information is read through the dyld APIs # On other platorms, this information is exported through extra # entry points in the Swift runtime that are only available in debug builds. @@ -2040,3 +2038,9 @@ if kIsWindows: config.substitutions.append( ('%diff', 'diff --strip-trailing-cr') ) else: config.substitutions.append( ('%diff', 'diff') ) + +visual_studio_version = os.environ.get('VisualStudioVersion') +if kIsWindows and visual_studio_version: + config.available_features.add('MSVC_VER=%s' % visual_studio_version) + +lit_config.note("Available features: " + ", ".join(sorted(config.available_features))) From 6d92486c9a6ddc8b71f7c842d546ae3599be5c30 Mon Sep 17 00:00:00 2001 From: Cassie Jones Date: Wed, 30 Sep 2020 19:37:25 -0400 Subject: [PATCH 148/745] [build-script] Add SWIFT_ prefix to USE_SCCACHE env var --- docs/DevelopmentTips.md | 2 +- utils/build-script | 2 +- utils/build_swift/build_swift/driver_arguments.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/DevelopmentTips.md b/docs/DevelopmentTips.md index d0462c89f9793..16ba068fe2577 100644 --- a/docs/DevelopmentTips.md +++ b/docs/DevelopmentTips.md @@ -36,7 +36,7 @@ $ sccache --start-server $ ./swift/utils/build-script MY_ARGS --sccache ``` -If you want to always use sccache, you can `export USE_SCCACHE=1` and the build script will pick it up. +If you want to always use sccache, you can `export SWIFT_USE_SCCACHE=1` and the build script will pick it up. Given the size of artifacts generated, you might also want to bump the cache size from the default 10GB to something larger, say by putting `export SCCACHE_CACHE_SIZE="50G"` in your dotfile(s). diff --git a/utils/build-script b/utils/build-script index e3bd4deb01e05..90f62b4394f9b 100755 --- a/utils/build-script +++ b/utils/build-script @@ -1077,7 +1077,7 @@ def parse_preset_args(): help="use sccache", action=argparse.actions.StoreTrueAction, nargs=argparse.Nargs.OPTIONAL, - default=os.environ.get('USE_SCCACHE') == '1') + default=os.environ.get('SWIFT_USE_SCCACHE') == '1') parser.add_argument( "--cmake-c-launcher", help="the absolute path to set CMAKE_C_COMPILER_LAUNCHER", diff --git a/utils/build_swift/build_swift/driver_arguments.py b/utils/build_swift/build_swift/driver_arguments.py index bc96b354b89dc..cb1c5c0b0e380 100644 --- a/utils/build_swift/build_swift/driver_arguments.py +++ b/utils/build_swift/build_swift/driver_arguments.py @@ -388,7 +388,7 @@ def create_argument_parser(): default=os.environ.get('USE_DISTCC') == '1', help='use distcc in pump mode') option('--sccache', toggle_true, - default=os.environ.get('USE_SCCACHE') == '1', + default=os.environ.get('SWIFT_USE_SCCACHE') == '1', help='use sccache') option('--enable-asan', toggle_true, help='enable Address Sanitizer') From 4cfadfae00f31fc66581ad270176e4916682bf51 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Mon, 28 Sep 2020 19:24:58 -0700 Subject: [PATCH 149/745] [NFC] Documented IRGenFunction::IndirectReturn. --- lib/IRGen/IRGenSIL.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 43fa8b25592c6..2656badc54db5 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -406,6 +406,9 @@ class IRGenSILFunction : llvm::SmallVector FailBBs; SILFunction *CurSILFn; + // If valid, the address by means of which a return--which is direct in + // SIL--is passed indirectly in IR. Such indirection is necessary when the + // value which would be returned directly cannot fit into registers. Address IndirectReturn; /// The unique block that calls @llvm.coro.end. From 607772aaa21ce58db04236cb817b5958c72b2123 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 25 Sep 2020 15:44:17 -0700 Subject: [PATCH 150/745] [Runtime] Stubbed entry points for task de/alloc. --- include/swift/AST/ASTContext.h | 3 ++ include/swift/Runtime/RuntimeFunctions.def | 16 ++++++++++ lib/AST/Availability.cpp | 4 +++ lib/IRGen/IRGenModule.cpp | 13 ++++++++ lib/IRGen/IRGenModule.h | 5 +++ stdlib/public/runtime/CMakeLists.txt | 1 + stdlib/public/runtime/Concurrency.cpp | 37 ++++++++++++++++++++++ 7 files changed, 79 insertions(+) create mode 100644 stdlib/public/runtime/Concurrency.cpp diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 79a48b324e588..bce09761b9f70 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -694,6 +694,9 @@ class ASTContext final { /// generic metadata. AvailabilityContext getIntermodulePrespecializedGenericMetadataAvailability(); + /// Get the runtime availability of support for concurrency. + AvailabilityContext getConcurrencyAvailability(); + /// Get the runtime availability of features introduced in the Swift 5.2 /// compiler for the target platform. AvailabilityContext getSwift52Availability(); diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index 4dac4fca66765..566ca9040cbd8 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -1461,6 +1461,22 @@ FUNCTION(GetTypeByMangledNameInContextInMetadataState, Int8PtrPtrTy), ATTRS(NoUnwind, ArgMemOnly)) +// void *swift_taskAlloc(SwiftTask *task, size_t size); +FUNCTION(TaskAlloc, + swift_taskAlloc, SwiftCC, + ConcurrencyAvailability, + RETURNS(Int8PtrTy), + ARGS(SwiftTaskPtrTy, SizeTy), + ATTRS(NoUnwind, ArgMemOnly)) + +// void swift_taskDealloc(SwiftTask *task, void *ptr); +FUNCTION(TaskDealloc, + swift_taskDealloc, SwiftCC, + ConcurrencyAvailability, + RETURNS(VoidTy), + ARGS(SwiftTaskPtrTy, Int8PtrTy), + ATTRS(NoUnwind, ArgMemOnly)) + #undef RETURNS #undef ARGS #undef ATTRS diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index 743d3f80c69fb..e042a3dda668f 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -323,6 +323,10 @@ ASTContext::getIntermodulePrespecializedGenericMetadataAvailability() { return getSwift54Availability(); } +AvailabilityContext ASTContext::getConcurrencyAvailability() { + return getSwiftFutureAvailability(); +} + AvailabilityContext ASTContext::getSwift52Availability() { auto target = LangOpts.Target; diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index dd90ae9399526..5df5d617e02ae 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -587,6 +587,11 @@ IRGenModule::IRGenModule(IRGenerator &irgen, DynamicReplacementKeyTy = createStructType(*this, "swift.dyn_repl_key", {RelativeAddressTy, Int32Ty}); + SwiftContextTy = createStructType(*this, "swift.context", {}); + SwiftTaskTy = createStructType(*this, "swift.task", {}); + SwiftContextPtrTy = SwiftContextTy->getPointerTo(DefaultAS); + SwiftTaskPtrTy = SwiftTaskTy->getPointerTo(DefaultAS); + DifferentiabilityWitnessTy = createStructType( *this, "swift.differentiability_witness", {Int8PtrTy, Int8PtrTy}); } @@ -681,6 +686,14 @@ namespace RuntimeConstants { } return RuntimeAvailability::AlwaysAvailable; } + + RuntimeAvailability ConcurrencyAvailability(ASTContext &context) { + auto featureAvailability = context.getConcurrencyAvailability(); + if (!isDeploymentAvailabilityContainedIn(context, featureAvailability)) { + return RuntimeAvailability::ConditionallyAvailable; + } + return RuntimeAvailability::AlwaysAvailable; + } } // namespace RuntimeConstants // We don't use enough attributes to justify generalizing the diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 6c2d988fdff7c..f92f1183a8692 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -723,6 +723,11 @@ class IRGenModule { *DynamicReplacementLinkEntryPtrTy; // %link_entry* llvm::StructType *DynamicReplacementKeyTy; // { i32, i32} + llvm::StructType *SwiftContextTy; + llvm::StructType *SwiftTaskTy; + llvm::PointerType *SwiftContextPtrTy; + llvm::PointerType *SwiftTaskPtrTy; + llvm::StructType *DifferentiabilityWitnessTy; // { i8*, i8* } llvm::GlobalVariable *TheTrivialPropertyDescriptor = nullptr; diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index 4b51b04fec37b..8ea899e0614d5 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -31,6 +31,7 @@ set(swift_runtime_sources BackDeployment.cpp Casting.cpp CompatibilityOverride.cpp + Concurrency.cpp CygwinPort.cpp Demangle.cpp DynamicCast.cpp diff --git a/stdlib/public/runtime/Concurrency.cpp b/stdlib/public/runtime/Concurrency.cpp new file mode 100644 index 0000000000000..aa5d25df7cc70 --- /dev/null +++ b/stdlib/public/runtime/Concurrency.cpp @@ -0,0 +1,37 @@ +//===------------ Concurrency.cpp - Swift Concurrency Support ------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Implementations of the concurrency runtime functions. +// +// void *swift_taskAlloc(SwiftTask *task, size_t size); +// void swift_taskDealloc(SwiftTask *task, void *ptr); +// +//===----------------------------------------------------------------------===// + +#include "../SwiftShims/Visibility.h" +#include "swift/Runtime/Config.h" +#include +#include + +struct SwiftTask; + +SWIFT_RUNTIME_EXPORT +SWIFT_CC(swift) +void *swift_taskAlloc(SwiftTask *task, size_t size) { + return malloc(size); +} + +SWIFT_RUNTIME_EXPORT +SWIFT_CC(swift) +void swift_taskDealloc(SwiftTask *task, void *ptr) { + free(ptr); +} From c02c2bc112c1d2fd3e9d769d0dbbe08253962ee6 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 1 Oct 2020 02:32:18 -0400 Subject: [PATCH 151/745] [NFC] Add an iterator template for walking singly-linked lists. --- include/swift/Basic/STLExtras.h | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/include/swift/Basic/STLExtras.h b/include/swift/Basic/STLExtras.h index 21d2993bbaa90..2d96e0bc802ae 100644 --- a/include/swift/Basic/STLExtras.h +++ b/include/swift/Basic/STLExtras.h @@ -217,6 +217,47 @@ inline Iterator prev_or_begin(Iterator it, Iterator begin) { /// @} +/// An iterator that walks a linked list of objects until it reaches +/// a null pointer. +template +class LinkedListIterator { + T *Pointer; +public: + using iterator_category = std::forward_iterator_tag; + using value_type = T *; + using reference = T *; + using pointer = void; + + /// Returns an iterator range starting from the given pointer and + /// running until it reaches a null pointer. + static llvm::iterator_range rangeBeginning(T *pointer) { + return {pointer, nullptr}; + } + + constexpr LinkedListIterator(T *pointer) : Pointer(pointer) {} + + T *operator*() const { + assert(Pointer && "dereferencing a null iterator"); + return Pointer; + } + + LinkedListIterator &operator++() { + Pointer = getNext(Pointer); + return *this; + } + LinkedListIterator operator++(int) { + auto copy = *this; + Pointer = getNext(Pointer); + return copy; + } + + friend bool operator==(LinkedListIterator lhs, LinkedListIterator rhs) { + return lhs.Pointer == rhs.Pointer; + } + friend bool operator!=(LinkedListIterator lhs, LinkedListIterator rhs) { + return lhs.Pointer != rhs.Pointer; + } +}; /// An iterator that transforms the result of an underlying bidirectional /// iterator with a given operation. From 4fa17bf59774ae6543a33b1a29f01320705f4dfe Mon Sep 17 00:00:00 2001 From: Alexey Komnin Date: Tue, 29 Sep 2020 14:55:42 +0300 Subject: [PATCH 152/745] SR-12022: refactor LiteralExpr to combine common initializer code --- include/swift/AST/Expr.h | 219 ++++++++------------------- include/swift/AST/ExprNodes.def | 18 ++- lib/AST/ASTDumper.cpp | 2 +- lib/AST/Expr.cpp | 2 +- lib/SILGen/SILGenApply.cpp | 257 ++++++++++++++++++-------------- lib/SILGen/SILGenExpr.cpp | 2 +- lib/Sema/CSApply.cpp | 44 ++---- 7 files changed, 233 insertions(+), 311 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index b747589cb5078..310ab53bf9b37 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -613,6 +613,9 @@ class CodeCompletionExpr : public Expr { /// LiteralExpr - Common base class between the literals. class LiteralExpr : public Expr { + // Set by Sema: + ConcreteDeclRef Initializer; + public: LiteralExpr(ExprKind Kind, bool Implicit) : Expr(Kind, Implicit) {} @@ -620,13 +623,55 @@ class LiteralExpr : public Expr { return E->getKind() >= ExprKind::First_LiteralExpr && E->getKind() <= ExprKind::Last_LiteralExpr; } + + /// Retrieve the initializer that will be used to construct the + /// literal from the result of the initializer. + /// + /// Only literals that have no builtin literal conformance will have + /// this initializer, which will be called on the result of the builtin + /// initializer. + ConcreteDeclRef getInitializer() const { return Initializer; } + + /// Set the initializer that will be used to construct the literal. + void setInitializer(ConcreteDeclRef initializer) { + Initializer = initializer; + } +}; + +/// BuiltinLiteralExpr - Common base class between all literals +/// that provides BuiltinInitializer +class BuiltinLiteralExpr : public LiteralExpr { + // Set by Seam: + ConcreteDeclRef BuiltinInitializer; + +public: + BuiltinLiteralExpr(ExprKind Kind, bool Implicit) + : LiteralExpr(Kind, Implicit) {} + + static bool classof(const Expr *E) { + return E->getKind() >= ExprKind::First_BuiltinLiteralExpr && + E->getKind() <= ExprKind::Last_BuiltinLiteralExpr; + } + + /// Retrieve the builtin initializer that will be used to construct the + /// literal. + /// + /// Any type-checked literal will have a builtin initializer, which is + /// called first to form a concrete Swift type. + ConcreteDeclRef getBuiltinInitializer() const { return BuiltinInitializer; } + + /// Set the builtin initializer that will be used to construct the + /// literal. + void setBuiltinInitializer(ConcreteDeclRef builtinInitializer) { + BuiltinInitializer = builtinInitializer; + } }; /// The 'nil' literal. /// class NilLiteralExpr : public LiteralExpr { SourceLoc Loc; - ConcreteDeclRef Initializer; + public: NilLiteralExpr(SourceLoc Loc, bool Implicit = false) : LiteralExpr(ExprKind::NilLiteral, Implicit), Loc(Loc) { @@ -635,15 +680,6 @@ class NilLiteralExpr : public LiteralExpr { SourceRange getSourceRange() const { return Loc; } - - /// Retrieve the initializer that will be used to construct the 'nil' - /// literal from the result of the initializer. - ConcreteDeclRef getInitializer() const { return Initializer; } - - /// Set the initializer that will be used to construct the 'nil' literal. - void setInitializer(ConcreteDeclRef initializer) { - Initializer = initializer; - } static bool classof(const Expr *E) { return E->getKind() == ExprKind::NilLiteral; @@ -651,26 +687,23 @@ class NilLiteralExpr : public LiteralExpr { }; /// Abstract base class for numeric literals, potentially with a sign. -class NumberLiteralExpr : public LiteralExpr { +class NumberLiteralExpr : public BuiltinLiteralExpr { /// The value of the literal as an ASTContext-owned string. Underscores must /// be stripped. StringRef Val; // Use StringRef instead of APInt or APFloat, which leak. - ConcreteDeclRef BuiltinInitializer; - ConcreteDeclRef Initializer; protected: SourceLoc MinusLoc; SourceLoc DigitsLoc; public: - NumberLiteralExpr(ExprKind Kind, - StringRef Val, SourceLoc DigitsLoc, bool Implicit) - : LiteralExpr(Kind, Implicit), Val(Val), DigitsLoc(DigitsLoc) - { - Bits.NumberLiteralExpr.IsNegative = false; - Bits.NumberLiteralExpr.IsExplicitConversion = false; - } - + NumberLiteralExpr(ExprKind Kind, StringRef Val, SourceLoc DigitsLoc, + bool Implicit) + : BuiltinLiteralExpr(Kind, Implicit), Val(Val), DigitsLoc(DigitsLoc) { + Bits.NumberLiteralExpr.IsNegative = false; + Bits.NumberLiteralExpr.IsExplicitConversion = false; + } + bool isNegative() const { return Bits.NumberLiteralExpr.IsNegative; } void setNegative(SourceLoc Loc) { MinusLoc = Loc; @@ -701,32 +734,6 @@ class NumberLiteralExpr : public LiteralExpr { return DigitsLoc; } - /// Retrieve the builtin initializer that will be used to construct the - /// boolean literal. - /// - /// Any type-checked boolean literal will have a builtin initializer, which is - /// called first to form a concrete Swift type. - ConcreteDeclRef getBuiltinInitializer() const { return BuiltinInitializer; } - - /// Set the builtin initializer that will be used to construct the boolean - /// literal. - void setBuiltinInitializer(ConcreteDeclRef builtinInitializer) { - BuiltinInitializer = builtinInitializer; - } - - /// Retrieve the initializer that will be used to construct the boolean - /// literal from the result of the initializer. - /// - /// Only boolean literals that have no builtin literal conformance will have - /// this initializer, which will be called on the result of the builtin - /// initializer. - ConcreteDeclRef getInitializer() const { return Initializer; } - - /// Set the initializer that will be used to construct the boolean literal. - void setInitializer(ConcreteDeclRef initializer) { - Initializer = initializer; - } - static bool classof(const Expr *E) { return E->getKind() >= ExprKind::First_NumberLiteralExpr && E->getKind() <= ExprKind::Last_NumberLiteralExpr; @@ -790,14 +797,12 @@ class FloatLiteralExpr : public NumberLiteralExpr { /// A Boolean literal ('true' or 'false') /// -class BooleanLiteralExpr : public LiteralExpr { +class BooleanLiteralExpr : public BuiltinLiteralExpr { SourceLoc Loc; - ConcreteDeclRef BuiltinInitializer; - ConcreteDeclRef Initializer; public: BooleanLiteralExpr(bool Value, SourceLoc Loc, bool Implicit = false) - : LiteralExpr(ExprKind::BooleanLiteral, Implicit), Loc(Loc) { + : BuiltinLiteralExpr(ExprKind::BooleanLiteral, Implicit), Loc(Loc) { Bits.BooleanLiteralExpr.Value = Value; } @@ -808,43 +813,15 @@ class BooleanLiteralExpr : public LiteralExpr { return Loc; } - /// Retrieve the builtin initializer that will be used to construct the - /// boolean literal. - /// - /// Any type-checked boolean literal will have a builtin initializer, which is - /// called first to form a concrete Swift type. - ConcreteDeclRef getBuiltinInitializer() const { return BuiltinInitializer; } - - /// Set the builtin initializer that will be used to construct the boolean - /// literal. - void setBuiltinInitializer(ConcreteDeclRef builtinInitializer) { - BuiltinInitializer = builtinInitializer; - } - - /// Retrieve the initializer that will be used to construct the boolean - /// literal from the result of the initializer. - /// - /// Only boolean literals that have no builtin literal conformance will have - /// this initializer, which will be called on the result of the builtin - /// initializer. - ConcreteDeclRef getInitializer() const { return Initializer; } - - /// Set the initializer that will be used to construct the boolean literal. - void setInitializer(ConcreteDeclRef initializer) { - Initializer = initializer; - } - static bool classof(const Expr *E) { return E->getKind() == ExprKind::BooleanLiteral; } }; - + /// StringLiteralExpr - String literal, like '"foo"'. -class StringLiteralExpr : public LiteralExpr { +class StringLiteralExpr : public BuiltinLiteralExpr { StringRef Val; SourceRange Range; - ConcreteDeclRef BuiltinInitializer; - ConcreteDeclRef Initializer; public: /// The encoding that should be used for the string literal. @@ -879,32 +856,6 @@ class StringLiteralExpr : public LiteralExpr { return Bits.StringLiteralExpr.IsSingleExtendedGraphemeCluster; } - /// Retrieve the builtin initializer that will be used to construct the string - /// literal. - /// - /// Any type-checked string literal will have a builtin initializer, which is - /// called first to form a concrete Swift type. - ConcreteDeclRef getBuiltinInitializer() const { return BuiltinInitializer; } - - /// Set the builtin initializer that will be used to construct the string - /// literal. - void setBuiltinInitializer(ConcreteDeclRef builtinInitializer) { - BuiltinInitializer = builtinInitializer; - } - - /// Retrieve the initializer that will be used to construct the string - /// literal from the result of the initializer. - /// - /// Only string literals that have no builtin literal conformance will have - /// this initializer, which will be called on the result of the builtin - /// initializer. - ConcreteDeclRef getInitializer() const { return Initializer; } - - /// Set the initializer that will be used to construct the string literal. - void setInitializer(ConcreteDeclRef initializer) { - Initializer = initializer; - } - static bool classof(const Expr *E) { return E->getKind() == ExprKind::StringLiteral; } @@ -973,7 +924,6 @@ class InterpolatedStringLiteralExpr : public LiteralExpr { // Set by Sema: OpaqueValueExpr *interpolationExpr = nullptr; ConcreteDeclRef builderInit; - ConcreteDeclRef resultInit; Expr *interpolationCountExpr = nullptr; Expr *literalCapacityExpr = nullptr; @@ -995,11 +945,6 @@ class InterpolatedStringLiteralExpr : public LiteralExpr { void setBuilderInit(ConcreteDeclRef decl) { builderInit = decl; } ConcreteDeclRef getBuilderInit() const { return builderInit; } - /// Sets the decl that constructs the final result type after the - /// AppendingExpr has been evaluated. - void setResultInit(ConcreteDeclRef decl) { resultInit = decl; } - ConcreteDeclRef getResultInit() const { return resultInit; } - /// Sets the OpaqueValueExpr that is passed into AppendingExpr as the SubExpr /// that the tap operates on. void setInterpolationExpr(OpaqueValueExpr *expr) { interpolationExpr = expr; } @@ -1059,7 +1004,7 @@ class InterpolatedStringLiteralExpr : public LiteralExpr { /// MagicIdentifierLiteralExpr - A magic identifier like #file which expands /// out to a literal at SILGen time. -class MagicIdentifierLiteralExpr : public LiteralExpr { +class MagicIdentifierLiteralExpr : public BuiltinLiteralExpr { public: enum Kind : unsigned { #define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) NAME, @@ -1077,17 +1022,16 @@ class MagicIdentifierLiteralExpr : public LiteralExpr { private: SourceLoc Loc; - ConcreteDeclRef BuiltinInitializer; - ConcreteDeclRef Initializer; public: MagicIdentifierLiteralExpr(Kind kind, SourceLoc loc, bool implicit = false) - : LiteralExpr(ExprKind::MagicIdentifierLiteral, implicit), Loc(loc) { + : BuiltinLiteralExpr(ExprKind::MagicIdentifierLiteral, implicit), + Loc(loc) { Bits.MagicIdentifierLiteralExpr.Kind = static_cast(kind); Bits.MagicIdentifierLiteralExpr.StringEncoding = static_cast(StringLiteralExpr::UTF8); } - + Kind getKind() const { return static_cast(Bits.MagicIdentifierLiteralExpr.Kind); } @@ -1123,35 +1067,6 @@ class MagicIdentifierLiteralExpr : public LiteralExpr { = static_cast(encoding); } - /// Retrieve the builtin initializer that will be used to construct the - /// literal. - /// - /// Any type-checked literal will have a builtin initializer, which is - /// called first to form a concrete Swift type. - ConcreteDeclRef getBuiltinInitializer() const { - return BuiltinInitializer; - } - - /// Set the builtin initializer that will be used to construct the literal. - void setBuiltinInitializer(ConcreteDeclRef builtinInitializer) { - BuiltinInitializer = builtinInitializer; - } - - /// Retrieve the initializer that will be used to construct the literal from - /// the result of the initializer. - /// - /// Only literals that have no builtin literal conformance will have - /// this initializer, which will be called on the result of the builtin - /// initializer. - ConcreteDeclRef getInitializer() const { - return Initializer; - } - - /// Set the initializer that will be used to construct the literal. - void setInitializer(ConcreteDeclRef initializer) { - Initializer = initializer; - } - static bool classof(const Expr *E) { return E->getKind() == ExprKind::MagicIdentifierLiteral; } @@ -1174,7 +1089,6 @@ class ObjectLiteralExpr final private: Expr *Arg; SourceLoc PoundLoc; - ConcreteDeclRef Initializer; ObjectLiteralExpr(SourceLoc PoundLoc, LiteralKind LitKind, Expr *Arg, @@ -1237,15 +1151,6 @@ class ObjectLiteralExpr final StringRef getLiteralKindPlainName() const; - /// Retrieve the initializer that will be used to construct the 'object' - /// literal from the result of the initializer. - ConcreteDeclRef getInitializer() const { return Initializer; } - - /// Set the initializer that will be used to construct the 'object' literal. - void setInitializer(ConcreteDeclRef initializer) { - Initializer = initializer; - } - static bool classof(const Expr *E) { return E->getKind() == ExprKind::ObjectLiteral; } diff --git a/include/swift/AST/ExprNodes.def b/include/swift/AST/ExprNodes.def index 7c98a83c0afd9..bcb187333fca1 100644 --- a/include/swift/AST/ExprNodes.def +++ b/include/swift/AST/ExprNodes.def @@ -68,16 +68,18 @@ EXPR(Error, Expr) ABSTRACT_EXPR(Literal, Expr) LITERAL_EXPR(NilLiteral, LiteralExpr) - ABSTRACT_EXPR(NumberLiteral, LiteralExpr) - LITERAL_EXPR(IntegerLiteral, NumberLiteralExpr) - LITERAL_EXPR(FloatLiteral, NumberLiteralExpr) - EXPR_RANGE(NumberLiteral, IntegerLiteral, FloatLiteral) - LITERAL_EXPR(BooleanLiteral, LiteralExpr) - LITERAL_EXPR(StringLiteral, LiteralExpr) + ABSTRACT_EXPR(BuiltinLiteral, LiteralExpr) + LITERAL_EXPR(BooleanLiteral, BuiltinLiteralExpr) + ABSTRACT_EXPR(NumberLiteral, BuiltinLiteralExpr) + LITERAL_EXPR(IntegerLiteral, NumberLiteralExpr) + LITERAL_EXPR(FloatLiteral, NumberLiteralExpr) + EXPR_RANGE(NumberLiteral, IntegerLiteral, FloatLiteral) + LITERAL_EXPR(StringLiteral, BuiltinLiteralExpr) + LITERAL_EXPR(MagicIdentifierLiteral, BuiltinLiteralExpr) + EXPR_RANGE(BuiltinLiteral, BooleanLiteral, MagicIdentifierLiteral) LITERAL_EXPR(InterpolatedStringLiteral, LiteralExpr) LITERAL_EXPR(ObjectLiteral, LiteralExpr) - LITERAL_EXPR(MagicIdentifierLiteral, LiteralExpr) - EXPR_RANGE(Literal, NilLiteral, MagicIdentifierLiteral) + EXPR_RANGE(Literal, NilLiteral, ObjectLiteral) EXPR(DiscardAssignment, Expr) EXPR(DeclRef, Expr) EXPR(SuperRef, Expr) diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 91b809e881ed5..a7e14391f876b 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -1992,7 +1992,7 @@ class PrintExpr : public ExprVisitor { PrintWithColorRAII(OS, LiteralValueColor) << " builder_init="; E->getBuilderInit().dump(PrintWithColorRAII(OS, LiteralValueColor).getOS()); PrintWithColorRAII(OS, LiteralValueColor) << " result_init="; - E->getResultInit().dump(PrintWithColorRAII(OS, LiteralValueColor).getOS()); + E->getInitializer().dump(PrintWithColorRAII(OS, LiteralValueColor).getOS()); OS << "\n"; printRec(E->getAppendingExpr()); PrintWithColorRAII(OS, ParenthesisColor) << ')'; diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 36707fa48d2d7..f721934182b05 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -871,7 +871,7 @@ llvm::APFloat FloatLiteralExpr::getValue() const { StringLiteralExpr::StringLiteralExpr(StringRef Val, SourceRange Range, bool Implicit) - : LiteralExpr(ExprKind::StringLiteral, Implicit), Val(Val), + : BuiltinLiteralExpr(ExprKind::StringLiteral, Implicit), Val(Val), Range(Range) { Bits.StringLiteralExpr.Encoding = static_cast(UTF8); Bits.StringLiteralExpr.IsSingleUnicodeScalar = diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 65c44598af668..77730b41db068 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -1786,6 +1786,139 @@ static bool hasUnownedInnerPointerResult(CanSILFunctionType fnType) { return false; } +//===----------------------------------------------------------------------===// +// Argument Emission for Builtin Initializer +//===----------------------------------------------------------------------===// +static inline PreparedArguments +buildBuiltinLiteralArgs(SILGenFunction &SGF, SGFContext C, + StringLiteralExpr *stringLiteral) { + return emitStringLiteral(SGF, stringLiteral, stringLiteral->getValue(), C, + stringLiteral->getEncoding()); +} + +static inline PreparedArguments +buildBuiltinLiteralArgs(NilLiteralExpr *nilLiteral) { + PreparedArguments builtinLiteralArgs; + builtinLiteralArgs.emplace({}); + return builtinLiteralArgs; +} + +static inline PreparedArguments +buildBuiltinLiteralArgs(SILGenFunction &SGF, SGFContext C, + BooleanLiteralExpr *booleanLiteral) { + PreparedArguments builtinLiteralArgs; + auto i1Ty = SILType::getBuiltinIntegerType(1, SGF.getASTContext()); + SILValue boolValue = SGF.B.createIntegerLiteral(booleanLiteral, i1Ty, + booleanLiteral->getValue()); + ManagedValue boolManaged = ManagedValue::forUnmanaged(boolValue); + CanType ty = boolManaged.getType().getASTType()->getCanonicalType(); + builtinLiteralArgs.emplace(AnyFunctionType::Param(ty)); + builtinLiteralArgs.add(booleanLiteral, RValue(SGF, {boolManaged}, ty)); + return builtinLiteralArgs; +} + +static inline PreparedArguments +buildBuiltinLiteralArgs(SILGenFunction &SGF, SGFContext C, + IntegerLiteralExpr *integerLiteral) { + PreparedArguments builtinLiteralArgs; + ManagedValue integerManaged = + ManagedValue::forUnmanaged(SGF.B.createIntegerLiteral( + integerLiteral, + SILType::getBuiltinIntegerLiteralType(SGF.getASTContext()), + integerLiteral->getRawValue())); + CanType ty = integerManaged.getType().getASTType(); + builtinLiteralArgs.emplace(AnyFunctionType::Param(ty)); + builtinLiteralArgs.add(integerLiteral, RValue(SGF, {integerManaged}, ty)); + return builtinLiteralArgs; +} + +static inline PreparedArguments +buildBuiltinLiteralArgs(SILGenFunction &SGF, SGFContext C, + FloatLiteralExpr *floatLiteral) { + PreparedArguments builtinLiteralArgs; + auto *litTy = floatLiteral->getBuiltinType()->castTo(); + ManagedValue floatManaged = + ManagedValue::forUnmanaged(SGF.B.createFloatLiteral( + floatLiteral, + SILType::getBuiltinFloatType(litTy->getFPKind(), SGF.getASTContext()), + floatLiteral->getValue())); + + CanType ty = floatManaged.getType().getASTType(); + builtinLiteralArgs.emplace(AnyFunctionType::Param(ty)); + builtinLiteralArgs.add(floatLiteral, RValue(SGF, {floatManaged}, ty)); + return builtinLiteralArgs; +} + +static inline PreparedArguments +buildBuiltinLiteralArgs(SILGenFunction &SGF, SGFContext C, + MagicIdentifierLiteralExpr *magicLiteral) { + ASTContext &ctx = SGF.getASTContext(); + SourceLoc loc = magicLiteral->getStartLoc(); + + switch (magicLiteral->getKind()) { + case MagicIdentifierLiteralExpr::FileIDSpelledAsFile: + case MagicIdentifierLiteralExpr::FileID: { + std::string value = loc.isValid() ? SGF.getMagicFileIDString(loc) : ""; + return emitStringLiteral(SGF, magicLiteral, value, C, + magicLiteral->getStringEncoding()); + } + + case MagicIdentifierLiteralExpr::FilePathSpelledAsFile: + case MagicIdentifierLiteralExpr::FilePath: { + StringRef value = loc.isValid() ? SGF.getMagicFilePathString(loc) : ""; + return emitStringLiteral(SGF, magicLiteral, value, C, + magicLiteral->getStringEncoding()); + } + + case MagicIdentifierLiteralExpr::Function: { + StringRef value = loc.isValid() ? SGF.getMagicFunctionString() : ""; + return emitStringLiteral(SGF, magicLiteral, value, C, + magicLiteral->getStringEncoding()); + } + + case MagicIdentifierLiteralExpr::Line: + case MagicIdentifierLiteralExpr::Column: { + SourceLoc Loc = magicLiteral->getStartLoc(); + unsigned Value = 0; + if (Loc.isValid()) { + Value = magicLiteral->getKind() == MagicIdentifierLiteralExpr::Line + ? ctx.SourceMgr.getPresumedLineAndColumnForLoc(Loc).first + : ctx.SourceMgr.getPresumedLineAndColumnForLoc(Loc).second; + } + + auto silTy = SILType::getBuiltinIntegerLiteralType(ctx); + auto ty = silTy.getASTType(); + SILValue integer = SGF.B.createIntegerLiteral(magicLiteral, silTy, Value); + ManagedValue integerManaged = ManagedValue::forUnmanaged(integer); + PreparedArguments builtinLiteralArgs; + builtinLiteralArgs.emplace(AnyFunctionType::Param(ty)); + builtinLiteralArgs.add(magicLiteral, RValue(SGF, {integerManaged}, ty)); + return builtinLiteralArgs; + } + case MagicIdentifierLiteralExpr::DSOHandle: + llvm_unreachable("handled elsewhere"); + } +} + +static inline PreparedArguments buildBuiltinLiteralArgs(SILGenFunction &SGF, + SGFContext C, + LiteralExpr *literal) { + if (auto stringLiteral = dyn_cast(literal)) { + return buildBuiltinLiteralArgs(SGF, C, stringLiteral); + } else if (auto nilLiteral = dyn_cast(literal)) { + return buildBuiltinLiteralArgs(nilLiteral); + } else if (auto booleanLiteral = dyn_cast(literal)) { + return buildBuiltinLiteralArgs(SGF, C, booleanLiteral); + } else if (auto integerLiteral = dyn_cast(literal)) { + return buildBuiltinLiteralArgs(SGF, C, integerLiteral); + } else if (auto floatLiteral = dyn_cast(literal)) { + return buildBuiltinLiteralArgs(SGF, C, floatLiteral); + } else { + return buildBuiltinLiteralArgs( + SGF, C, cast(literal)); + } +} + //===----------------------------------------------------------------------===// // Argument Emission //===----------------------------------------------------------------------===// @@ -4794,124 +4927,30 @@ RValue SILGenFunction::emitApplyOfPropertyWrapperBackingInitializer( RValue SILGenFunction::emitLiteral(LiteralExpr *literal, SGFContext C) { ConcreteDeclRef builtinInit; ConcreteDeclRef init; - // Emit the raw, builtin literal arguments. - PreparedArguments builtinLiteralArgs; - if (auto stringLiteral = dyn_cast(literal)) { - builtinLiteralArgs = emitStringLiteral(*this, literal, - stringLiteral->getValue(), C, - stringLiteral->getEncoding()); - builtinInit = stringLiteral->getBuiltinInitializer(); - init = stringLiteral->getInitializer(); - } else if (auto nilLiteral = dyn_cast(literal)) { - builtinLiteralArgs.emplace({}); - builtinInit = nilLiteral->getInitializer(); - } else if (auto booleanLiteral = dyn_cast(literal)) { - auto i1Ty = SILType::getBuiltinIntegerType(1, getASTContext()); - SILValue boolValue = B.createIntegerLiteral(booleanLiteral, i1Ty, - booleanLiteral->getValue()); - ManagedValue boolManaged = ManagedValue::forUnmanaged(boolValue); - CanType ty = boolManaged.getType().getASTType()->getCanonicalType(); - builtinLiteralArgs.emplace(AnyFunctionType::Param(ty)); - builtinLiteralArgs.add(literal, RValue(*this, {boolManaged}, ty)); - builtinInit = booleanLiteral->getBuiltinInitializer(); - init = booleanLiteral->getInitializer(); - } else if (auto integerLiteral = dyn_cast(literal)) { - ManagedValue integerManaged = - ManagedValue::forUnmanaged(B.createIntegerLiteral( - integerLiteral, - SILType::getBuiltinIntegerLiteralType(getASTContext()), - integerLiteral->getRawValue())); - CanType ty = integerManaged.getType().getASTType(); - builtinLiteralArgs.emplace(AnyFunctionType::Param(ty)); - builtinLiteralArgs.add(literal, RValue(*this, {integerManaged}, ty)); - builtinInit = integerLiteral->getBuiltinInitializer(); - init = integerLiteral->getInitializer(); - } else if (auto floatLiteral = dyn_cast(literal)) { - auto *litTy = floatLiteral->getBuiltinType()->castTo(); - ManagedValue floatManaged = ManagedValue::forUnmanaged(B.createFloatLiteral( - floatLiteral, - SILType::getBuiltinFloatType(litTy->getFPKind(), getASTContext()), - floatLiteral->getValue())); - - CanType ty = floatManaged.getType().getASTType(); - builtinLiteralArgs.emplace(AnyFunctionType::Param(ty)); - builtinLiteralArgs.add(literal, RValue(*this, {floatManaged}, ty)); - builtinInit = floatLiteral->getBuiltinInitializer(); - init = floatLiteral->getInitializer(); + if (auto builtinLiteral = dyn_cast(literal)) { + builtinInit = builtinLiteral->getBuiltinInitializer(); + init = builtinLiteral->getInitializer(); } else { - ASTContext &ctx = getASTContext(); - SourceLoc loc = literal->getStartLoc(); - - auto magicLiteral = cast(literal); - switch (magicLiteral->getKind()) { - case MagicIdentifierLiteralExpr::FileIDSpelledAsFile: - case MagicIdentifierLiteralExpr::FileID: { - std::string value = loc.isValid() ? getMagicFileIDString(loc) : ""; - builtinLiteralArgs = emitStringLiteral(*this, literal, value, C, - magicLiteral->getStringEncoding()); - builtinInit = magicLiteral->getBuiltinInitializer(); - init = magicLiteral->getInitializer(); - break; - } - - case MagicIdentifierLiteralExpr::FilePathSpelledAsFile: - case MagicIdentifierLiteralExpr::FilePath: { - StringRef value = loc.isValid() ? getMagicFilePathString(loc) : ""; - builtinLiteralArgs = emitStringLiteral(*this, literal, value, C, - magicLiteral->getStringEncoding()); - builtinInit = magicLiteral->getBuiltinInitializer(); - init = magicLiteral->getInitializer(); - break; - } - - case MagicIdentifierLiteralExpr::Function: { - StringRef value = loc.isValid() ? getMagicFunctionString() : ""; - builtinLiteralArgs = emitStringLiteral(*this, literal, value, C, - magicLiteral->getStringEncoding()); - builtinInit = magicLiteral->getBuiltinInitializer(); - init = magicLiteral->getInitializer(); - break; - } - - case MagicIdentifierLiteralExpr::Line: - case MagicIdentifierLiteralExpr::Column: { - SourceLoc Loc = literal->getStartLoc(); - unsigned Value = 0; - if (Loc.isValid()) { - Value = magicLiteral->getKind() == MagicIdentifierLiteralExpr::Line - ? ctx.SourceMgr.getPresumedLineAndColumnForLoc(Loc).first - : ctx.SourceMgr.getPresumedLineAndColumnForLoc(Loc).second; - } - - auto silTy = SILType::getBuiltinIntegerLiteralType(ctx); - auto ty = silTy.getASTType(); - SILValue integer = B.createIntegerLiteral(literal, silTy, Value); - ManagedValue integerManaged = ManagedValue::forUnmanaged(integer); - builtinLiteralArgs.emplace(AnyFunctionType::Param(ty)); - builtinLiteralArgs.add(literal, RValue(*this, {integerManaged}, ty)); - builtinInit = magicLiteral->getBuiltinInitializer(); - init = magicLiteral->getInitializer(); - break; - } - case MagicIdentifierLiteralExpr::DSOHandle: - llvm_unreachable("handled elsewhere"); - } + builtinInit = literal->getInitializer(); } + // Emit the raw, builtin literal arguments. + PreparedArguments builtinLiteralArgs = + buildBuiltinLiteralArgs(*this, C, literal); + // Call the builtin initializer. - RValue builtinLiteral = - emitApplyAllocatingInitializer(literal, builtinInit, - std::move(builtinLiteralArgs), - Type(), - init ? SGFContext() : C); + RValue builtinResult = emitApplyAllocatingInitializer( + literal, builtinInit, std::move(builtinLiteralArgs), Type(), + init ? SGFContext() : C); // If we were able to directly initialize the literal we wanted, we're done. - if (!init) return builtinLiteral; + if (!init) + return builtinResult; // Otherwise, perform the second initialization step. - auto ty = builtinLiteral.getType(); + auto ty = builtinResult.getType(); PreparedArguments args((AnyFunctionType::Param(ty))); - args.add(literal, std::move(builtinLiteral)); + args.add(literal, std::move(builtinResult)); RValue result = emitApplyAllocatingInitializer(literal, init, std::move(args), diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 6a3b8bde0cf57..4e4cf08b84e3e 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -2515,7 +2515,7 @@ visitInterpolatedStringLiteralExpr(InterpolatedStringLiteralExpr *E, resultInitArgs.add(E, std::move(interpolation)); return SGF.emitApplyAllocatingInitializer( - E, E->getResultInit(), std::move(resultInitArgs), Type(), C); + E, E->getInitializer(), std::move(resultInitArgs), Type(), C); } RValue RValueEmitter:: diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 8daffa99526cb..d91caff835a75 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -1513,10 +1513,8 @@ namespace { /// protocol is broken. /// /// \returns the converted literal expression. - Expr *convertLiteralInPlace(Expr *literal, - Type type, - ProtocolDecl *protocol, - Identifier literalType, + Expr *convertLiteralInPlace(LiteralExpr *literal, Type type, + ProtocolDecl *protocol, Identifier literalType, DeclName literalFuncName, ProtocolDecl *builtinProtocol, DeclName builtinLiteralFuncName, @@ -2532,7 +2530,7 @@ namespace { KnownProtocolKind::ExpressibleByStringInterpolation, type, {ctx.Id_stringInterpolation}); if (!resultInit) return nullptr; - expr->setResultInit(resultInit); + expr->setInitializer(resultInit); // Make the integer literals for the parameters. auto buildExprFromUnsigned = [&](unsigned value) { @@ -6938,15 +6936,11 @@ ExprRewriter::coerceSelfArgumentToType(Expr *expr, /*isImplicit*/ true)); } -Expr *ExprRewriter::convertLiteralInPlace(Expr *literal, - Type type, - ProtocolDecl *protocol, - Identifier literalType, - DeclName literalFuncName, - ProtocolDecl *builtinProtocol, - DeclName builtinLiteralFuncName, - Diag<> brokenProtocolDiag, - Diag<> brokenBuiltinProtocolDiag) { +Expr *ExprRewriter::convertLiteralInPlace( + LiteralExpr *literal, Type type, ProtocolDecl *protocol, + Identifier literalType, DeclName literalFuncName, + ProtocolDecl *builtinProtocol, DeclName builtinLiteralFuncName, + Diag<> brokenProtocolDiag, Diag<> brokenBuiltinProtocolDiag) { // If coercing a literal to an unresolved type, we don't try to look up the // witness members, just do it. if (type->is()) { @@ -6970,16 +6964,7 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal, // Form a reference to the builtin conversion function. // Set the builtin initializer. - if (auto stringLiteral = dyn_cast(literal)) - stringLiteral->setBuiltinInitializer(witness); - else if (auto booleanLiteral = dyn_cast(literal)) - booleanLiteral->setBuiltinInitializer(witness); - else if (auto numberLiteral = dyn_cast(literal)) - numberLiteral->setBuiltinInitializer(witness); - else { - cast(literal)->setBuiltinInitializer( - witness); - } + dyn_cast(literal)->setBuiltinInitializer(witness); // The literal expression has this type. cs.setType(literal, type); @@ -7016,16 +7001,7 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal, return nullptr; // Set the initializer. - if (auto nilLiteral = dyn_cast(literal)) - nilLiteral->setInitializer(witness); - else if (auto stringLiteral = dyn_cast(literal)) - stringLiteral->setInitializer(witness); - else if (auto booleanLiteral = dyn_cast(literal)) - booleanLiteral->setInitializer(witness); - else if (auto numberLiteral = dyn_cast(literal)) - numberLiteral->setInitializer(witness); - else - cast(literal)->setInitializer(witness); + literal->setInitializer(witness); // The literal expression has this type. cs.setType(literal, type); From 8ccee27db72e0ce6fbf236a8f13ab2e85984a482 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Tue, 29 Sep 2020 11:26:17 -0700 Subject: [PATCH 153/745] ModuleInterface: refactor ModuleInterfaceChecker out of ModuleInterfaceLoader This refactoring allows us to drop ModuleInterfaceLoader when explicit modules are enabled. Before this change, the dependencies scanner needs the loader to be present to access functionalities like collecting prebuilt module candidates. --- include/swift/AST/ASTContext.h | 9 ++- include/swift/AST/ModuleLoader.h | 17 +++++ .../swift/Frontend/ModuleInterfaceLoader.h | 68 +++++++++++-------- .../Serialization/SerializedModuleLoader.h | 12 ---- lib/AST/ASTContext.cpp | 20 ++++-- lib/Frontend/Frontend.cpp | 34 +++++----- lib/Frontend/ModuleInterfaceBuilder.cpp | 7 +- lib/Frontend/ModuleInterfaceLoader.cpp | 31 ++++----- lib/Serialization/ModuleDependencyScanner.cpp | 3 +- unittests/FrontendTool/ModuleLoadingTests.cpp | 7 +- 10 files changed, 117 insertions(+), 91 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 79a48b324e588..ff4cfafea73f0 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -762,6 +762,12 @@ class ASTContext final { bool isClang = false, bool isDWARF = false, bool IsInterface = false); + /// Add a module interface checker to use for this AST context. + void addModuleInterfaceChecker(std::unique_ptr checker); + + /// Retrieve the module interface checker associated with this AST context. + ModuleInterfaceChecker *getModuleInterfaceChecker() const; + /// Retrieve the module dependencies for the module with the given name. /// /// \param isUnderlyingClangModule When true, only look for a Clang module @@ -839,9 +845,6 @@ class ASTContext final { /// If there is no Clang module loader, returns a null pointer. /// The loader is owned by the AST context. ClangModuleLoader *getDWARFModuleLoader() const; - - /// Retrieve the module interface loader for this ASTContext. - ModuleLoader *getModuleInterfaceLoader() const; public: namelookup::ImportCache &getImportCache() const; diff --git a/include/swift/AST/ModuleLoader.h b/include/swift/AST/ModuleLoader.h index 9e5e836bac514..47d7682eca652 100644 --- a/include/swift/AST/ModuleLoader.h +++ b/include/swift/AST/ModuleLoader.h @@ -116,6 +116,23 @@ struct SubCompilerInstanceInfo { ArrayRef ExtraPCMArgs; }; +/// Abstract interface for a checker of module interfaces and prebuilt modules. +class ModuleInterfaceChecker { +public: + virtual std::vector + getCompiledModuleCandidatesForInterface(StringRef moduleName, + StringRef interfacePath) = 0; + + /// Given a list of potential ready-to-use compiled modules for \p interfacePath, + /// check if any one of them is up-to-date. If so, emit a forwarding module + /// to the candidate binary module to \p outPath. + virtual bool tryEmitForwardingModule(StringRef moduleName, + StringRef interfacePath, + ArrayRef candidates, + StringRef outPath) = 0; + virtual ~ModuleInterfaceChecker() = default; +}; + /// Abstract interface to run an action in a sub ASTContext. struct InterfaceSubContextDelegate { virtual std::error_code runInSubContext(StringRef moduleName, diff --git a/include/swift/Frontend/ModuleInterfaceLoader.h b/include/swift/Frontend/ModuleInterfaceLoader.h index 2e0cc1760e7a1..56c63173fb661 100644 --- a/include/swift/Frontend/ModuleInterfaceLoader.h +++ b/include/swift/Frontend/ModuleInterfaceLoader.h @@ -309,6 +309,35 @@ struct ModuleInterfaceLoaderOptions { ModuleInterfaceLoaderOptions() = default; }; +class ModuleInterfaceCheckerImpl: public ModuleInterfaceChecker { + friend class ModuleInterfaceLoader; + ASTContext &Ctx; + std::string CacheDir; + std::string PrebuiltCacheDir; + ModuleInterfaceLoaderOptions Opts; + +public: + explicit ModuleInterfaceCheckerImpl(ASTContext &Ctx, + StringRef cacheDir, + StringRef prebuiltCacheDir, + ModuleInterfaceLoaderOptions Opts) + : Ctx(Ctx), CacheDir(cacheDir), PrebuiltCacheDir(prebuiltCacheDir), + Opts(Opts) {} + + std::vector + getCompiledModuleCandidatesForInterface(StringRef moduleName, + StringRef interfacePath) override; + + /// Given a list of potential ready-to-use compiled modules for \p interfacePath, + /// check if any one of them is up-to-date. If so, emit a forwarding module + /// to the candidate binary module to \p outPath. + bool tryEmitForwardingModule(StringRef moduleName, + StringRef interfacePath, + ArrayRef candidates, + StringRef outPath) override; + bool isCached(StringRef DepPath); +}; + /// A ModuleLoader that runs a subordinate \c CompilerInvocation and /// \c CompilerInstance to convert .swiftinterface files to .swiftmodule /// files on the fly, caching the resulting .swiftmodules in the module cache @@ -316,20 +345,16 @@ struct ModuleInterfaceLoaderOptions { class ModuleInterfaceLoader : public SerializedModuleLoaderBase { friend class unittest::ModuleInterfaceLoaderTest; explicit ModuleInterfaceLoader( - ASTContext &ctx, StringRef cacheDir, StringRef prebuiltCacheDir, + ASTContext &ctx, ModuleInterfaceCheckerImpl &InterfaceChecker, DependencyTracker *tracker, ModuleLoadingMode loadMode, ArrayRef PreferInterfaceForModules, - bool IgnoreSwiftSourceInfoFile, ModuleInterfaceLoaderOptions Opts) - : SerializedModuleLoaderBase(ctx, tracker, loadMode, - IgnoreSwiftSourceInfoFile), - CacheDir(cacheDir), PrebuiltCacheDir(prebuiltCacheDir), - PreferInterfaceForModules(PreferInterfaceForModules), - Opts(Opts) {} + bool IgnoreSwiftSourceInfoFile) + : SerializedModuleLoaderBase(ctx, tracker, loadMode, IgnoreSwiftSourceInfoFile), + InterfaceChecker(InterfaceChecker), + PreferInterfaceForModules(PreferInterfaceForModules){} - std::string CacheDir; - std::string PrebuiltCacheDir; + ModuleInterfaceCheckerImpl &InterfaceChecker; ArrayRef PreferInterfaceForModules; - ModuleInterfaceLoaderOptions Opts; std::error_code findModuleFilesInDirectory( ImportPath::Element ModuleID, @@ -343,17 +368,14 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase { bool isCached(StringRef DepPath) override; public: static std::unique_ptr - create(ASTContext &ctx, StringRef cacheDir, StringRef prebuiltCacheDir, + create(ASTContext &ctx, ModuleInterfaceCheckerImpl &InterfaceChecker, DependencyTracker *tracker, ModuleLoadingMode loadMode, ArrayRef PreferInterfaceForModules = {}, - ModuleInterfaceLoaderOptions Opts = ModuleInterfaceLoaderOptions(), bool IgnoreSwiftSourceInfoFile = false) { return std::unique_ptr( - new ModuleInterfaceLoader(ctx, cacheDir, prebuiltCacheDir, - tracker, loadMode, - PreferInterfaceForModules, - IgnoreSwiftSourceInfoFile, - Opts)); + new ModuleInterfaceLoader(ctx, InterfaceChecker, tracker, loadMode, + PreferInterfaceForModules, + IgnoreSwiftSourceInfoFile)); } /// Append visible module names to \p names. Note that names are possibly @@ -373,18 +395,6 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase { StringRef ModuleName, StringRef InPath, StringRef OutPath, bool SerializeDependencyHashes, bool TrackSystemDependencies, ModuleInterfaceLoaderOptions Opts); - - std::vector - getCompiledModuleCandidatesForInterface(StringRef moduleName, - StringRef interfacePath) override; - - /// Given a list of potential ready-to-use compiled modules for \p interfacePath, - /// check if any one of them is up-to-date. If so, emit a forwarding module - /// to the candidate binary module to \p outPath. - bool tryEmitForwardingModule(StringRef moduleName, - StringRef interfacePath, - ArrayRef candidates, - StringRef outPath) override; }; struct InterfaceSubContextDelegateImpl: InterfaceSubContextDelegate { diff --git a/include/swift/Serialization/SerializedModuleLoader.h b/include/swift/Serialization/SerializedModuleLoader.h index f150c8481d1ef..e0aa714fd3914 100644 --- a/include/swift/Serialization/SerializedModuleLoader.h +++ b/include/swift/Serialization/SerializedModuleLoader.h @@ -201,18 +201,6 @@ class SerializedModuleLoaderBase : public ModuleLoader { virtual Optional getModuleDependencies( StringRef moduleName, ModuleDependenciesCache &cache, InterfaceSubContextDelegate &delegate) override; - - virtual std::vector - getCompiledModuleCandidatesForInterface(StringRef moduleName, - StringRef interfacePath) { - return std::vector(); - } - virtual bool tryEmitForwardingModule(StringRef moduleName, - StringRef interfacePath, - ArrayRef candidates, - StringRef outPath) { - return false; - } }; /// Imports serialized Swift modules into an ASTContext. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 4b8be5ff67243..f47353247d4e2 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -255,6 +255,9 @@ struct ASTContext::Implementation { /// The set of known protocols, lazily populated as needed. ProtocolDecl *KnownProtocols[NumKnownProtocols] = { }; + /// The module interface checker owned by the ASTContext. + std::unique_ptr InterfaceChecker; + /// The various module loaders that import external modules into this /// ASTContext. SmallVector, 4> ModuleLoaders; @@ -268,9 +271,6 @@ struct ASTContext::Implementation { /// The module loader used to load Clang modules from DWARF. ClangModuleLoader *TheDWARFModuleLoader = nullptr; - /// The module loader used to load Swift textual interface. - ModuleLoader *TheModuleInterfaceLoader = nullptr; - /// Map from Swift declarations to raw comments. llvm::DenseMap RawComments; @@ -1519,11 +1519,15 @@ void ASTContext::addModuleLoader(std::unique_ptr loader, if (IsClang && IsDwarf && !getImpl().TheDWARFModuleLoader) getImpl().TheDWARFModuleLoader = static_cast(loader.get()); - if (IsInterface && !getImpl().TheModuleInterfaceLoader) - getImpl().TheModuleInterfaceLoader = loader.get(); getImpl().ModuleLoaders.push_back(std::move(loader)); } +void ASTContext::addModuleInterfaceChecker( + std::unique_ptr checker) { + assert(!getImpl().InterfaceChecker && "Checker has been set already"); + getImpl().InterfaceChecker = std::move(checker); +} + Optional ASTContext::getModuleDependencies( StringRef moduleName, bool isUnderlyingClangModule, ModuleDependenciesCache &cache, InterfaceSubContextDelegate &delegate) { @@ -1610,8 +1614,10 @@ ClangModuleLoader *ASTContext::getDWARFModuleLoader() const { return getImpl().TheDWARFModuleLoader; } -ModuleLoader *ASTContext::getModuleInterfaceLoader() const { - return getImpl().TheModuleInterfaceLoader; +ModuleInterfaceChecker *ASTContext::getModuleInterfaceChecker() const { + auto *result = getImpl().InterfaceChecker.get(); + assert(result); + return result; } ModuleDecl *ASTContext::getLoadedModule( diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index a337d876eedc1..8ca60ba422374 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -493,6 +493,14 @@ bool CompilerInstance::setUpModuleLoaders() { return true; } + // Configure ModuleInterfaceChecker for the ASTContext. + auto const &Clang = clangImporter->getClangInstance(); + std::string ModuleCachePath = getModuleCachePathFromClang(Clang); + auto &FEOpts = Invocation.getFrontendOptions(); + ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); + Context->addModuleInterfaceChecker( + std::make_unique(*Context, ModuleCachePath, + FEOpts.PrebuiltModuleCachePath, LoaderOpts)); // If implicit modules are disabled, we need to install an explicit module // loader. bool ExplicitModuleBuild = Invocation.getFrontendOptions().DisableImplicitModules; @@ -505,23 +513,15 @@ bool CompilerInstance::setUpModuleLoaders() { IgnoreSourceInfoFile); this->DefaultSerializedLoader = ESML.get(); Context->addModuleLoader(std::move(ESML)); - } - - if (MLM != ModuleLoadingMode::OnlySerialized) { - auto const &Clang = clangImporter->getClangInstance(); - std::string ModuleCachePath = getModuleCachePathFromClang(Clang); - auto &FEOpts = Invocation.getFrontendOptions(); - StringRef PrebuiltModuleCachePath = FEOpts.PrebuiltModuleCachePath; - ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); - auto PIML = ModuleInterfaceLoader::create( - *Context, ModuleCachePath, PrebuiltModuleCachePath, - getDependencyTracker(), MLM, FEOpts.PreferInterfaceForModules, - LoaderOpts, - IgnoreSourceInfoFile); - Context->addModuleLoader(std::move(PIML), false, false, true); - } - - if (!ExplicitModuleBuild) { + } else { + if (MLM != ModuleLoadingMode::OnlySerialized) { + // We only need ModuleInterfaceLoader for implicit modules. + auto PIML = ModuleInterfaceLoader::create( + *Context, *static_cast(Context + ->getModuleInterfaceChecker()), getDependencyTracker(), MLM, + FEOpts.PreferInterfaceForModules, IgnoreSourceInfoFile); + Context->addModuleLoader(std::move(PIML), false, false, true); + } std::unique_ptr ISML = ImplicitSerializedModuleLoader::create(*Context, getDependencyTracker(), MLM, IgnoreSourceInfoFile); diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index 63ad78f6ecefd..45f9ea0867c55 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -170,10 +170,9 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( auto &SubInstance = *info.Instance; auto subInvocation = SubInstance.getInvocation(); // Try building forwarding module first. If succeed, return. - if (static_cast(SubInstance.getASTContext() - .getModuleInterfaceLoader())->tryEmitForwardingModule(moduleName, - interfacePath, - CompiledCandidates, OutPath)) { + if (SubInstance.getASTContext().getModuleInterfaceChecker() + ->tryEmitForwardingModule(moduleName, interfacePath, + CompiledCandidates, OutPath)) { return std::error_code(); } FrontendOptions &FEOpts = subInvocation.getFrontendOptions(); diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index ccbf279afcffa..ab054a3423ba8 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -335,6 +335,7 @@ struct ModuleRebuildInfo { /// a module that we'll build from a module interface. class ModuleInterfaceLoaderImpl { friend class swift::ModuleInterfaceLoader; + friend class swift::ModuleInterfaceCheckerImpl; ASTContext &ctx; llvm::vfs::FileSystem &fs; DiagnosticEngine &diags; @@ -907,10 +908,6 @@ class ModuleInterfaceLoaderImpl { return std::move(module.moduleBuffer); } - // If implicit module is disabled, we are done. - if (Opts.disableImplicitSwiftModule) { - return std::make_error_code(std::errc::not_supported); - } std::unique_ptr moduleBuffer; @@ -942,12 +939,16 @@ class ModuleInterfaceLoaderImpl { } // end anonymous namespace -bool ModuleInterfaceLoader::isCached(StringRef DepPath) { +bool ModuleInterfaceCheckerImpl::isCached(StringRef DepPath) { if (!CacheDir.empty() && DepPath.startswith(CacheDir)) return true; return !PrebuiltCacheDir.empty() && DepPath.startswith(PrebuiltCacheDir); } +bool ModuleInterfaceLoader::isCached(StringRef DepPath) { + return InterfaceChecker.isCached(DepPath); +} + /// Load a .swiftmodule associated with a .swiftinterface either from a /// cache or by converting it in a subordinate \c CompilerInstance, caching /// the results. @@ -990,8 +991,8 @@ std::error_code ModuleInterfaceLoader::findModuleFilesInDirectory( auto ModuleName = ModuleID.Item.str(); ModuleInterfaceLoaderImpl Impl( Ctx, ModPath, InPath, ModuleName, - CacheDir, PrebuiltCacheDir, ModuleID.Loc, - Opts, + InterfaceChecker.CacheDir, InterfaceChecker.PrebuiltCacheDir, + ModuleID.Loc, InterfaceChecker.Opts, dependencyTracker, llvm::is_contained(PreferInterfaceForModules, ModuleName) ? @@ -1024,8 +1025,8 @@ std::error_code ModuleInterfaceLoader::findModuleFilesInDirectory( } std::vector -ModuleInterfaceLoader::getCompiledModuleCandidatesForInterface(StringRef moduleName, - StringRef interfacePath) { +ModuleInterfaceCheckerImpl::getCompiledModuleCandidatesForInterface( + StringRef moduleName, StringRef interfacePath) { // Derive .swiftmodule path from the .swiftinterface path. auto newExt = file_types::getExtension(file_types::TY_SwiftModuleFile); llvm::SmallString<32> modulePath = interfacePath; @@ -1034,9 +1035,8 @@ ModuleInterfaceLoader::getCompiledModuleCandidatesForInterface(StringRef moduleN Ctx, modulePath, interfacePath, moduleName, CacheDir, PrebuiltCacheDir, SourceLoc(), Opts, - dependencyTracker, - llvm::is_contained(PreferInterfaceForModules, moduleName) ? - ModuleLoadingMode::PreferInterface : LoadMode); + nullptr, + ModuleLoadingMode::PreferSerialized); std::vector results; auto pair = Impl.getCompiledModuleCandidates(); // Add compiled module candidates only when they are non-empty. @@ -1047,7 +1047,7 @@ ModuleInterfaceLoader::getCompiledModuleCandidatesForInterface(StringRef moduleN return results; } -bool ModuleInterfaceLoader::tryEmitForwardingModule(StringRef moduleName, +bool ModuleInterfaceCheckerImpl::tryEmitForwardingModule(StringRef moduleName, StringRef interfacePath, ArrayRef candidates, StringRef outputPath) { @@ -1059,9 +1059,8 @@ bool ModuleInterfaceLoader::tryEmitForwardingModule(StringRef moduleName, Ctx, modulePath, interfacePath, moduleName, CacheDir, PrebuiltCacheDir, SourceLoc(), Opts, - dependencyTracker, - llvm::is_contained(PreferInterfaceForModules, moduleName) ? - ModuleLoadingMode::PreferInterface : LoadMode); + nullptr, + ModuleLoadingMode::PreferSerialized); SmallVector deps; std::unique_ptr moduleBuffer; for (auto mod: candidates) { diff --git a/lib/Serialization/ModuleDependencyScanner.cpp b/lib/Serialization/ModuleDependencyScanner.cpp index 6e4f82b6178ed..600744840648e 100644 --- a/lib/Serialization/ModuleDependencyScanner.cpp +++ b/lib/Serialization/ModuleDependencyScanner.cpp @@ -98,8 +98,7 @@ std::error_code PlaceholderSwiftModuleScanner::findModuleFilesInDirectory( static std::vector getCompiledCandidates(ASTContext &ctx, StringRef moduleName, StringRef interfacePath) { - return static_cast(ctx - .getModuleInterfaceLoader())->getCompiledModuleCandidatesForInterface( + return ctx.getModuleInterfaceChecker()->getCompiledModuleCandidatesForInterface( moduleName.str(), interfacePath); } diff --git a/unittests/FrontendTool/ModuleLoadingTests.cpp b/unittests/FrontendTool/ModuleLoadingTests.cpp index 0d80267cd90a9..30dd8553d16bb 100644 --- a/unittests/FrontendTool/ModuleLoadingTests.cpp +++ b/unittests/FrontendTool/ModuleLoadingTests.cpp @@ -103,8 +103,13 @@ class ModuleInterfaceLoaderTest : public testing::Test { ASTContext::get(langOpts, typeckOpts, searchPathOpts, clangImpOpts, sourceMgr, diags); + ctx->addModuleInterfaceChecker( + std::make_unique(*ctx, cacheDir, + prebuiltCacheDir, ModuleInterfaceLoaderOptions())); + auto loader = ModuleInterfaceLoader::create( - *ctx, cacheDir, prebuiltCacheDir, + *ctx, *static_cast( + ctx->getModuleInterfaceChecker()), /*dependencyTracker*/nullptr, ModuleLoadingMode::PreferSerialized); From 7ef9b3287ab097ce2bdd1ab70f9ed9df20d39ff1 Mon Sep 17 00:00:00 2001 From: Cassie Jones Date: Thu, 1 Oct 2020 14:17:22 -0400 Subject: [PATCH 154/745] [build-script] Fix CMake sccache test --- utils/swift_build_support/tests/test_cmake.py | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/utils/swift_build_support/tests/test_cmake.py b/utils/swift_build_support/tests/test_cmake.py index 81db80c6ad605..3640751bb598d 100644 --- a/utils/swift_build_support/tests/test_cmake.py +++ b/utils/swift_build_support/tests/test_cmake.py @@ -33,6 +33,15 @@ def mock_distcc_path(self): executable = 'mock-distcc' return os.path.join(os.path.dirname(__file__), executable) + def mock_sccache_path(self): + """Return a path string of a mock sccache executable + """ + if platform.system() == 'Windows': + executable = 'sccache.cmd' + else: + executable = 'sccache' + return os.path.join(os.path.dirname(__file__), executable) + def default_args(self): """Return new args object with default values """ @@ -46,6 +55,7 @@ def default_args(self): enable_sanitize_coverage=False, export_compile_commands=False, distcc=False, + sccache=False, cmake_generator="Ninja", cmake_c_launcher=None, cmake_cxx_launcher=None, @@ -73,6 +83,8 @@ def cmake(self, args): toolchain.libtool = args.host_libtool if args.distcc: toolchain.distcc = self.mock_distcc_path() + if args.sccache: + toolchain.sccache = self.mock_sccache_path() toolchain.ninja = self.which_ninja(args) return CMake(args=args, toolchain=toolchain) @@ -120,7 +132,7 @@ def test_common_options_tsan(self): self.assertEqual( list(cmake.common_options()), ["-G", "Ninja", - "-DLLVM_USE_SANITIZER=Thread", + "-DLLVM_USE_SANITIZER=Thread", "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", @@ -222,6 +234,20 @@ def test_common_options_distcc(self): "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) + def test_common_options_sccache(self): + args = self.default_args() + args.sccache = True + cmake = self.cmake(args) + self.assertEqual( + list(cmake.common_options()), + ["-G", "Ninja", + "-DCMAKE_C_COMPILER_LAUNCHER:PATH=" + self.mock_sccache_path(), + "-DCMAKE_CXX_COMPILER_LAUNCHER:PATH=" + self.mock_sccache_path(), + "-DCMAKE_C_COMPILER:PATH=/path/to/clang", + "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) + def test_common_options_launcher(self): args = self.default_args() cmake_c_launcher = "/path/to/c_launcher" From e254f1c3e2d13a95384d024a6ad5599ba02e6d18 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 30 Sep 2020 18:04:44 -0700 Subject: [PATCH 155/745] [ConstraintSystem] Infer whether locator is related to return of a single-expression function This information could be inferred from state recorded in a constraint system, so to need to record that in the constraint locator as well. --- lib/Sema/CSGen.cpp | 5 ++--- lib/Sema/CSSimplify.cpp | 17 +++++++++++------ lib/Sema/ConstraintLocator.cpp | 11 +---------- lib/Sema/ConstraintLocator.h | 18 ------------------ lib/Sema/ConstraintLocatorPathElts.def | 2 +- 5 files changed, 15 insertions(+), 38 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 75bbeff4bd707..751d893323e44 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3777,9 +3777,8 @@ bool ConstraintSystem::generateConstraints( // Substitute type variables in for unresolved types. if (allowFreeTypeVariables == FreeTypeVariableBinding::UnresolvedType) { - bool isForSingleExprFunction = (ctp == CTP_ReturnSingleExpr); - auto *convertTypeLocator = getConstraintLocator( - expr, LocatorPathElt::ContextualType(isForSingleExprFunction)); + auto *convertTypeLocator = + getConstraintLocator(expr, LocatorPathElt::ContextualType()); convertType = convertType.transform([&](Type type) -> Type { if (type->is()) { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 1b667acf2c8ab..e0a48caa88359 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -5256,9 +5256,15 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, } // Single expression function with implicit `return`. - if (elt->isResultOfSingleExprFunction()) { - increaseScore(SK_FunctionConversion); - return getTypeMatchSuccess(); + if (elt->is()) { + auto anchor = locator.getAnchor(); + auto contextualInfo = getContextualTypeInfo(anchor); + assert(contextualInfo && + "Found contextual type locator without additional information"); + if (contextualInfo->purpose == CTP_ReturnSingleExpr) { + increaseScore(SK_FunctionConversion); + return getTypeMatchSuccess(); + } } } } @@ -10541,9 +10547,8 @@ void ConstraintSystem::addContextualConversionConstraint( } // Add the constraint. - bool isForSingleExprFunction = (purpose == CTP_ReturnSingleExpr); - auto *convertTypeLocator = getConstraintLocator( - expr, LocatorPathElt::ContextualType(isForSingleExprFunction)); + auto *convertTypeLocator = + getConstraintLocator(expr, LocatorPathElt::ContextualType()); addConstraint(constraintKind, getType(expr), conversionType, convertTypeLocator, /*isFavored*/ true); } diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index 708d90d7dc6de..401e48e9cfb3a 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -97,12 +97,6 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { llvm_unreachable("Unhandled PathElementKind in switch."); } -bool LocatorPathElt::isResultOfSingleExprFunction() const { - if (auto elt = getAs()) - return elt->isForSingleExprFunction(); - return false; -} - /// Determine whether given locator points to the subscript reference /// e.g. `foo[0]` or `\Foo.[0]` bool ConstraintLocator::isSubscriptMemberRef() const { @@ -412,10 +406,7 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { break; case ContextualType: - if (elt.isResultOfSingleExprFunction()) - out << "expected result type of the function with a single expression"; - else - out << "contextual type"; + out << "contextual type"; break; case SynthesizedArgument: { diff --git a/lib/Sema/ConstraintLocator.h b/lib/Sema/ConstraintLocator.h index fd5b07a155675..2d0bc6b0e1e7d 100644 --- a/lib/Sema/ConstraintLocator.h +++ b/lib/Sema/ConstraintLocator.h @@ -139,10 +139,6 @@ class ConstraintLocator : public llvm::FoldingSetNode { bool isClosureResult() const { return getKind() == PathElementKind::ClosureResult; } - - /// Determine whether this element points to the contextual type - /// associated with result of a single expression function. - bool isResultOfSingleExprFunction() const; }; /// Return the summary flags for an entire path. @@ -647,20 +643,6 @@ class LocatorPathElt::ClosureBody final : public StoredIntegerElement<1> { } }; -class LocatorPathElt::ContextualType final : public StoredIntegerElement<1> { -public: - ContextualType(bool isForSingleExprFunction = false) - : StoredIntegerElement(ConstraintLocator::ContextualType, isForSingleExprFunction) {} - - /// Whether this element points to the contextual type associated with the - /// result of a single expression function. - bool isForSingleExprFunction() const { return bool(getValue()); } - - static bool classof(const LocatorPathElt *elt) { - return elt->getKind() == ConstraintLocator::ContextualType; - } -}; - class LocatorPathElt::Witness final : public StoredPointerElement { public: Witness(ValueDecl *witness) diff --git a/lib/Sema/ConstraintLocatorPathElts.def b/lib/Sema/ConstraintLocatorPathElts.def index 435d20b74ec9f..2be54c03cc36c 100644 --- a/lib/Sema/ConstraintLocatorPathElts.def +++ b/lib/Sema/ConstraintLocatorPathElts.def @@ -61,7 +61,7 @@ CUSTOM_LOCATOR_PATH_ELT(ClosureBody) SIMPLE_LOCATOR_PATH_ELT(ConstructorMember) /// The desired contextual type passed in to the constraint system. -CUSTOM_LOCATOR_PATH_ELT(ContextualType) +SIMPLE_LOCATOR_PATH_ELT(ContextualType) /// A result of an expression involving dynamic lookup. SIMPLE_LOCATOR_PATH_ELT(DynamicLookupResult) From 1fd6ef95ce483dd7447e09935597cde5c42c14fc Mon Sep 17 00:00:00 2001 From: Josh Learn Date: Tue, 8 Sep 2020 14:28:07 -0700 Subject: [PATCH 156/745] [OSLog] Update compiler stubs and tests The compiler stubs for testing the OSLog implementation are in need of an update. This change updates the stubs to be consistent with the current OSLog implementation, updates the existing tests, and adds new tests for String and metatype interpolations. rdar://69719729 --- stdlib/private/OSLog/CMakeLists.txt | 3 + .../private/OSLog/OSLogFloatFormatting.swift | 426 ++++++++++++++++++ .../OSLog/OSLogFloatingPointTypes.swift | 99 ++-- .../OSLog/OSLogIntegerFormatting.swift | 120 +++-- stdlib/private/OSLog/OSLogIntegerTypes.swift | 128 ++++-- stdlib/private/OSLog/OSLogMessage.swift | 216 +++++---- stdlib/private/OSLog/OSLogNSObjectType.swift | 49 +- stdlib/private/OSLog/OSLogPrivacy.swift | 220 +++++++++ .../private/OSLog/OSLogStringAlignment.swift | 54 ++- stdlib/private/OSLog/OSLogStringTypes.swift | 93 ++-- .../private/OSLog/OSLogSwiftProtocols.swift | 72 +++ stdlib/private/OSLog/OSLogTestHelper.swift | 40 +- .../Inputs/OSLogConstantEvaluable.swift | 5 + test/SILOptimizer/OSLogFullOptTest.swift | 149 +++++- test/SILOptimizer/OSLogMandatoryOptTest.swift | 52 +-- .../Sema/diag_constantness_check_os_log.swift | 4 +- test/stdlib/OSLogExecutionTest.swift | 22 +- 17 files changed, 1362 insertions(+), 390 deletions(-) create mode 100644 stdlib/private/OSLog/OSLogFloatFormatting.swift create mode 100644 stdlib/private/OSLog/OSLogPrivacy.swift create mode 100644 stdlib/private/OSLog/OSLogSwiftProtocols.swift diff --git a/stdlib/private/OSLog/CMakeLists.txt b/stdlib/private/OSLog/CMakeLists.txt index 7241543470fe8..2e37b642fb6c0 100644 --- a/stdlib/private/OSLog/CMakeLists.txt +++ b/stdlib/private/OSLog/CMakeLists.txt @@ -10,6 +10,9 @@ add_swift_target_library(swiftOSLogTestHelper OSLogStringTypes.swift OSLogNSObjectType.swift OSLogFloatingPointTypes.swift + OSLogSwiftProtocols.swift + OSLogPrivacy.swift + OSLogFloatFormatting.swift SWIFT_MODULE_DEPENDS_IOS Darwin ObjectiveC SWIFT_MODULE_DEPENDS_OSX Darwin ObjectiveC diff --git a/stdlib/private/OSLog/OSLogFloatFormatting.swift b/stdlib/private/OSLog/OSLogFloatFormatting.swift new file mode 100644 index 0000000000000..8397f849e5abb --- /dev/null +++ b/stdlib/private/OSLog/OSLogFloatFormatting.swift @@ -0,0 +1,426 @@ +//===--------------------------- OSLogFloatFormatting.swift ------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===------------------------------------------------------------------------------===// + +// This file defines types and functions for specifying formatting of +// floating-point typed interpolations passed to the os log APIs. + +@frozen +public struct OSLogFloatFormatting { + /// When set, a `+` will be printed for all non-negative floats. + @usableFromInline + internal var explicitPositiveSign: Bool + + /// Whether to use uppercase letters to represent numerals greater than 9 + /// (default is to use lowercase). This applies to hexadecimal digits, NaN, Inf, + /// the symbols E and X used to denote exponent and hex format. + @usableFromInline + internal var uppercase: Bool + + // Note: includePrefix is not supported for FloatFormatting. The format specifier %a + // always prints a prefix, %efg don't need one. + + /// Number of digits to display following the radix point. Hex notation does not accept + /// a precision. For non-hex notations, precision can be a dynamic value. The default + /// precision is 6 for non-hex notations. + @usableFromInline + internal var precision: (() -> Int)? + + @usableFromInline + internal enum Notation { + /// Hexadecimal formatting. + case hex + + /// fprintf's `%f` formatting. + /// + /// Prints all digits before the radix point, and `precision` digits following + /// the radix point. If `precision` is zero, the radix point is omitted. + /// + /// Note that very large floating-point values may print quite a lot of digits + /// when using this format, even if `precision` is zero--up to hundreds for + /// `Double`, and thousands for `Float80`. Note also that this format is + /// very likely to print non-zero values as all-zero. If these are a concern, use + /// `.exponential` or `.hybrid` instead. + /// + /// Systems may impose an upper bound on the number of digits that are + /// supported following the radix point. + case fixed + + /// fprintf's `%e` formatting. + /// + /// Prints the number in the form [-]d.ddd...dde±dd, with `precision` significant + /// digits following the radix point. Systems may impose an upper bound on the number + /// of digits that are supported. + case exponential + + /// fprintf's `%g` formatting. + /// + /// Behaves like `.fixed` when the number is scaled close to 1.0, and like + /// `.exponential` if it has a very large or small exponent. + case hybrid + } + + @usableFromInline + internal var notation: Notation + + @_transparent + @usableFromInline + internal init( + explicitPositiveSign: Bool = false, + uppercase: Bool = false, + precision: (() -> Int)?, + notation: Notation + ) { + self.explicitPositiveSign = explicitPositiveSign + self.uppercase = uppercase + self.precision = precision + self.notation = notation + } + + /// Displays an interpolated floating-point value in fprintf's `%f` format with + /// default precision. + /// + /// Prints all digits before the radix point, and 6 digits following the radix point. + /// Note also that this format is very likely to print non-zero values as all-zero. + /// + /// Note that very large floating-point values may print quite a lot of digits + /// when using this format --up to hundreds for `Double`. Note also that this + /// format is very likely to print non-zero values as all-zero. If these are a concern, + /// use `.exponential` or `.hybrid` instead. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static var fixed: OSLogFloatFormatting { .fixed() } + + /// Displays an interpolated floating-point value in fprintf's `%f` format with + /// specified precision, and optional sign and case. + /// + /// Prints all digits before the radix point, and `precision` digits following + /// the radix point. If `precision` is zero, the radix point is omitted. + /// + /// Note that very large floating-point values may print quite a lot of digits + /// when using this format, even if `precision` is zero--up to hundreds for + /// `Double`. Note also that this format is very likely to print non-zero values as + /// all-zero. If these are a concern, use `.exponential` or `.hybrid` instead. + /// + /// Systems may impose an upper bound on the number of digits that are + /// supported following the radix point. + /// + /// All parameters to this function except `precision` must be boolean literals. + /// + /// - Parameters: + /// - precision: Number of digits to display after the radix point. + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. + /// - uppercase: Pass `true` to use uppercase letters or `false` to use + /// lowercase letters. The default is `false`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func fixed( + precision: @escaping @autoclosure () -> Int, + explicitPositiveSign: Bool = false, + uppercase: Bool = false + ) -> OSLogFloatFormatting { + return OSLogFloatFormatting( + explicitPositiveSign: explicitPositiveSign, + uppercase: uppercase, + precision: precision, + notation: .fixed + ) + } + + /// Displays an interpolated floating-point value in fprintf's `%f` format with + /// default precision, and optional sign and case. + /// + /// Prints all digits before the radix point, and 6 digits following the radix point. + /// Note also that this format is very likely to print non-zero values as all-zero. + /// + /// Note that very large floating-point values may print quite a lot of digits + /// when using this format, even if `precision` is zero--up to hundreds for + /// `Double`. Note also that this format is very likely to print non-zero values as + /// all-zero. If these are a concern, use `.exponential` or `.hybrid` instead. + /// + /// Systems may impose an upper bound on the number of digits that are + /// supported following the radix point. + /// + /// All parameters to this function must be boolean literals. + /// - Parameters: + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. + /// - uppercase: Pass `true` to use uppercase letters or `false` to use + /// lowercase letters. The default is `false`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func fixed( + explicitPositiveSign: Bool = false, + uppercase: Bool = false + ) -> OSLogFloatFormatting { + return OSLogFloatFormatting( + explicitPositiveSign: explicitPositiveSign, + uppercase: uppercase, + precision: nil, + notation: .fixed + ) + } + + /// Displays an interpolated floating-point value in hexadecimal format. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static var hex: OSLogFloatFormatting { .hex() } + + /// Displays an interpolated floating-point value in hexadecimal format with + /// optional sign and case. + /// + /// All parameters to this function must be boolean literals. + /// + /// - Parameters: + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. + /// - uppercase: Pass `true` to use uppercase letters or `false` to use + /// lowercase letters. The default is `false`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func hex( + explicitPositiveSign: Bool = false, + uppercase: Bool = false + ) -> OSLogFloatFormatting { + return OSLogFloatFormatting( + explicitPositiveSign: explicitPositiveSign, + uppercase: uppercase, + precision: nil, + notation: .hex + ) + } + + /// Displays an interpolated floating-point value in fprintf's `%e` format. + /// + /// Prints the number in the form [-]d.ddd...dde±dd. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static var exponential: OSLogFloatFormatting { .exponential() } + + /// Displays an interpolated floating-point value in fprintf's `%e` format with + /// specified precision, and optional sign and case. + /// + /// Prints the number in the form [-]d.ddd...dde±dd, with `precision` significant + /// digits following the radix point. Systems may impose an upper bound on the number + /// of digits that are supported. + /// + /// All parameters except `precision` must be boolean literals. + /// + /// - Parameters: + /// - precision: Number of digits to display after the radix point. + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. + /// - uppercase: Pass `true` to use uppercase letters or `false` to use + /// lowercase letters. The default is `false`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func exponential( + precision: @escaping @autoclosure () -> Int, + explicitPositiveSign: Bool = false, + uppercase: Bool = false + ) -> OSLogFloatFormatting { + return OSLogFloatFormatting( + explicitPositiveSign: explicitPositiveSign, + uppercase: uppercase, + precision: precision, + notation: .exponential + ) + } + + /// Displays an interpolated floating-point value in fprintf's `%e` format with + /// an optional sign and case. + /// + /// Prints the number in the form [-]d.ddd...dde±dd. + /// + /// All parameters to this function must be boolean literals. + /// + /// - Parameters: + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. + /// - uppercase: Pass `true` to use uppercase letters or `false` to use + /// lowercase letters. The default is `false`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func exponential( + explicitPositiveSign: Bool = false, + uppercase: Bool = false + ) -> OSLogFloatFormatting { + return OSLogFloatFormatting( + explicitPositiveSign: explicitPositiveSign, + uppercase: uppercase, + precision: nil, + notation: .exponential + ) + } + + /// Displays an interpolated floating-point value in fprintf's `%g` format. + /// + /// Behaves like `.fixed` when the number is scaled close to 1.0, and like + /// `.exponential` if it has a very large or small exponent. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static var hybrid: OSLogFloatFormatting { .hybrid() } + + /// Displays an interpolated floating-point value in fprintf's `%g` format with the + /// specified precision, and optional sign and case. + /// + /// Behaves like `.fixed` when the number is scaled close to 1.0, and like + /// `.exponential` if it has a very large or small exponent. + /// + /// All parameters except `precision` must be boolean literals. + /// + /// - Parameters: + /// - precision: Number of digits to display after the radix point. + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. + /// - uppercase: Pass `true` to use uppercase letters or `false` to use + /// lowercase letters. The default is `false`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func hybrid( + precision: @escaping @autoclosure () -> Int, + explicitPositiveSign: Bool = false, + uppercase: Bool = false + ) -> OSLogFloatFormatting { + return OSLogFloatFormatting( + explicitPositiveSign: explicitPositiveSign, + uppercase: uppercase, + precision: precision, + notation: .hybrid + ) + } + + /// Displays an interpolated floating-point value in fprintf's `%g` format with + /// optional sign and case. + /// + /// Behaves like `.fixed` when the number is scaled close to 1.0, and like + /// `.exponential` if it has a very large or small exponent. + /// + /// All parameters to this function must be boolean literals. + /// + /// - Parameters: + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. + /// - uppercase: Pass `true` to use uppercase letters or `false` to use + /// lowercase letters. The default is `false`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func hybrid( + explicitPositiveSign: Bool = false, + uppercase: Bool = false + ) -> OSLogFloatFormatting { + return OSLogFloatFormatting( + explicitPositiveSign: explicitPositiveSign, + uppercase: uppercase, + precision: nil, + notation: .hybrid + ) + } +} + +extension OSLogFloatFormatting { + /// Returns a fprintf-compatible length modifier for a given argument type + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + internal static func _formatStringLengthModifier( + _ type: I.Type + ) -> String? { + switch type { + // fprintf formatters promote Float to Double + case is Float.Type: return "" + case is Double.Type: return "" +#if !os(Windows) && (arch(i386) || arch(x86_64)) + // fprintf formatters use L for Float80 + case is Float80.Type: return "L" +#endif + default: return nil + } + } + + /// Constructs an os_log format specifier for the given type `type` + /// using the specified alignment `align` and privacy qualifier `privacy`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + internal func formatSpecifier( + for type: I.Type, + align: OSLogStringAlignment, + privacy: OSLogPrivacy + ) -> String { + var specification = "%" + // Add privacy qualifier after % sign within curly braces. This is an + // os log specific flag. + if let privacySpecifier = privacy.privacySpecifier { + specification += "{" + specification += privacySpecifier + specification += "}" + } + + // 1. Flags + // IEEE: `+` The result of a signed conversion shall always begin with a sign + // ( '+' or '-' ) + if explicitPositiveSign { + specification += "+" + } + + // IEEE: `-` The result of the conversion shall be left-justified within the field. + // The conversion is right-justified if this flag is not specified. + if case .start = align.anchor { + specification += "-" + } + + if let _ = align.minimumColumnWidth { + // The alignment could be a dynamic value. Therefore, use a star here and pass it + // as an additional argument. + specification += "*" + } + + if let _ = precision { + specification += ".*" + } + + guard let lengthModifier = + OSLogFloatFormatting._formatStringLengthModifier(type) else { + fatalError("Float type has unknown length") + } + specification += lengthModifier + + // 3. Precision and conversion specifier. + switch notation { + case .fixed: + specification += (uppercase ? "F" : "f") + case .exponential: + specification += (uppercase ? "E" : "e") + case .hybrid: + specification += (uppercase ? "G" : "g") + case .hex: + //guard type.radix == 2 else { return nil } + specification += (uppercase ? "A" : "a") + default: + fatalError("Unknown float notation") + } + return specification + } +} + diff --git a/stdlib/private/OSLog/OSLogFloatingPointTypes.swift b/stdlib/private/OSLog/OSLogFloatingPointTypes.swift index b7616b98215b8..e135911d2380b 100644 --- a/stdlib/private/OSLog/OSLogFloatingPointTypes.swift +++ b/stdlib/private/OSLog/OSLogFloatingPointTypes.swift @@ -17,46 +17,88 @@ // // The `appendInterpolation` functions defined in this file accept privacy // options along with the interpolated expression as shown below: -// TODO: support floating-point formatting options. // -// "\(x, privacy: .private\)" +// "\(x, format: .fixed(precision: 10), privacy: .private\)" extension OSLogInterpolation { - /// Define interpolation for expressions of type Float. + /// Defines interpolation for expressions of type Float. + /// + /// Do not call this function directly. It will be called automatically when interpolating + /// a value of type `Float` in the string interpolations passed to the log APIs. + /// /// - Parameters: - /// - number: the interpolated expression of type Float, which is autoclosured. - /// - privacy: a privacy qualifier which is either private or public. - /// It is auto-inferred by default. + /// - number: The interpolated expression of type Float, which is autoclosured. + /// - format: A formatting option available for float types, defined by the + /// type`OSLogFloatFormatting`. The default is `.fixed`. + /// - align: Left or right alignment with the minimum number of columns as + /// defined by the type `OSLogStringAlignment`. + /// - privacy: A privacy qualifier which is either private or public. + /// It is auto-inferred by default. @_semantics("constant_evaluable") - @_semantics("oslog.requires_constant_arguments") @inlinable @_optimize(none) + @_semantics("oslog.requires_constant_arguments") public mutating func appendInterpolation( _ number: @autoclosure @escaping () -> Float, + format: OSLogFloatFormatting = .fixed, + align: OSLogStringAlignment = .none, privacy: OSLogPrivacy = .auto ) { - appendInterpolation(Double(number()), privacy: privacy) + appendInterpolation( + Double(number()), + format: format, + align: align, + privacy: privacy) } /// Define interpolation for expressions of type Double. + /// + /// Do not call this function directly. It will be called automatically when interpolating + /// a value of type `Double` in the string interpolations passed to the log APIs. + /// /// - Parameters: - /// - number: the interpolated expression of type Double, which is autoclosured. - /// - privacy: a privacy qualifier which is either private or public. - /// It is auto-inferred by default. + /// - number: The interpolated expression of type Double, which is autoclosured. + /// - format: A formatting option available for float types, defined by the + /// type`OSLogFloatFormatting`. The default is `.fixed`. + /// - align: Left or right alignment with the minimum number of columns as + /// defined by the type `OSLogStringAlignment`. + /// - privacy: A privacy qualifier which is either private or public. + /// It is auto-inferred by default. @_semantics("constant_evaluable") - @_semantics("oslog.requires_constant_arguments") @inlinable @_optimize(none) + @_semantics("oslog.requires_constant_arguments") public mutating func appendInterpolation( _ number: @autoclosure @escaping () -> Double, + format: OSLogFloatFormatting = .fixed, + align: OSLogStringAlignment = .none, privacy: OSLogPrivacy = .auto ) { guard argumentCount < maxOSLogArgumentCount else { return } + formatString += + format.formatSpecifier(for: Double.self, align: align, privacy: privacy) - formatString += getDoubleFormatSpecifier(privacy: privacy) - addDoubleHeaders(privacy) + // If minimum column width is specified, append this value first. Note that + // the format specifier would use a '*' for width e.g. %*f. + if let minColumns = align.minimumColumnWidth { + appendAlignmentArgument(minColumns) + } + + // If the privacy has a mask, append the mask argument, which is a constant payload. + // Note that this should come after the width but before the precision. + if privacy.hasMask { + appendMaskArgument(privacy) + } + // If minimum number of digits (precision) is specified, append the + // precision before the argument. Note that the format specifier would use + // a '*' for precision: %.*f. + if let precision = format.precision { + appendPrecisionArgument(precision) + } + // Append the double. + addDoubleHeaders(privacy) arguments.append(number) argumentCount += 1 } @@ -82,29 +124,6 @@ extension OSLogInterpolation { preamble = getUpdatedPreamble(privacy: privacy, isScalar: true) } - - /// Construct an os_log format specifier from the given parameters. - /// This function must be constant evaluable and all its arguments - /// must be known at compile time. - @inlinable - @_semantics("constant_evaluable") - @_effects(readonly) - @_optimize(none) - internal func getDoubleFormatSpecifier(privacy: OSLogPrivacy) -> String { - // TODO: this will become more sophisticated when floating-point formatting - // options are supported. - var specifier = "%" - switch privacy { - case .private: - specifier += "{private}" - case .public: - specifier += "{public}" - default: - break - } - specifier += "f" - return specifier - } } extension OSLogArguments { @@ -115,7 +134,7 @@ extension OSLogArguments { @inlinable @_optimize(none) internal mutating func append(_ value: @escaping () -> Double) { - argumentClosures.append({ (position, _) in + argumentClosures.append({ (position, _, _) in serialize(value(), at: &position) }) } @@ -125,14 +144,13 @@ extension OSLogArguments { /// specified by os_log. Note that this is marked transparent instead of /// @inline(__always) as it is used in optimize(none) functions. @_transparent -@usableFromInline +@_alwaysEmitIntoClient internal func doubleSizeInBytes() -> Int { return 8 } /// Serialize a double at the buffer location that `position` points to and /// increment `position` by the byte size of the double. -@inlinable @_alwaysEmitIntoClient @inline(__always) internal func serialize( @@ -145,3 +163,4 @@ internal func serialize( withUnsafeBytes(of: value) { dest.copyMemory(from: $0) } bufferPosition += byteCount } + diff --git a/stdlib/private/OSLog/OSLogIntegerFormatting.swift b/stdlib/private/OSLog/OSLogIntegerFormatting.swift index 9dc1389efd208..2d9a53563c3bc 100644 --- a/stdlib/private/OSLog/OSLogIntegerFormatting.swift +++ b/stdlib/private/OSLog/OSLogIntegerFormatting.swift @@ -17,23 +17,30 @@ public struct OSLogIntegerFormatting { /// The base to use for the string representation. `radix` must be at least 2 /// and at most 36. The default is 10. - public var radix: Int + @usableFromInline + internal var radix: Int /// When set, a `+` will be printed for all non-negative integers. - public var explicitPositiveSign: Bool + @usableFromInline + internal var explicitPositiveSign: Bool /// When set, a prefix: 0b or 0o or 0x will be added when the radix is 2, 8 or /// 16 respectively. - public var includePrefix: Bool + @usableFromInline + internal var includePrefix: Bool /// Whether to use uppercase letters to represent numerals /// greater than 9 (default is to use lowercase). - public var uppercase: Bool + @usableFromInline + internal var uppercase: Bool /// Minimum number of digits to display. Numbers having fewer digits than /// minDigits will be displayed with leading zeros. - public var minDigits: (() -> Int)? + @usableFromInline + internal var minDigits: (() -> Int)? + /// Initializes all stored properties. + /// /// - Parameters: /// - radix: The base to use for the string representation. `radix` must be /// at least 2 and at most 36. The default is 10. @@ -46,9 +53,8 @@ public struct OSLogIntegerFormatting { /// `false`. /// - minDigits: minimum number of digits to display. Numbers will be /// prefixed with zeros if necessary to meet the minimum. The default is 1. - @_semantics("constant_evaluable") - @inlinable - @_optimize(none) + @_transparent + @usableFromInline internal init( radix: Int = 10, explicitPositiveSign: Bool = false, @@ -56,8 +62,6 @@ public struct OSLogIntegerFormatting { uppercase: Bool = false, minDigits: (() -> Int)? ) { - precondition(radix >= 2 && radix <= 36) - self.radix = radix self.explicitPositiveSign = explicitPositiveSign self.includePrefix = includePrefix @@ -65,11 +69,17 @@ public struct OSLogIntegerFormatting { self.minDigits = minDigits } + /// Displays an interpolated integer as a decimal number with the specified number + /// of digits and an optional sign. + /// + /// The parameter `explicitPositiveSign` must be a boolean literal. The + /// parameter `minDigits` can be an arbitrary expression. + /// /// - Parameters: /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative - /// numbers. Default is `false`. + /// numbers. /// - minDigits: minimum number of digits to display. Numbers will be - /// prefixed with zeros if necessary to meet the minimum. The default is 1. + /// prefixed with zeros if necessary to meet the minimum. @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -83,9 +93,13 @@ public struct OSLogIntegerFormatting { minDigits: minDigits) } + /// Displays an interpolated integer as a decimal number with an optional sign. + /// + /// The parameter `explicitPositiveSign` must be a boolean literal. + /// /// - Parameters: /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative - /// numbers. Default is `false`. + /// numbers. @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -98,22 +112,28 @@ public struct OSLogIntegerFormatting { minDigits: nil) } - /// Default decimal format. + /// Displays an interpolated integer as a decimal number. This is the default format for + /// integers. @_semantics("constant_evaluable") @inlinable @_optimize(none) public static var decimal: OSLogIntegerFormatting { .decimal() } + /// Displays an interpolated unsigned integer as a hexadecimal number with the + /// specified parameters. This formatting option should be used only with unsigned + /// integers. + /// + /// All parameters except `minDigits` should be boolean literals. `minDigits` + /// can be an arbitrary expression. + /// /// - Parameters: /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative - /// numbers. Default is `false`. - /// - includePrefix: Pass `true` to add a prefix: 0b, 0o, 0x to corresponding - /// radices. Default is `false`. + /// numbers. + /// - includePrefix: Pass `true` to add a prefix 0x. /// - uppercase: Pass `true` to use uppercase letters to represent numerals - /// greater than 9, or `false` to use lowercase letters. The default is - /// `false`. + /// greater than 9, or `false` to use lowercase letters. The default is `false`. /// - minDigits: minimum number of digits to display. Numbers will be - /// prefixed with zeros if necessary to meet the minimum. The default is 1. + /// prefixed with zeros if necessary to meet the minimum. @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -131,14 +151,17 @@ public struct OSLogIntegerFormatting { minDigits: minDigits) } + /// Displays an interpolated unsigned integer as a hexadecimal number with the specified + /// parameters. This formatting option should be used only with unsigned integers. + /// + /// All parameters should be boolean literals. + /// /// - Parameters: /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative - /// numbers. Default is `false`. - /// - includePrefix: Pass `true` to add a prefix: 0b, 0o, 0x to corresponding - /// radices. Default is `false`. + /// numbers. + /// - includePrefix: Pass `true` to add a prefix 0x. /// - uppercase: Pass `true` to use uppercase letters to represent numerals - /// greater than 9, or `false` to use lowercase letters. The default is - /// `false`. + /// greater than 9, or `false` to use lowercase letters. The default is `false`. @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -155,22 +178,28 @@ public struct OSLogIntegerFormatting { minDigits: nil) } - /// Default hexadecimal format. + /// Displays an interpolated unsigned integer as a hexadecimal number. + /// This formatting option should be used only with unsigned integers. @_semantics("constant_evaluable") @inlinable @_optimize(none) public static var hex: OSLogIntegerFormatting { .hex() } + /// Displays an interpolated unsigned integer as an octal number with the specified + /// parameters. This formatting option should be used only with unsigned + /// integers. + /// + /// All parameters except `minDigits` should be boolean literals. `minDigits` + /// can be an arbitrary expression. + /// /// - Parameters: /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative - /// numbers. Default is `false`. - /// - includePrefix: Pass `true` to add a prefix: 0b, 0o, 0x to corresponding - /// radices. Default is `false`. + /// numbers. + /// - includePrefix: Pass `true` to add a prefix 0o. /// - uppercase: Pass `true` to use uppercase letters to represent numerals - /// greater than 9, or `false` to use lowercase letters. The default is - /// `false`. + /// greater than 9, or `false` to use lowercase letters. The default is `false`. /// - minDigits: minimum number of digits to display. Numbers will be - /// prefixed with zeros if necessary to meet the minimum. The default is 1. + /// prefixed with zeros if necessary to meet the minimum. @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -188,14 +217,17 @@ public struct OSLogIntegerFormatting { minDigits: minDigits) } + /// Displays an interpolated unsigned integer as an octal number with the specified parameters. + /// This formatting option should be used only with unsigned integers. + /// + /// All parameters must be boolean literals. + /// /// - Parameters: /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative - /// numbers. Default is `false`. - /// - includePrefix: Pass `true` to add a prefix: 0b, 0o, 0x to corresponding - /// radices. Default is `false`. + /// numbers. + /// - includePrefix: Pass `true` to add a prefix 0o. /// - uppercase: Pass `true` to use uppercase letters to represent numerals - /// greater than 9, or `false` to use lowercase letters. The default is - /// `false`. + /// greater than 9, or `false` to use lowercase letters. @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -212,7 +244,8 @@ public struct OSLogIntegerFormatting { minDigits: nil) } - /// Default octal format. + /// Displays an interpolated unsigned integer as an octal number. + /// This formatting option should be used only with unsigned integers. @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -311,13 +344,10 @@ extension OSLogIntegerFormatting { // Add privacy qualifier after % sign within curly braces. This is an // os log specific flag. - switch privacy { - case .private: - specification += "{private}" - case .public: - specification += "{public}" - default: - break + if let privacySpecifier = privacy.privacySpecifier { + specification += "{" + specification += privacySpecifier + specification += "}" } // diff --git a/stdlib/private/OSLog/OSLogIntegerTypes.swift b/stdlib/private/OSLog/OSLogIntegerTypes.swift index 248e90e90c57a..dc24531648d42 100644 --- a/stdlib/private/OSLog/OSLogIntegerTypes.swift +++ b/stdlib/private/OSLog/OSLogIntegerTypes.swift @@ -24,19 +24,23 @@ extension OSLogInterpolation { - /// Define interpolation for expressions of type Int. + /// Defines interpolation for expressions of type Int. + /// + /// Do not call this function directly. It will be called automatically when interpolating + /// a value of type `Int` in the string interpolations passed to the log APIs. + /// /// - Parameters: - /// - number: the interpolated expression of type Int, which is autoclosured. - /// - format: a formatting option available for integer types, defined by the - /// type`OSLogIntegerFormatting`. The default is .decimal. - /// - align: left or right alignment with the minimum number of columns as - /// defined by the type `OSLogStringAlignment`. - /// - privacy: a privacy qualifier which is either private or public. - /// It is auto-inferred by default. + /// - number: The interpolated expression of type Int, which is autoclosured. + /// - format: A formatting option available for integer types, defined by the + /// type: `OSLogIntegerFormatting`. The default is `.decimal`. + /// - align: Left or right alignment with the minimum number of columns as + /// defined by the type `OSLogStringAlignment`. + /// - privacy: A privacy qualifier which is either private or public. + /// It is auto-inferred by default. @_semantics("constant_evaluable") - @_semantics("oslog.requires_constant_arguments") @inlinable @_optimize(none) + @_semantics("oslog.requires_constant_arguments") public mutating func appendInterpolation( _ number: @autoclosure @escaping () -> Int, format: OSLogIntegerFormatting = .decimal, @@ -46,18 +50,12 @@ extension OSLogInterpolation { appendInteger(number, format: format, align: align, privacy: privacy) } - /// Define interpolation for expressions of type Int32. - /// - Parameters: - /// - number: the interpolated expression of type Int32, which is autoclosured. - /// - format: a formatting option available for integer types, defined by the - /// type `OSLogIntegerFormatting`. - /// - align: left or right alignment with the minimum number of columns as - /// defined by the type `OSLogStringAlignment`. - /// - privacy: a privacy qualifier which is either private or public. - /// It is auto-inferred by default. + // Define appendInterpolation overloads for fixed-size integers. + @_semantics("constant_evaluable") @inlinable @_optimize(none) + @_semantics("oslog.requires_constant_arguments") public mutating func appendInterpolation( _ number: @autoclosure @escaping () -> Int32, format: OSLogIntegerFormatting = .decimal, @@ -67,19 +65,23 @@ extension OSLogInterpolation { appendInteger(number, format: format, align: align, privacy: privacy) } - /// Define interpolation for expressions of type UInt. + /// Defines interpolation for expressions of type UInt. + /// + /// Do not call this function directly. It will be called automatically when interpolating + /// a value of type `Int` in the string interpolations passed to the log APIs. + /// /// - Parameters: - /// - number: the interpolated expression of type UInt, which is autoclosured. - /// - format: a formatting option available for integer types, defined by the - /// type `OSLogIntegerFormatting`. - /// - align: left or right alignment with the minimum number of columns as - /// defined by the type `OSLogStringAlignment`. - /// - privacy: a privacy qualifier which is either private or public. - /// It is auto-inferred by default. + /// - number: The interpolated expression of type UInt, which is autoclosured. + /// - format: A formatting option available for integer types, defined by the + /// type `OSLogIntegerFormatting`. + /// - align: Left or right alignment with the minimum number of columns as + /// defined by the type `OSLogStringAlignment`. + /// - privacy: A privacy qualifier which is either private or public. + /// It is auto-inferred by default. @_semantics("constant_evaluable") - @_semantics("oslog.requires_constant_arguments") @inlinable @_optimize(none) + @_semantics("oslog.requires_constant_arguments") public mutating func appendInterpolation( _ number: @autoclosure @escaping () -> UInt, format: OSLogIntegerFormatting = .decimal, @@ -104,18 +106,27 @@ extension OSLogInterpolation { guard argumentCount < maxOSLogArgumentCount else { return } formatString += format.formatSpecifier(for: T.self, align: align, privacy: privacy) - // If minimum column width is specified, append this value first. Note that the - // format specifier would use a '*' for width e.g. %*d. + + // If minimum column width is specified, append this value first. Note that + // the format specifier would use a '*' for width e.g. %*d. if let minColumns = align.minimumColumnWidth { - appendPrecisionArgument(minColumns) + appendAlignmentArgument(minColumns) } - // If minimum number of digits (precision) is specified, append the precision before - // the argument. Note that the format specifier would use a '*' for precision: %.*d. + // If the privacy has a mask, append the mask argument, which is a constant payload. + // Note that this should come after the width but before the precision. + if privacy.hasMask { + appendMaskArgument(privacy) + } + + // If minimum number of digits (precision) is specified, append the + // precision before the argument. Note that the format specifier would use + // a '*' for precision: %.*d. if let minDigits = format.minDigits { appendPrecisionArgument(minDigits) } + // Append the integer. addIntHeaders(privacy, sizeForEncoding(T.self)) arguments.append(number) argumentCount += 1 @@ -151,19 +162,57 @@ extension OSLogInterpolation { @inlinable @_optimize(none) internal mutating func appendPrecisionArgument(_ count: @escaping () -> Int) { - // Note that we don't have to update the preamble here. - let argumentHeader = getArgumentHeader(privacy: .auto, type: .count) + appendPrecisionAlignCount( + count, + getArgumentHeader(privacy: .auto, type: .count)) + } + + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + internal mutating func appendAlignmentArgument(_ count: @escaping () -> Int) { + appendPrecisionAlignCount( + count, + getArgumentHeader(privacy: .auto, type: .scalar)) + } + + // This is made transparent to minimize compile time overheads. The function's + // implementation also uses literals whenever possible for the same reason. + @_transparent + @inlinable + internal mutating func appendPrecisionAlignCount( + _ count: @escaping () -> Int, + _ argumentHeader: UInt8 + ) { arguments.append(argumentHeader) // Append number of bytes needed to serialize the argument. - let byteCount = sizeForEncoding(CInt.self) - arguments.append(UInt8(byteCount)) + arguments.append(4) // Increment total byte size by the number of bytes needed for this // argument, which is the sum of the byte size of the argument and // two bytes needed for the headers. - totalBytesForSerializingArguments += 2 + byteCount + totalBytesForSerializingArguments += 6 // The count is expected to be a CInt. arguments.append({ CInt(count()) }) argumentCount += 1 + // Note that we don't have to update the preamble here. + } + + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + internal mutating func appendMaskArgument(_ privacy: OSLogPrivacy) { + arguments.append(getArgumentHeader(privacy: .auto, type: .mask)) + // Append number of bytes needed to serialize the mask. Mask is 64 bit payload. + arguments.append(8) + // Increment total byte size by the number of bytes needed for this + // argument, which is the sum of the byte size of the argument and + // two bytes needed for the headers. + totalBytesForSerializingArguments += 10 + // Append the mask value. This is a compile-time constant. + let maskValue = privacy.maskValue + arguments.append({ maskValue }) + argumentCount += 1 + // Note that we don't have to update the preamble here. } } @@ -177,7 +226,7 @@ extension OSLogArguments { internal mutating func append( _ value: @escaping () -> T ) where T: FixedWidthInteger { - argumentClosures.append({ (position, _) in + argumentClosures.append({ (position, _, _) in serialize(value(), at: &position) }) } @@ -188,7 +237,7 @@ extension OSLogArguments { /// it is marked transparent instead of @inline(__always) as it is used in /// optimize(none) functions. @_transparent -@usableFromInline +@_alwaysEmitIntoClient internal func sizeForEncoding( _ type: T.Type ) -> Int where T : FixedWidthInteger { @@ -197,7 +246,6 @@ internal func sizeForEncoding( /// Serialize an integer at the buffer location that `position` points to and /// increment `position` by the byte size of `T`. -@inlinable @_alwaysEmitIntoClient @inline(__always) internal func serialize( diff --git a/stdlib/private/OSLog/OSLogMessage.swift b/stdlib/private/OSLog/OSLogMessage.swift index e49b2a1aa46be..99fd0e0bf3c1f 100644 --- a/stdlib/private/OSLog/OSLogMessage.swift +++ b/stdlib/private/OSLog/OSLogMessage.swift @@ -11,47 +11,31 @@ //===----------------------------------------------------------------------===// // This file contains data structures and helper functions that are used by -// the new OS log APIs. These are prototype implementations and should not be -// used outside of tests. - -/// Privacy qualifiers for indicating the privacy level of the logged data -/// to the logging system. These can be specified in the string interpolation -/// passed to the log APIs. -/// For Example, -/// log.info("Login request from user id \(userid, privacy: .private)") -/// -/// See `OSLogInterpolation.appendInterpolation` definitions for default options -/// for each supported type. -public enum OSLogPrivacy { - case `private` - case `public` - case auto -} +// the new OS log APIs. + +import ObjectiveC /// Maximum number of arguments i.e., interpolated expressions that can /// be used in the string interpolations passed to the log APIs. -/// This limit is imposed by the ABI of os_log. +/// This limit is imposed by the logging system. @_semantics("constant_evaluable") @inlinable @_optimize(none) public var maxOSLogArgumentCount: UInt8 { return 48 } -/// Note that this is marked transparent instead of @inline(__always) as it is -/// used in optimize(none) functions. +// Note that this is marked transparent instead of @inline(__always) as it is +// used in optimize(none) functions. @_transparent -@usableFromInline +@_alwaysEmitIntoClient internal var logBitsPerByte: Int { return 3 } /// Represents a string interpolation passed to the log APIs. /// /// This type converts (through its methods) the given string interpolation into -/// a C-style format string and a sequence of arguments, which is represented -/// by the type `OSLogArguments`. +/// a C-style format string and a sequence of arguments. /// -/// Do not create an instance of this type directly. It is used by the compiler -/// when you pass a string interpolation to the log APIs. -/// Extend this type with more `appendInterpolation` overloads to enable -/// interpolating additional types. +/// - Warning: Do not explicitly refer to this type. It will be implicitly created +/// by the compiler when you pass a string interpolation to the log APIs. @frozen public struct OSLogInterpolation : StringInterpolationProtocol { /// A format string constructed from the given string interpolation to be @@ -82,39 +66,14 @@ public struct OSLogInterpolation : StringInterpolationProtocol { @usableFromInline internal var arguments: OSLogArguments - /// The possible values for the argument flag, as defined by the os_log ABI, - /// which occupies four least significant bits of the first byte of the - /// argument header. The first two bits are used to indicate privacy and - /// the other two are reserved. - @usableFromInline - @frozen - internal enum ArgumentFlag { - case autoFlag - case privateFlag - case publicFlag - - @inlinable - internal var rawValue: UInt8 { - switch self { - case .autoFlag: - return 0 - case .privateFlag: - return 0x1 - case .publicFlag: - return 0x2 - } - } - } - /// The possible values for the argument type, as defined by the os_log ABI, /// which occupies four most significant bits of the first byte of the /// argument header. The rawValue of this enum must be constant evaluable. /// (Note that an auto-generated rawValue is not constant evaluable because /// it cannot be annotated so.) @usableFromInline - @frozen internal enum ArgumentType { - case scalar, count, string, pointer, object + case scalar, count, string, pointer, object, mask @inlinable internal var rawValue: UInt8 { @@ -127,7 +86,9 @@ public struct OSLogInterpolation : StringInterpolationProtocol { return 2 case .pointer: return 3 - case .object: + case .mask: + return 7 + default: //.object return 4 } } @@ -138,25 +99,18 @@ public struct OSLogInterpolation : StringInterpolationProtocol { @usableFromInline internal var preamble: UInt8 - /// Bit mask for setting bits in the peamble. The bits denoted by the bit - /// mask indicate whether there is an argument that is private, and whether - /// there is an argument that is non-scalar: String, NSObject or Pointer. - @usableFromInline - @frozen - internal enum PreambleBitMask { - case privateBitMask - case nonScalarBitMask + /// Denotes the bit that indicates whether there is private argument. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + internal var privateBitMask: UInt8 { 0x1 } - @inlinable - internal var rawValue: UInt8 { - switch self { - case .privateBitMask: - return 0x1 - case .nonScalarBitMask: - return 0x2 - } - } - } + /// Denotes the bit that indicates whether there is non-scalar argument: + /// String, NSObject or Pointer. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + internal var nonScalarBitMask: UInt8 { 0x2 } /// The second summary byte that denotes the number of arguments, which is /// also the number of interpolated expressions. This will be determined @@ -170,11 +124,24 @@ public struct OSLogInterpolation : StringInterpolationProtocol { @usableFromInline internal var totalBytesForSerializingArguments: Int + /// The number of arguments that are Strings. This count is used to create + /// auxiliary storage meant for extending the lifetime of the string arguments + /// until the log call completes. + @usableFromInline + internal var stringArgumentCount: Int + + /// The number of arguments that are NSObjects. This count is used to create + /// auxiliary storage meant for extending the lifetime of the NSObject + /// arguments until the log call completes. + @usableFromInline + internal var objectArgumentCount: Int + // Some methods defined below are marked @_optimize(none) to prevent inlining // of string internals (such as String._StringGuts) which will interfere with // constant evaluation and folding. Note that these methods will be inlined, // constant evaluated/folded and optimized in the context of a caller. + @_semantics("oslog.interpolation.init") @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -186,6 +153,8 @@ public struct OSLogInterpolation : StringInterpolationProtocol { preamble = 0 argumentCount = 0 totalBytesForSerializingArguments = 0 + stringArgumentCount = 0 + objectArgumentCount = 0 } @_semantics("constant_evaluable") @@ -197,23 +166,6 @@ public struct OSLogInterpolation : StringInterpolationProtocol { /// `appendInterpolation` conformances will be added by extensions to this type. - /// Return true if and only if the parameter is .private. - /// This function must be constant evaluable. - @inlinable - @_semantics("constant_evaluable") - @_effects(readonly) - @_optimize(none) - internal func getArugmentFlag(_ privacy: OSLogPrivacy) -> ArgumentFlag { - switch privacy { - case .public: - return .publicFlag - case .private: - return .privateFlag - default: - return .autoFlag - } - } - /// Compute a byte-sized argument header consisting of flag and type. /// Flag and type take up the least and most significant four bits /// of the header byte, respectively. @@ -226,9 +178,7 @@ public struct OSLogInterpolation : StringInterpolationProtocol { privacy: OSLogPrivacy, type: ArgumentType ) -> UInt8 { - let flag = getArugmentFlag(privacy) - let flagAndType: UInt8 = (type.rawValue &<< 4) | flag.rawValue - return flagAndType + return (type.rawValue &<< 4) | privacy.argumentFlag } /// Compute the new preamble based whether the current argument is private @@ -242,13 +192,11 @@ public struct OSLogInterpolation : StringInterpolationProtocol { isScalar: Bool ) -> UInt8 { var preamble = self.preamble - // Equality comparisions on enums is not yet supported by the constant - // evaluator. - if case .private = privacy { - preamble |= PreambleBitMask.privateBitMask.rawValue + if privacy.isAtleastPrivate { + preamble |= privateBitMask } - if !isScalar { - preamble |= PreambleBitMask.nonScalarBitMask.rawValue + if !isScalar || privacy.hasMask { + preamble |= nonScalarBitMask } return preamble } @@ -258,7 +206,8 @@ extension String { /// Replace all percents "%" in the string by "%%" so that the string can be /// interpreted as a C format string. This function is constant evaluable /// and its semantics is modeled within the evaluator. - public var percentEscapedString: String { + @inlinable + internal var percentEscapedString: String { @_semantics("string.escapePercent.get") @_effects(readonly) @_optimize(none) @@ -270,14 +219,17 @@ extension String { } } +/// Represents a message passed to the log APIs. This type should be created +/// from a string interpolation or a string literal. +/// +/// Do not explicitly refer to this type. It will be implicitly created +/// by the compiler when you pass a string interpolation to the log APIs. @frozen public struct OSLogMessage : ExpressibleByStringInterpolation, ExpressibleByStringLiteral { public let interpolation: OSLogInterpolation - /// Initializer for accepting string interpolations. This function must be - /// constant evaluable. @inlinable @_optimize(none) @_semantics("oslog.message.init_interpolation") @@ -286,8 +238,6 @@ public struct OSLogMessage : self.interpolation = stringInterpolation } - /// Initializer for accepting string literals. This function must be - /// constant evaluable. @inlinable @_optimize(none) @_semantics("oslog.message.init_stringliteral") @@ -298,19 +248,25 @@ public struct OSLogMessage : self.interpolation = s } - /// The byte size of the buffer that will be passed to the C os_log ABI. - /// It will contain the elements of `interpolation.arguments` and the two - /// summary bytes: preamble and argument count. + /// The byte size of the buffer that will be passed to the logging system. @_semantics("constant_evaluable") @inlinable @_optimize(none) public var bufferSize: Int { + // The two additional bytes is for the preamble and argument count. return interpolation.totalBytesForSerializingArguments + 2 } } -public typealias ByteBufferPointer = UnsafeMutablePointer -public typealias StorageObjects = [AnyObject] +@usableFromInline +internal typealias ByteBufferPointer = UnsafeMutablePointer +@usableFromInline +internal typealias ObjectStorage = UnsafeMutablePointer? +@usableFromInline +internal typealias ArgumentClosures = + [(inout ByteBufferPointer, + inout ObjectStorage, + inout ObjectStorage) -> ()] /// A representation of a sequence of arguments and headers (of possibly /// different types) that have to be serialized to a byte buffer. The arguments @@ -326,8 +282,7 @@ internal struct OSLogArguments { /// array of AnyObject to store references to auxiliary storage created during /// serialization. @usableFromInline - internal var argumentClosures: [(inout ByteBufferPointer, - inout StorageObjects) -> ()] + internal var argumentClosures: ArgumentClosures @_semantics("constant_evaluable") @inlinable @@ -342,7 +297,7 @@ internal struct OSLogArguments { @inlinable @_optimize(none) internal mutating func append(_ header: UInt8) { - argumentClosures.append({ (position, _) in + argumentClosures.append({ (position, _, _) in serialize(header, at: &position) }) } @@ -352,7 +307,6 @@ internal struct OSLogArguments { /// Serialize a UInt8 value at the buffer location pointed to by `bufferPosition`, /// and increment the `bufferPosition` with the byte size of the serialized value. -@inlinable @_alwaysEmitIntoClient @inline(__always) internal func serialize( @@ -362,3 +316,43 @@ internal func serialize( bufferPosition[0] = value bufferPosition += 1 } + +// The following code defines helper functions for creating and maintaining +// a buffer for holding a fixed number for instances of a type T. Such buffers +// are used to hold onto NSObjects and Strings that are interpolated in the log +// message until the end of the log call. + +@_alwaysEmitIntoClient +@inline(__always) +internal func createStorage( + capacity: Int, + type: T.Type +) -> ObjectStorage { + return + capacity == 0 ? + nil : + UnsafeMutablePointer.allocate(capacity: capacity) +} + +@_alwaysEmitIntoClient +@inline(__always) +internal func initializeAndAdvance( + _ storageOpt: inout ObjectStorage, + to value: T +) { + // This if statement should get optimized away. + if let storage = storageOpt { + storage.initialize(to: value) + storageOpt = storage.advanced(by: 1) + } +} + +@_alwaysEmitIntoClient +@inline(__always) +internal func destroyStorage(_ storageOpt: ObjectStorage, count: Int) { + // This if statement should get optimized away. + if let storage = storageOpt { + storage.deinitialize(count: count) + storage.deallocate() + } +} diff --git a/stdlib/private/OSLog/OSLogNSObjectType.swift b/stdlib/private/OSLog/OSLogNSObjectType.swift index 3942999a9c682..bcf1c7222f2f4 100644 --- a/stdlib/private/OSLog/OSLogNSObjectType.swift +++ b/stdlib/private/OSLog/OSLogNSObjectType.swift @@ -24,15 +24,18 @@ import ObjectiveC extension OSLogInterpolation { - /// Define interpolation for expressions of type NSObject. + /// Defines interpolation for expressions of type NSObject. + /// + /// Do not call this function directly. It will be called automatically when interpolating + /// a value of type `NSObject` in the string interpolations passed to the log APIs. + /// /// - Parameters: - /// - argumentObject: the interpolated expression of type NSObject, which is autoclosured. - /// - privacy: a privacy qualifier which is either private or public. - /// It is auto-inferred by default. + /// - argumentObject: The interpolated expression of type NSObject, which is autoclosured. + /// - privacy: A privacy qualifier which is either private or public. It is auto-inferred by default. @_semantics("constant_evaluable") - @_semantics("oslog.requires_constant_arguments") @inlinable @_optimize(none) + @_semantics("oslog.requires_constant_arguments") public mutating func appendInterpolation( _ argumentObject: @autoclosure @escaping () -> NSObject, privacy: OSLogPrivacy = .auto @@ -40,10 +43,14 @@ extension OSLogInterpolation { guard argumentCount < maxOSLogArgumentCount else { return } formatString += getNSObjectFormatSpecifier(privacy) + // If the privacy has a mask, append the mask argument, which is a constant payload. + if privacy.hasMask { + appendMaskArgument(privacy) + } addNSObjectHeaders(privacy) - arguments.append(argumentObject) argumentCount += 1 + objectArgumentCount += 1 } /// Update preamble and append argument headers based on the parameters of @@ -76,14 +83,14 @@ extension OSLogInterpolation { @_effects(readonly) @_optimize(none) internal func getNSObjectFormatSpecifier(_ privacy: OSLogPrivacy) -> String { - switch privacy { - case .private: - return "%{private}@" - case .public: - return "%{public}@" - default: - return "%@" + var specifier = "%" + if let privacySpecifier = privacy.privacySpecifier { + specifier += "{" + specifier += privacySpecifier + specifier += "}" } + specifier += "@" + return specifier } } @@ -95,20 +102,20 @@ extension OSLogArguments { @inlinable @_optimize(none) internal mutating func append(_ value: @escaping () -> NSObject) { - argumentClosures.append({ (position, _) in - serialize(value(), at: &position) + argumentClosures.append({ (position, objectArguments, _) in + serialize(value(), at: &position, storingObjectsIn: &objectArguments) }) } } /// Serialize an NSObject pointer at the buffer location pointed by /// `bufferPosition`. -@inlinable @_alwaysEmitIntoClient @inline(__always) internal func serialize( _ object: NSObject, - at bufferPosition: inout ByteBufferPointer + at bufferPosition: inout ByteBufferPointer, + storingObjectsIn objectArguments: inout ObjectStorage ) { let byteCount = pointerSizeInBytes(); let dest = @@ -116,9 +123,11 @@ internal func serialize( // Get the address of this NSObject as an UnsafeRawPointer. let objectAddress = Unmanaged.passUnretained(object).toOpaque() // Copy the address into the destination buffer. Note that the input NSObject - // is an interpolated expression and is guaranteed to be alive until the - // os_log ABI call is completed by the implementation. Therefore, passing - // this address to the os_log ABI is safe. + // is kept alive until the os_log ABI call completes by storing in the + // objectArguments. withUnsafeBytes(of: objectAddress) { dest.copyMemory(from: $0) } bufferPosition += byteCount + // This object could be newly created by the auto-closure. Therefore, make + // sure it is alive until the log call completes. + initializeAndAdvance(&objectArguments, to: object) } diff --git a/stdlib/private/OSLog/OSLogPrivacy.swift b/stdlib/private/OSLog/OSLogPrivacy.swift new file mode 100644 index 0000000000000..e0646ed6395b7 --- /dev/null +++ b/stdlib/private/OSLog/OSLogPrivacy.swift @@ -0,0 +1,220 @@ +//===----------------- OSLogPrivacy.swift ---------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// This file defines the APIs for specifying privacy in the log messages and also +// the logic for encoding them in the byte buffer passed to the libtrace library. + +/// Privacy options for specifying privacy level of the interpolated expressions +/// in the string interpolations passed to the log APIs. +@frozen +public struct OSLogPrivacy { + + @usableFromInline + internal enum PrivacyOption { + case `private` + case `public` + case auto + } + + public enum Mask { + /// Applies a salted hashing transformation to an interpolated value to redact it in the logs. + /// + /// Its purpose is to permit the correlation of identical values across multiple log lines + /// without revealing the value itself. + case hash + case none + } + + @usableFromInline + internal var privacy: PrivacyOption + + @usableFromInline + internal var mask: Mask + + @_transparent + @usableFromInline + internal init(privacy: PrivacyOption, mask: Mask) { + self.privacy = privacy + self.mask = mask + } + + /// Sets the privacy level of an interpolated value to public. + /// + /// When the privacy level is public, the value will be displayed + /// normally without any redaction in the logs. + @_semantics("constant_evaluable") + @_optimize(none) + @inlinable + public static var `public`: OSLogPrivacy { + OSLogPrivacy(privacy: .public, mask: .none) + } + + /// Sets the privacy level of an interpolated value to private. + /// + /// When the privacy level is private, the value will be redacted in the logs, + /// subject to the privacy configuration of the logging system. + @_semantics("constant_evaluable") + @_optimize(none) + @inlinable + public static var `private`: OSLogPrivacy { + OSLogPrivacy(privacy: .private, mask: .none) + } + + /// Sets the privacy level of an interpolated value to private and + /// applies a `mask` to the interpolated value to redacted it. + /// + /// When the privacy level is private, the value will be redacted in the logs, + /// subject to the privacy configuration of the logging system. + /// + /// If the value need not be redacted in the logs, its full value is captured as normal. + /// Otherwise (i.e. if the value would be redacted) the `mask` is applied to + /// the argument value and the result of the transformation is recorded instead. + /// + /// - Parameters: + /// - mask: Mask to use with the privacy option. + @_semantics("constant_evaluable") + @_optimize(none) + @inlinable + public static func `private`(mask: Mask) -> OSLogPrivacy { + OSLogPrivacy(privacy: .private, mask: mask) + } + + /// Auto-infers a privacy level for an interpolated value. + /// + /// The system will automatically decide whether the value should + /// be captured fully in the logs or should be redacted. + @_semantics("constant_evaluable") + @_optimize(none) + @inlinable + public static var auto: OSLogPrivacy { + OSLogPrivacy(privacy: .auto, mask: .none) + } + + /// Auto-infers a privacy level for an interpolated value and applies a `mask` + /// to the interpolated value to redacted it when necessary. + /// + /// The system will automatically decide whether the value should + /// be captured fully in the logs or should be redacted. + /// If the value need not be redacted in the logs, its full value is captured as normal. + /// Otherwise (i.e. if the value would be redacted) the `mask` is applied to + /// the argument value and the result of the transformation is recorded instead. + /// + /// - Parameters: + /// - mask: Mask to use with the privacy option. + @_semantics("constant_evaluable") + @_optimize(none) + @inlinable + public static func auto(mask: Mask) -> OSLogPrivacy { + OSLogPrivacy(privacy: .auto, mask: mask) + } + + /// Return an argument flag for the privacy option., as defined by the + /// os_log ABI, which occupies four least significant bits of the first byte of the + /// argument header. The first two bits are used to indicate privacy and + /// the other two are reserved. + @inlinable + @_semantics("constant_evaluable") + @_optimize(none) + internal var argumentFlag: UInt8 { + switch privacy { + case .private: + return 0x1 + case .public: + return 0x2 + default: + return 0 + } + } + + @inlinable + @_semantics("constant_evaluable") + @_optimize(none) + internal var isAtleastPrivate: Bool { + switch privacy { + case .public: + return false + case .auto: + return false + default: + return true + } + } + + @inlinable + @_semantics("constant_evaluable") + @_optimize(none) + internal var needsPrivacySpecifier: Bool { + if case .hash = mask { + return true + } + switch privacy { + case .auto: + return false + default: + return true + } + } + + @inlinable + @_transparent + internal var hasMask: Bool { + if case .hash = mask { + return true + } + return false + } + + /// A 64-bit value obtained by interpreting the mask name as a little-endian unsigned + /// integer. + @inlinable + @_transparent + internal var maskValue: UInt64 { + // Return the value of + // 'h' | 'a' << 8 | 's' << 16 | 'h' << 24 which equals + // 104 | (97 << 8) | (115 << 16) | (104 << 24) + 1752392040 + } + + /// Return an os log format specifier for this `privacy` level. The + /// format specifier goes within curly braces e.g. %{private} in the format + /// string passed to the os log ABI. + @inlinable + @_semantics("constant_evaluable") + @_optimize(none) + internal var privacySpecifier: String? { + let hasMask = self.hasMask + var isAuto = false + if case .auto = privacy { + isAuto = true + } + if isAuto, !hasMask { + return nil + } + var specifier: String + switch privacy { + case .public: + specifier = "public" + case .private: + specifier = "private" + default: + specifier = "" + } + if hasMask { + if !isAuto { + specifier += "," + } + specifier += "mask.hash" + } + return specifier + } +} + diff --git a/stdlib/private/OSLog/OSLogStringAlignment.swift b/stdlib/private/OSLog/OSLogStringAlignment.swift index 05afc4f430964..a93388a9cfef9 100644 --- a/stdlib/private/OSLog/OSLogStringAlignment.swift +++ b/stdlib/private/OSLog/OSLogStringAlignment.swift @@ -13,8 +13,8 @@ // This file defines types and functions for specifying alignment of the // interpolations passed to the os log APIs. -@frozen -public enum OSLogCollectionBound { +@usableFromInline +internal enum OSLogCollectionBound { case start case end } @@ -23,20 +23,24 @@ public enum OSLogCollectionBound { public struct OSLogStringAlignment { /// Minimum number of characters to be displayed. If the value to be printed /// is shorter than this number, the result is padded with spaces. The value - /// is not truncated even if the result is larger.This value need not be a + /// is not truncated even if the result is larger. This value need not be a /// compile-time constant, and is therefore an autoclosure. - public var minimumColumnWidth: (() -> Int)? + @usableFromInline + internal var minimumColumnWidth: (() -> Int)? + /// This captures right/left alignment. - public var anchor: OSLogCollectionBound + @usableFromInline + internal var anchor: OSLogCollectionBound + /// Initiailzes stored properties. + /// /// - Parameters: /// - minimumColumnWidth: Minimum number of characters to be displayed. If the value to be /// printed is shorter than this number, the result is padded with spaces. The value is not truncated /// even if the result is larger. /// - anchor: Use `.end` for right alignment and `.start` for left. - @_semantics("constant_evaluable") - @inlinable - @_optimize(none) + @_transparent + @usableFromInline internal init( minimumColumnWidth: (() -> Int)? = nil, anchor: OSLogCollectionBound = .end @@ -45,29 +49,18 @@ public struct OSLogStringAlignment { self.anchor = anchor } - /// Right alignment formatter. - @_semantics("constant_evaluable") - @inlinable - @_optimize(none) - public static var right: OSLogStringAlignment { - OSLogStringAlignment(anchor: .end) - } - - /// Left alignment formatter. - @_semantics("constant_evaluable") - @inlinable - @_optimize(none) - public static var left: OSLogStringAlignment { - OSLogStringAlignment(anchor: .start) - } - - /// Use default alignment, which is right alignment. + /// Indicates no alignment. @_semantics("constant_evaluable") @inlinable @_optimize(none) - public static var none: OSLogStringAlignment { .right } + public static var none: OSLogStringAlignment { OSLogStringAlignment(anchor: .end) } - /// Right align and display at least`columns` characters. + /// Right align and display at least `columns` characters. + /// + /// The interpolated value would be padded with spaces, if necessary, to + /// meet the specified `columns` characters. + /// + /// - Parameter columns: minimum number of characters to display. @_semantics("constant_evaluable") @inlinable @_optimize(none) @@ -77,7 +70,12 @@ public struct OSLogStringAlignment { OSLogStringAlignment(minimumColumnWidth: columns, anchor: .end) } - /// Left align and display at least`columns` characters. + /// Left align and display at least `columns` characters. + /// + /// The interpolated value would be padded with spaces, if necessary, to + /// meet the specified `columns` characters. + /// + /// - Parameter columns: minimum number of characters to display. @_semantics("constant_evaluable") @inlinable @_optimize(none) diff --git a/stdlib/private/OSLog/OSLogStringTypes.swift b/stdlib/private/OSLog/OSLogStringTypes.swift index 8a90b03c40598..ef0a7961916ec 100644 --- a/stdlib/private/OSLog/OSLogStringTypes.swift +++ b/stdlib/private/OSLog/OSLogStringTypes.swift @@ -14,8 +14,7 @@ // It defines `appendInterpolation` function for String type. It also defines // extensions for serializing strings into the argument buffer passed to // os_log ABIs. Note that os_log requires passing a stable pointer to an -// interpolated string. The SPI: `_convertConstStringToUTF8PointerArgument` -// is used to construct a stable pointer to a (dynamic) string. +// interpolated string. // // The `appendInterpolation` function defined in this file accept privacy and // alignment options along with the interpolated expression as shown below: @@ -25,17 +24,21 @@ extension OSLogInterpolation { - /// Define interpolation for expressions of type String. + /// Defines interpolation for expressions of type String. + /// + /// Do not call this function directly. It will be called automatically when interpolating + /// a value of type `String` in the string interpolations passed to the log APIs. + /// /// - Parameters: - /// - argumentString: the interpolated expression of type String, which is autoclosured. - /// - align: left or right alignment with the minimum number of columns as - /// defined by the type `OSLogStringAlignment`. - /// - privacy: a privacy qualifier which is either private or public. - /// It is auto-inferred by default. + /// - argumentString: The interpolated expression of type String, which is autoclosured. + /// - align: Left or right alignment with the minimum number of columns as + /// defined by the type `OSLogStringAlignment`. + /// - privacy: A privacy qualifier which is either private or public. + /// It is auto-inferred by default. @_semantics("constant_evaluable") - @_semantics("oslog.requires_constant_arguments") @inlinable @_optimize(none) + @_semantics("oslog.requires_constant_arguments") public mutating func appendInterpolation( _ argumentString: @autoclosure @escaping () -> String, align: OSLogStringAlignment = .none, @@ -48,12 +51,20 @@ extension OSLogInterpolation { // If minimum column width is specified, append this value first. Note that the // format specifier would use a '*' for width e.g. %*s. if let minColumns = align.minimumColumnWidth { - appendPrecisionArgument(minColumns) + appendAlignmentArgument(minColumns) + } + + // If the privacy has a mask, append the mask argument, which is a constant payload. + // Note that this should come after the width but before the precision. + if privacy.hasMask { + appendMaskArgument(privacy) } + // Append the string argument. addStringHeaders(privacy) arguments.append(argumentString) argumentCount += 1 + stringArgumentCount += 1 } /// Update preamble and append argument headers based on the parameters of @@ -90,13 +101,10 @@ extension OSLogInterpolation { _ privacy: OSLogPrivacy ) -> String { var specifier = "%" - switch privacy { - case .private: - specifier += "{private}" - case .public: - specifier += "{public}" - default: - break + if let privacySpecifier = privacy.privacySpecifier { + specifier += "{" + specifier += privacySpecifier + specifier += "}" } if case .start = align.anchor { specifier += "-" @@ -117,7 +125,12 @@ extension OSLogArguments { @inlinable @_optimize(none) internal mutating func append(_ value: @escaping () -> String) { - argumentClosures.append({ serialize(value(), at: &$0, using: &$1) }) + argumentClosures.append({ (position, _, stringArgumentOwners) in + serialize( + value(), + at: &position, + storingStringOwnersIn: &stringArgumentOwners) + }) } } @@ -129,34 +142,48 @@ extension OSLogArguments { /// This function must be constant evaluable. Note that it is marked transparent /// instead of @inline(__always) as it is used in optimize(none) functions. @_transparent -@usableFromInline +@_alwaysEmitIntoClient internal func pointerSizeInBytes() -> Int { return Int.bitWidth &>> logBitsPerByte } /// Serialize a stable pointer to the string `stringValue` at the buffer location -/// pointed by `bufferPosition`. When necessary, this function would copy the -/// string contents to a storage with a stable pointer. If that happens, a reference -/// to the storage will be added to `storageObjects`. -@inlinable +/// pointed to by `bufferPosition`. @_alwaysEmitIntoClient @inline(__always) internal func serialize( _ stringValue: String, - at bufferPosition: inout ByteBufferPointer, - using storageObjects: inout StorageObjects + at bufferPosition: inout UnsafeMutablePointer, + storingStringOwnersIn stringArgumentOwners: inout ObjectStorage ) { - let (optionalStorage, bytePointer): (AnyObject?, UnsafeRawPointer) = - _convertConstStringToUTF8PointerArgument( - stringValue) - - if let storage = optionalStorage { - storageObjects.append(storage) - } + let stringPointer = + getNullTerminatedUTF8Pointer( + stringValue, + storingStringOwnersIn: &stringArgumentOwners) let byteCount = pointerSizeInBytes() let dest = UnsafeMutableRawBufferPointer(start: bufferPosition, count: byteCount) - withUnsafeBytes(of: bytePointer) { dest.copyMemory(from: $0) } + withUnsafeBytes(of: stringPointer) { dest.copyMemory(from: $0) } bufferPosition += byteCount } + +/// Return a pointer that points to a contiguous sequence of null-terminated, +/// UTF8 charcters. If necessary, extends the lifetime of `stringValue` by +/// using `stringArgumentOwners`. +@_alwaysEmitIntoClient +@inline(never) +internal func getNullTerminatedUTF8Pointer( + _ stringValue: String, + storingStringOwnersIn stringArgumentOwners: inout ObjectStorage +) -> UnsafeRawPointer { + let (optStorage, bytePointer, _, _, _): + (AnyObject?, UnsafeRawPointer, Int, Bool, Bool) = + stringValue._deconstructUTF8(scratch: nil) + if let storage = optStorage { + initializeAndAdvance(&stringArgumentOwners, to: storage) + } else { + initializeAndAdvance(&stringArgumentOwners, to: stringValue._guts) + } + return bytePointer +} diff --git a/stdlib/private/OSLog/OSLogSwiftProtocols.swift b/stdlib/private/OSLog/OSLogSwiftProtocols.swift new file mode 100644 index 0000000000000..f14ee3c99bef8 --- /dev/null +++ b/stdlib/private/OSLog/OSLogSwiftProtocols.swift @@ -0,0 +1,72 @@ +//===----------------- OSLogSwiftProtocols.swift -----------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// This file defines extensions for interpolating types conforming to common +// Swift protocols. It defines `appendInterpolation` overloads for these protocols. +// All overloads defined in this file, delegate to other appendInterpolation +// functions for types natively supported by os_log. + +extension OSLogInterpolation { + + /// Defines interpolation for values conforming to CustomStringConvertible. The values + /// are displayed using the description methods on them. + /// + /// Do not call this function directly. It will be called automatically when interpolating + /// a value conforming to CustomStringConvertible in the string interpolations passed + /// to the log APIs. + /// + /// - Parameters: + /// - value: The interpolated expression conforming to CustomStringConvertible. + /// - align: Left or right alignment with the minimum number of columns as + /// defined by the type `OSLogStringAlignment`. + /// - privacy: A privacy qualifier which is either private or public. + /// It is auto-inferred by default. + @_optimize(none) + @_transparent + @_semantics("oslog.requires_constant_arguments") + public mutating func appendInterpolation( + _ value: @autoclosure @escaping () -> T, + align: OSLogStringAlignment = .none, + privacy: OSLogPrivacy = .auto + ) { + // TODO: Dead code elimination does not remove the call to the default value + // of alignment: .none. This function is made @_transparent to work around + // that. + appendInterpolation(value().description, align: align, privacy: privacy) + } + + /// Defines interpolation for meta-types. + /// + /// Do not call this function directly. It will be called automatically when interpolating + /// a value of type `Any.Type` in the string interpolations passed to the log APIs. + /// + /// - Parameters: + /// - value: An interpolated expression of type Any.Type, which is autoclosured. + /// - align: Left or right alignment with the minimum number of columns as + /// defined by the type `OSLogStringAlignment`. + /// - privacy: A privacy qualifier which is either private or public. + /// It is auto-inferred by default. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + @_semantics("oslog.requires_constant_arguments") + public mutating func appendInterpolation( + _ value: @autoclosure @escaping () -> Any.Type, + align: OSLogStringAlignment = .none, + privacy: OSLogPrivacy = .auto + ) { + appendInterpolation( + _typeName(value(), qualified: false), + align: align, + privacy: privacy) + } +} diff --git a/stdlib/private/OSLog/OSLogTestHelper.swift b/stdlib/private/OSLog/OSLogTestHelper.swift index 85dde5ca81055..3d6e98dbaaad1 100644 --- a/stdlib/private/OSLog/OSLogTestHelper.swift +++ b/stdlib/private/OSLog/OSLogTestHelper.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +import ObjectiveC + // This file contains test helpers for testing the compiler diagnostics and optimizations // of the new swift APIs for os log that accept string interpolations. @@ -51,41 +53,47 @@ func _osLogTestHelper( let preamble = message.interpolation.preamble let argumentCount = message.interpolation.argumentCount let bufferSize = message.bufferSize + let objectCount = message.interpolation.objectArgumentCount + let stringCount = message.interpolation.stringArgumentCount + let uint32bufferSize = UInt32(bufferSize) let argumentClosures = message.interpolation.arguments.argumentClosures + let formatStringPointer = _getGlobalStringTablePointer(formatString) // Code that will execute at runtime. if (!isLoggingEnabled()) { return } - - // Allocate a byte buffer to store the arguments. The buffer could be stack - // allocated as it is local to this function and also its size is a - // compile-time constant. let bufferMemory = UnsafeMutablePointer.allocate(capacity: bufferSize) - // Array of references to auxiliary storage created during serialization of - // strings. This array can be stack allocated. - var stringStorageObjects: [AnyObject] = [] + // Buffer for storing NSObjects and strings to keep them alive until the + // _os_log_impl_test call completes. + let objectArguments = createStorage(capacity: objectCount, type: NSObject.self) + let stringArgumentOwners = createStorage(capacity: stringCount, type: Any.self) var currentBufferPosition = bufferMemory + var objectArgumentsPosition = objectArguments + var stringArgumentOwnersPosition = stringArgumentOwners serialize(preamble, at: ¤tBufferPosition) serialize(argumentCount, at: ¤tBufferPosition) - argumentClosures.forEach { $0(¤tBufferPosition, &stringStorageObjects) } + argumentClosures.forEach { + $0(¤tBufferPosition, + &objectArgumentsPosition, + &stringArgumentOwnersPosition) + } _os_log_impl_test( assertion, formatString, formatStringPointer, bufferMemory, - UInt32(bufferSize)) + uint32bufferSize) - // The following operation extends the lifetime of argumentClosures, - // stringStorageObjects, and also of the objects stored in them, till this - // point. This is necessary because the assertion is passed internal pointers - // to the objects/strings stored in these arrays, as in the actual os log - // implementation. - _fixLifetime(argumentClosures) - _fixLifetime(stringStorageObjects) + // The following operation extends the lifetime of objectArguments and + // stringArgumentOwners till this point. This is necessary because the + // assertion is passed internal pointers to the objects/strings stored + // in these arrays, as in the actual os log implementation. + destroyStorage(objectArguments, count: objectCount) + destroyStorage(stringArgumentOwners, count: stringCount) bufferMemory.deallocate() } diff --git a/test/SILOptimizer/Inputs/OSLogConstantEvaluable.swift b/test/SILOptimizer/Inputs/OSLogConstantEvaluable.swift index f7a563f29582f..2207ea86c0c0b 100644 --- a/test/SILOptimizer/Inputs/OSLogConstantEvaluable.swift +++ b/test/SILOptimizer/Inputs/OSLogConstantEvaluable.swift @@ -45,3 +45,8 @@ func intValueWithPrecisionTest() -> OSLogMessage { \(10, format: .decimal(minDigits: 25), align: .right(columns: 10)) """ } + +@_semantics("test_driver") +func intValueWithPrivacyTest() -> OSLogMessage { + return "An integer value \(10, privacy: .private(mask: .hash))" +} diff --git a/test/SILOptimizer/OSLogFullOptTest.swift b/test/SILOptimizer/OSLogFullOptTest.swift index 8cc72d891f5e1..2d7a9b6812383 100644 --- a/test/SILOptimizer/OSLogFullOptTest.swift +++ b/test/SILOptimizer/OSLogFullOptTest.swift @@ -21,10 +21,6 @@ func testSimpleInterpolation() { // CHECK: tail call swiftcc i1 @"${{.*}}isLoggingEnabled{{.*}}"() // CHECK-NEXT: br i1 {{%.*}}, label %[[ENABLED:[0-9]+]], label %[[NOT_ENABLED:[0-9]+]] - // CHECK: [[NOT_ENABLED]]: - // CHECK-NEXT: tail call void @swift_release - // CHECK-NEXT: ret void - // CHECK: [[ENABLED]]: // // Header bytes. @@ -51,6 +47,10 @@ func testSimpleInterpolation() { // CHECK-32-NEXT: tail call swiftcc void @"${{.*}}_os_log_impl_test{{.*}}"({{.*}}, {{.*}}, {{.*}}, {{.*}}, i8* getelementptr inbounds ([27 x i8], [27 x i8]* @{{.*}}, i32 0, i32 0), i8* {{(nonnull )?}}[[BUFFER]], i32 8) // CHECK-NEXT: tail call void @swift_slowDealloc(i8* {{(nonnull )?}}[[BUFFER]] // CHECK-NEXT: br label %[[NOT_ENABLED]] + + // CHECK: [[NOT_ENABLED]]: + // CHECK-NEXT: tail call void @swift_release + // CHECK-NEXT: ret void } // CHECK-LABEL: define hidden swiftcc void @"${{.*}}testInterpolationWithMultipleArguments @@ -135,18 +135,15 @@ func testNSObjectInterpolation(nsArray: NSArray) { // CHECK-NEXT: tail call void @llvm.objc.release // CHECK-NEXT: br label %[[EXIT:[0-9]+]] - // CHECK: [[EXIT]]: - // CHECK-NEXT: tail call void @llvm.objc.release(i8* [[NSARRAY_ARG]]) - // CHECK-NEXT: tail call void @swift_release - // CHECK-NEXT: ret void - // CHECK: [[ENABLED]]: // // Header bytes. // // CHECK-64: [[BUFFER:%.+]] = tail call noalias i8* @swift_slowAlloc(i64 12 // CHECK-32: [[BUFFER:%.+]] = tail call noalias i8* @swift_slowAlloc(i32 8 - // CHECK-NEXT: store i8 2, i8* [[BUFFER]], align 1 + // CHECK-64: [[OBJ_STORAGE:%.+]] = tail call noalias i8* @swift_slowAlloc(i64 8 + // CHECK-32: [[OBJ_STORAGE:%.+]] = tail call noalias i8* @swift_slowAlloc(i32 4 + // CHECK: store i8 2, i8* [[BUFFER]], align 1 // CHECK-NEXT: [[OFFSET1:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 1 // CHECK-NEXT: store i8 1, i8* [[OFFSET1]], align 1 // @@ -157,18 +154,29 @@ func testNSObjectInterpolation(nsArray: NSArray) { // CHECK-NEXT: [[OFFSET3:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 3 // CHECK-64-NEXT: store i8 8, i8* [[OFFSET3]], align 1 // CHECK-32-NEXT: store i8 4, i8* [[OFFSET3]], align 1 - // CHECK-NEXT: tail call void @llvm.objc.release // CHECK-NEXT: bitcast %swift.refcounted* %{{.*}} to %swift.opaque* // CHECK-NEXT: [[OFFSET4:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 4 // CHECK-NEXT: [[BITCASTED_DEST:%.+]] = bitcast i8* [[OFFSET4]] to %TSo7NSArrayC** // CHECK-NEXT: [[BITCASTED_SRC:%.+]] = bitcast i8* [[NSARRAY_ARG]] to %TSo7NSArrayC* // CHECK-NEXT: store %TSo7NSArrayC* [[BITCASTED_SRC]], %TSo7NSArrayC** [[BITCASTED_DEST]], align 1 + // CHECK-NEXT: [[BITCASTED_DEST2:%.+]] = bitcast i8* [[OBJ_STORAGE]] to %TSo7NSArrayC** + // CHECK-NEXT: [[BITCASTED_SRC2:%.+]] = bitcast i8* [[NSARRAY_ARG]] to %TSo7NSArrayC* + // CHECK-64-NEXT: store %TSo7NSArrayC* [[BITCASTED_SRC2]], %TSo7NSArrayC** [[BITCASTED_DEST2]], align 8 + // CHECK-32-NEXT: store %TSo7NSArrayC* [[BITCASTED_SRC2]], %TSo7NSArrayC** [[BITCASTED_DEST2]], align 4 // CHECK-64-NEXT: tail call swiftcc void @"${{.*}}_os_log_impl_test{{.*}}"({{.*}}, {{.*}}, {{.*}}, {{.*}}, i8* getelementptr inbounds ([20 x i8], [20 x i8]* @{{.*}}, i64 0, i64 0), i8* {{(nonnull )?}}[[BUFFER]], i32 12) // CHECK-32-NEXT: tail call swiftcc void @"${{.*}}_os_log_impl_test{{.*}}"({{.*}}, {{.*}}, {{.*}}, {{.*}}, i8* getelementptr inbounds ([20 x i8], [20 x i8]* @{{.*}}, i32 0, i32 0), i8* {{(nonnull )?}}[[BUFFER]], i32 8) + // CHECK-NEXT: [[BITCASTED_OBJ_STORAGE:%.+]] = bitcast i8* [[OBJ_STORAGE]] to %swift.opaque* + // CHECK-NEXT: tail call void @swift_arrayDestroy(%swift.opaque* [[BITCASTED_OBJ_STORAGE]] + // CHECK-NEXT: tail call void @swift_slowDealloc(i8* {{(nonnull )?}}[[OBJ_STORAGE]] // CHECK-NEXT: tail call void @swift_slowDealloc(i8* {{(nonnull )?}}[[BUFFER]] // CHECK-NEXT: tail call void @swift_release // CHECK-NEXT: br label %[[EXIT]] + + // CHECK: [[EXIT]]: + // CHECK-NEXT: tail call void @llvm.objc.release(i8* [[NSARRAY_ARG]]) + // CHECK-NEXT: tail call void @swift_release + // CHECK-NEXT: ret void } // CHECK-LABEL: define hidden swiftcc void @"${{.*}}testFloatInterpolation @@ -178,10 +186,6 @@ func testFloatInterpolation(doubleValue: Double) { // CHECK: tail call swiftcc i1 @"${{.*}}isLoggingEnabled{{.*}}"() // CHECK-NEXT: br i1 {{%.*}}, label %[[ENABLED:[0-9]+]], label %[[NOT_ENABLED:[0-9]+]] - // CHECK: [[NOT_ENABLED]]: - // CHECK-NEXT: tail call void @swift_release - // CHECK-NEXT: ret void - // CHECK: [[ENABLED]]: // // Header bytes. @@ -204,6 +208,10 @@ func testFloatInterpolation(doubleValue: Double) { // CHECK-NEXT: tail call swiftcc void @"${{.*}}_os_log_impl_test{{.*}}"({{.*}}, {{.*}}, {{.*}}, {{.*}}, i8* getelementptr inbounds ([17 x i8], [17 x i8]* @{{.*}}, i{{.*}} 0, i{{.*}} 0), i8* {{(nonnull )?}}[[BUFFER]], i32 12) // CHECK-NEXT: tail call void @swift_slowDealloc(i8* {{(nonnull )?}}[[BUFFER]] // CHECK-NEXT: br label %[[NOT_ENABLED]] + + // CHECK: [[NOT_ENABLED]]: + // CHECK-NEXT: tail call void @swift_release + // CHECK-NEXT: ret void } // This test checks that the precision and alignment are optimally "stored" into the @@ -235,7 +243,7 @@ func testDynamicPrecisionAndAlignment() { // First argument bytes. // // CHECK-NEXT: [[OFFSET2:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 2 - // CHECK-NEXT: store i8 16, i8* [[OFFSET2]], align 1 + // CHECK-NEXT: store i8 0, i8* [[OFFSET2]], align 1 // CHECK-NEXT: [[OFFSET3:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 3 // CHECK-NEXT: store i8 4, i8* [[OFFSET3]], align 1 // CHECK-NEXT: [[OFFSET4:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 4 @@ -269,5 +277,110 @@ func testDynamicPrecisionAndAlignment() { // CHECK-NEXT: br label %[[NOT_ENABLED]] } -// TODO: add test for String. It is more complicated due to more complex logic -// in string serialization. +// CHECK-LABEL: define hidden swiftcc void @"${{.*}}testStringInterpolation +func testStringInterpolation(stringValue: String) { + _osLogTestHelper("String value: \(stringValue)") + // CHECK: entry: + // CHECK-64: call %swift.bridge* @swift_bridgeObjectRetain_n(%swift.bridge* %1 + // CHECK-32: tail call void @"$ss13_StringObjectV7VariantOWOy"(i32 %1 + // CHECK-32-NEXT: tail call void @"$ss13_StringObjectV7VariantOWOy"(i32 %1 + // CHECK: tail call swiftcc i1 @"${{.*}}isLoggingEnabled{{.*}}"() + // CHECK-NEXT: br i1 {{%.*}}, label %[[ENABLED:[0-9]+]], label %[[NOT_ENABLED:[0-9]+]] + + // CHECK: [[NOT_ENABLED]]: + // CHECK-64: call void @swift_bridgeObjectRelease_n(%swift.bridge* %1 + // CHECK-32: tail call void @"$ss13_StringObjectV7VariantOWOe"(i32 %1 + // CHECK-32-NEXT: tail call void @"$ss13_StringObjectV7VariantOWOe"(i32 %1 + // CHECK: br label %[[EXIT:[0-9]+]] + + // CHECK: [[ENABLED]]: + // + // Header bytes. + // + // CHECK-64: [[BUFFER:%.+]] = tail call noalias i8* @swift_slowAlloc(i{{.*}} 12 + // CHECK-32: [[BUFFER:%.+]] = tail call noalias i8* @swift_slowAlloc(i{{.*}} 8 + // CHECK-64-NEXT: [[STR_STORAGE:%.+]] = tail call noalias i8* @swift_slowAlloc(i{{.*}} 32 + // CHECK-32-NEXT: [[STR_STORAGE:%.+]] = tail call noalias i8* @swift_slowAlloc(i{{.*}} 16 + // CHECK: call void @llvm.lifetime.start{{.*}}({{.*}}, i8* {{(nonnull )?}}[[STR_STORAGE_PTR:%.*]] + // CHECK: store i8 2, i8* [[BUFFER]], align 1 + // CHECK-NEXT: [[OFFSET1:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 1 + // CHECK-NEXT: store i8 1, i8* [[OFFSET1]], align 1 + + // Argument bytes. + // + // CHECK-NEXT: [[OFFSET2:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 2 + // CHECK-NEXT: store i8 32, i8* [[OFFSET2]], align 1 + // CHECK-NEXT: [[OFFSET3:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 3 + // CHECK-64-NEXT: store i8 8, i8* [[OFFSET3]], align 1 + // CHECK-32-NEXT: store i8 4, i8* [[OFFSET3]], align 1 + + // CHECK: [[STR_POINTER:%.*]] = call swiftcc i8* @"${{.*}}getNullTerminatedUTF8Pointer{{.*}}"(i{{.*}} %0, {{.*}} %1 + + // CHECK: [[OFFSET_BUFFER:%.*]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 4 + // CHECK-NEXT: [[OFFSET_BUFFER_PTR:%.*]] = bitcast i8* [[OFFSET_BUFFER]] to i8** + // CHECK-NEXT: store i8* [[STR_POINTER]], i8** [[OFFSET_BUFFER_PTR]] + + // os_log_impl call. + // CHECK-64-NEXT: tail call swiftcc void @"${{.*}}_os_log_impl_test{{.*}}"({{.*}}, {{.*}}, {{.*}}, {{.*}}, i8* getelementptr inbounds ([17 x i8], [17 x i8]* @{{.*}}, i64 0, i64 0), i8* {{(nonnull )?}}[[BUFFER]], i32 12) + // CHECK-32-NEXT: tail call swiftcc void @"${{.*}}_os_log_impl_test{{.*}}"({{.*}}, {{.*}}, {{.*}}, {{.*}}, i8* getelementptr inbounds ([17 x i8], [17 x i8]* @{{.*}}, i32 0, i32 0), i8* {{(nonnull )?}}[[BUFFER]], i32 8) + // CHECK-NEXT: [[BITCASTED_STR_STORAGE:%.*]] = bitcast i8* [[STR_STORAGE]] to %swift.opaque* + // CHECK-NEXT: tail call void @swift_arrayDestroy(%swift.opaque* [[BITCASTED_STR_STORAGE]] + // CHECK-NEXT: tail call void @swift_slowDealloc(i8* {{(nonnull )?}}[[STR_STORAGE]] + // CHECK-NEXT: tail call void @swift_slowDealloc(i8* {{(nonnull )?}}[[BUFFER]] + // CHECK: call void @llvm.lifetime.end{{.*}}({{.*}}, i8* {{(nonnull )?}}[[STR_STORAGE_PTR]] + // CHECK-NEXT: br label %[[EXIT]] + + // CHECK: [[EXIT]]: + // CHECK-NEXT: ret void +} + +// CHECK-LABEL: define hidden swiftcc void @"${{.*}}testMetatypeInterpolation +func testMetatypeInterpolation(of type: T.Type) { + _osLogTestHelper("Metatype value: \(type)") + // CHECK: entry: + // CHECK: tail call swiftcc i1 @"${{.*}}isLoggingEnabled{{.*}}"() + // CHECK-NEXT: br i1 {{%.*}}, label %[[ENABLED:[0-9]+]], label %[[NOT_ENABLED:[0-9]+]] + + // CHECK: [[NOT_ENABLED]]: + // CHECK-NEXT: call void @swift_release + // CHECK-NEXT: br label %[[EXIT:[0-9]+]] + + // CHECK: [[ENABLED]]: + // + // Header bytes. + // + // CHECK-64: [[BUFFER:%.+]] = tail call noalias i8* @swift_slowAlloc(i{{.*}} 12 + // CHECK-32: [[BUFFER:%.+]] = tail call noalias i8* @swift_slowAlloc(i{{.*}} 8 + // CHECK-64-NEXT: [[STR_STORAGE:%.+]] = tail call noalias i8* @swift_slowAlloc(i{{.*}} 32 + // CHECK-32-NEXT: [[STR_STORAGE:%.+]] = tail call noalias i8* @swift_slowAlloc(i{{.*}} 16 + // CHECK: call void @llvm.lifetime.start{{.*}}({{.*}}, i8* {{(nonnull )?}}[[STR_STORAGE_PTR:%.*]] + // CHECK: store i8 2, i8* [[BUFFER]], align 1 + // CHECK-NEXT: [[OFFSET1:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 1 + // CHECK-NEXT: store i8 1, i8* [[OFFSET1]], align 1 + + // Argument bytes. + // + // CHECK-NEXT: [[OFFSET2:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 2 + // CHECK-NEXT: store i8 32, i8* [[OFFSET2]], align 1 + // CHECK-NEXT: [[OFFSET3:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 3 + // CHECK-64-NEXT: store i8 8, i8* [[OFFSET3]], align 1 + // CHECK-32-NEXT: store i8 4, i8* [[OFFSET3]], align 1 + // CHECK-NEXT: [[TYPENAME:%.+]] = tail call swiftcc { i{{.*}}, {{.*}} } @"${{.*}}_typeName{{.*}}"({{.*}} %0 + // CHECK-NEXT: [[TYPENAME_0:%.+]] = extractvalue { i{{.*}}, {{.*}} } [[TYPENAME]], 0 + // CHECK-NEXT: [[TYPENAME_1:%.+]] = extractvalue { i{{.*}}, {{.*}} } [[TYPENAME]], 1 + + // CHECK: [[STR_POINTER:%.*]] = call swiftcc i8* @"${{.*}}getNullTerminatedUTF8Pointer{{.*}}"(i{{.*}} [[TYPENAME_0]], {{.*}} [[TYPENAME_1]] + + // os_log_impl call. + // CHECK-64: tail call swiftcc void @"${{.*}}_os_log_impl_test{{.*}}"({{.*}}, {{.*}}, {{.*}}, {{.*}}, i8* getelementptr inbounds ([19 x i8], [19 x i8]* @{{.*}}, i64 0, i64 0), i8* {{(nonnull )?}}[[BUFFER]], i32 12) + // CHECK-32: tail call swiftcc void @"${{.*}}_os_log_impl_test{{.*}}"({{.*}}, {{.*}}, {{.*}}, {{.*}}, i8* getelementptr inbounds ([19 x i8], [19 x i8]* @{{.*}}, i32 0, i32 0), i8* {{(nonnull )?}}[[BUFFER]], i32 8) + // CHECK-NEXT: [[BITCASTED_STR_STORAGE:%.*]] = bitcast i8* [[STR_STORAGE]] to %swift.opaque* + // CHECK-NEXT: tail call void @swift_arrayDestroy(%swift.opaque* [[BITCASTED_STR_STORAGE]] + // CHECK-NEXT: tail call void @swift_slowDealloc(i8* {{(nonnull )?}}[[STR_STORAGE]] + // CHECK-NEXT: tail call void @swift_slowDealloc(i8* {{(nonnull )?}}[[BUFFER]] + // CHECK: call void @llvm.lifetime.end{{.*}}({{.*}}, i8* {{(nonnull )?}}[[STR_STORAGE_PTR]] + // CHECK-NEXT: br label %[[EXIT]] + + // CHECK: [[EXIT]]: + // CHECK-NEXT: ret void +} diff --git a/test/SILOptimizer/OSLogMandatoryOptTest.swift b/test/SILOptimizer/OSLogMandatoryOptTest.swift index e5b2c9da9a946..8371053f21412 100644 --- a/test/SILOptimizer/OSLogMandatoryOptTest.swift +++ b/test/SILOptimizer/OSLogMandatoryOptTest.swift @@ -45,16 +45,16 @@ func testSimpleInterpolation() { // the array which is checked by a different test suite. // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF - // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: try_apply [[FOREACH]], inout Optional>, inout Optional>) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] // We need to wade through some borrows and copy values here. // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[FINARR:%[0-9]+]] // CHECK-DAG: [[FINARRFUNC:%[0-9]+]] = function_ref @$ss27_finalizeUninitializedArrayySayxGABnlF - // CHECK-DAG: [[FINARR]] = apply [[FINARRFUNC]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARGSARRAY:%[0-9]+]]) + // CHECK-DAG: [[FINARR]] = apply [[FINARRFUNC]]<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARGSARRAY:%[0-9]+]]) // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] - // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 3 } @@ -92,15 +92,15 @@ func testInterpolationWithFormatOptions() { // the array which is checked by a different test suite. // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF - // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: try_apply [[FOREACH]], inout Optional>, inout Optional>) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[FINARR:%[0-9]+]] // CHECK-DAG: [[FINARRFUNC:%[0-9]+]] = function_ref @$ss27_finalizeUninitializedArrayySayxGABnlF - // CHECK-DAG: [[FINARR]] = apply [[FINARRFUNC]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARGSARRAY:%[0-9]+]]) + // CHECK-DAG: [[FINARR]] = apply [[FINARRFUNC]]<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARGSARRAY:%[0-9]+]]) // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] - // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 3 } @@ -140,15 +140,15 @@ func testInterpolationWithFormatOptionsAndPrivacy() { // the array which is checked by a different test suite. // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF - // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: try_apply [[FOREACH]], inout Optional>, inout Optional>) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[FINARR:%[0-9]+]] // CHECK-DAG: [[FINARRFUNC:%[0-9]+]] = function_ref @$ss27_finalizeUninitializedArrayySayxGABnlF - // CHECK-DAG: [[FINARR]] = apply [[FINARRFUNC]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARGSARRAY:%[0-9]+]]) + // CHECK-DAG: [[FINARR]] = apply [[FINARRFUNC]]<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARGSARRAY:%[0-9]+]]) // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] - // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 3 } @@ -194,15 +194,15 @@ func testInterpolationWithMultipleArguments() { // the array which is checked by a different test suite. // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF - // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: try_apply [[FOREACH]], inout Optional>, inout Optional>) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[FINARR:%[0-9]+]] // CHECK-DAG: [[FINARRFUNC:%[0-9]+]] = function_ref @$ss27_finalizeUninitializedArrayySayxGABnlF - // CHECK-DAG: [[FINARR]] = apply [[FINARRFUNC]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARGSARRAY:%[0-9]+]]) + // CHECK-DAG: [[FINARR]] = apply [[FINARRFUNC]]<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARGSARRAY:%[0-9]+]]) // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] - // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 9 } @@ -244,13 +244,13 @@ func testLogMessageWithoutData() { // Check whether argument array is folded. // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF - // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: try_apply [[FOREACH]], inout Optional>, inout Optional>) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[ARGSARRAY:%[0-9]+]] // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] - // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 0 } @@ -317,15 +317,15 @@ func testMessageWithTooManyArguments() { // Check whether argument array is folded. // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF - // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: try_apply [[FOREACH]], inout Optional>, inout Optional>) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[FINARR:%[0-9]+]] // CHECK-DAG: [[FINARRFUNC:%[0-9]+]] = function_ref @$ss27_finalizeUninitializedArrayySayxGABnlF - // CHECK-DAG: [[FINARR]] = apply [[FINARRFUNC]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARGSARRAY:%[0-9]+]]) + // CHECK-DAG: [[FINARR]] = apply [[FINARRFUNC]]<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARGSARRAY:%[0-9]+]]) // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] - // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 144 } @@ -402,15 +402,15 @@ func testDynamicStringArguments() { // the array which is checked by a different test suite. // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF - // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: try_apply [[FOREACH]], inout Optional>, inout Optional>) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[FINARR:%[0-9]+]] // CHECK-DAG: [[FINARRFUNC:%[0-9]+]] = function_ref @$ss27_finalizeUninitializedArrayySayxGABnlF - // CHECK-DAG: [[FINARR]] = apply [[FINARRFUNC]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARGSARRAY:%[0-9]+]]) + // CHECK-DAG: [[FINARR]] = apply [[FINARRFUNC]]<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARGSARRAY:%[0-9]+]]) // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] - // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 6 } @@ -455,15 +455,15 @@ func testNSObjectInterpolation() { // the array which is checked by a different test suite. // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF - // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: try_apply [[FOREACH]], inout Optional>, inout Optional>) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[FINARR:%[0-9]+]] // CHECK-DAG: [[FINARRFUNC:%[0-9]+]] = function_ref @$ss27_finalizeUninitializedArrayySayxGABnlF - // CHECK-DAG: [[FINARR]] = apply {{.*}}<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARGSARRAY:%[0-9]+]]) + // CHECK-DAG: [[FINARR]] = apply {{.*}}<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARGSARRAY:%[0-9]+]]) // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] - // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 6 } @@ -503,15 +503,15 @@ func testDoubleInterpolation() { // not checked here, but is checked by a different test suite. // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF - // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: try_apply [[FOREACH]], inout Optional>, inout Optional>) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[FINARR:%[0-9]+]] // CHECK-DAG: [[FINARRFUNC:%[0-9]+]] = function_ref @$ss27_finalizeUninitializedArrayySayxGABnlF - // CHECK-DAG: [[FINARR]] = apply [[FINARRFUNC]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARGSARRAY:%[0-9]+]]) + // CHECK-DAG: [[FINARR]] = apply [[FINARRFUNC]]<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARGSARRAY:%[0-9]+]]) // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] - // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Optional>, inout Optional>) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 3 } diff --git a/test/Sema/diag_constantness_check_os_log.swift b/test/Sema/diag_constantness_check_os_log.swift index 4caa166692b65..aba696db441bb 100644 --- a/test/Sema/diag_constantness_check_os_log.swift +++ b/test/Sema/diag_constantness_check_os_log.swift @@ -27,7 +27,7 @@ func testNonconstantFormatOption( func testNonconstantPrivacyOption(privacyOpt: OSLogPrivacy) { _osLogTestHelper("Integer: \(Int.max, privacy: privacyOpt)") - // expected-error@-1 {{argument must be a case of enum 'OSLogPrivacy'}} + // expected-error@-1 {{argument must be a static method or property of 'OSLogPrivacy'}} } func testNonconstantAlignmentOption(alignOpt: OSLogStringAlignment) { @@ -44,7 +44,7 @@ func testMultipleOptions( \(2, format: formatOpt, align: .right(columns: 10), privacy: privacyOpt) """) // expected-error@-2 {{argument must be a static method or property of 'OSLogIntegerFormatting'}} - // expected-error@-3 {{argument must be a case of enum 'OSLogPrivacy'}} + // expected-error@-3 {{argument must be a static method or property of 'OSLogPrivacy'}} } func testNoninlinedOSLogMessage() { diff --git a/test/stdlib/OSLogExecutionTest.swift b/test/stdlib/OSLogExecutionTest.swift index 5e1eda880ac7c..b415579f6eaee 100644 --- a/test/stdlib/OSLogExecutionTest.swift +++ b/test/stdlib/OSLogExecutionTest.swift @@ -77,7 +77,7 @@ internal struct OSLogBufferChecker { /// This occupies four most significant bits of the first byte of the /// argument header. internal enum ArgumentType: UInt8 { - case scalar = 0, count, string, pointer, object + case scalar = 0, count = 1, string = 2, pointer = 3, object = 4, mask = 7 // TODO: include wide string and errno here if needed. } @@ -142,13 +142,13 @@ internal struct OSLogBufferChecker { /// Check whether the bytes starting from `startIndex` contain the encoding for a count. internal func checkCount( startIndex: Int, - flag: ArgumentFlag, - expectedCount: Int + expectedCount: Int, + precision: Bool ) { checkNumeric( startIndex: startIndex, - flag: flag, - type: .count, + flag: .autoFlag, + type: precision ? .count : .scalar, expectedValue: CInt(expectedCount)) } @@ -614,8 +614,8 @@ InterpolationTestSuite.test("Integer formatting with precision") { bufferChecker.checkArguments( { bufferChecker.checkCount( startIndex: $0, - flag: .autoFlag, - expectedCount: 10) + expectedCount: 10, + precision: true) }, { bufferChecker.checkInt( startIndex: $0, @@ -654,13 +654,13 @@ InterpolationTestSuite.test("Integer formatting with precision and alignment") { bufferChecker.checkArguments( { bufferChecker.checkCount( startIndex: $0, - flag: .autoFlag, - expectedCount: 7) + expectedCount: 7, + precision: false) }, { bufferChecker.checkCount( startIndex: $0, - flag: .autoFlag, - expectedCount: 10) + expectedCount: 10, + precision: true) }, { bufferChecker.checkInt( startIndex: $0, From 6173d7ec04eaa76fa4c3ef8f1193ce8fdeec7c28 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 1 Oct 2020 13:01:12 -0700 Subject: [PATCH 157/745] [Docs] Convert TypeChecker.rst to Markdown --- docs/{TypeChecker.rst => TypeChecker.md} | 212 +++++++++++------------ 1 file changed, 97 insertions(+), 115 deletions(-) rename docs/{TypeChecker.rst => TypeChecker.md} (92%) diff --git a/docs/TypeChecker.rst b/docs/TypeChecker.md similarity index 92% rename from docs/TypeChecker.rst rename to docs/TypeChecker.md index a526e1bffd5a0..dfc0927ec1f4d 100644 --- a/docs/TypeChecker.rst +++ b/docs/TypeChecker.md @@ -1,8 +1,7 @@ -Type Checker Design and Implementation -======================================== +# Type Checker Design and Implementation -Purpose ------------------ + +## Purpose This document describes the design and implementation of the Swift type checker. It is intended for developers who wish to modify, @@ -10,29 +9,28 @@ extend, or improve on the type checker, or simply to understand in greater depth how the Swift type system works. Familiarity with the Swift programming language is assumed. -Approach -------------------- +## Approach The Swift language and its type system incorporate a number of popular language features, including object-oriented programming via classes, function and operator overloading, subtyping, and constrained parametric polymorphism. Swift makes extensive use of type inference, allowing one to omit the types of many variables and expressions. For -example:: - +example: +```swift func round(_ x: Double) -> Int { /* ... */ } var pi: Double = 3.14159 var three = round(pi) // 'three' has type 'Int' func identity(_ x: T) -> T { return x } var eFloat: Float = -identity(2.71828) // numeric literal gets type 'Float' - +``` Swift's type inference allows type information to flow in two directions. As in most mainstream languages, type information can flow -from the leaves of the expression tree (e.g., the expression 'pi', +from the leaves of the expression tree (e.g., the expression `pi`, which refers to a double) up to the root (the type of the variable -'three'). However, Swift also allows type information to flow from the -context (e.g., the fixed type of the variable 'eFloat') at the root of +`three`). However, Swift also allows type information to flow from the +context (e.g., the fixed type of the variable `eFloat`) at the root of the expression tree down to the leaves (the type of the numeric literal 2.71828). This bi-directional type inference is common in languages that use ML-like type systems, but is not present in @@ -57,20 +55,20 @@ and vastly better diagnostics when the problem is limited in scope. Type checking proceeds in three main stages: -`Constraint Generation`_ +[Constraint Generation](#Constraint-Generation) Given an input expression and (possibly) additional contextual information, generate a set of type constraints that describes the relationships among the types of the various subexpressions. The generated constraints may contain unknown types, represented by type variables, which will be determined by the solver. -`Constraint Solving`_ +[Constraint Solving](#Constraint-Generation) Solve the system of constraints by assigning concrete types to each of the type variables in the constraint system. The constraint solver should provide the most specific solution possible among different alternatives. -`Solution Application`_ +[Solution Application](#Solution-Application) Given the input expression, the set of type constraints generated from that expression, and the set of assignments of concrete types to each of the type variables, produce a well-typed expression that @@ -80,8 +78,8 @@ Type checking proceeds in three main stages: The following sections describe these three stages of type checking, as well as matters of performance and diagnostics. -Constraints ----------------- +## Constraints + A constraint system consists of a set of type constraints. Each type constraint either places a requirement on a single type (e.g., it is an integer literal type) or relates two types (e.g., one is a subtype @@ -103,14 +101,14 @@ the Swift type system: ``T1`` get the same concrete type binding. There are two different flavors of equality constraints: - - Exact equality constraints, or "binding", written ``T0 := X`` - for some type variable ``T0`` and type ``X``, which requires - that ``T0`` be exactly identical to ``X``; - - Equality constraints, written ``X == Y`` for types ``X`` and - ``Y``, which require ``X`` and ``Y`` to have the same type, - ignoring lvalue types in the process. For example, the - constraint ``T0 == X`` would be satisfied by assigning ``T0`` - the type ``X`` and by assigning ``T0`` the type ``@lvalue X``. + - Exact equality constraints, or "binding", written ``T0 := X`` + for some type variable ``T0`` and type ``X``, which requires + that ``T0`` be exactly identical to ``X``; + - Equality constraints, written ``X == Y`` for types ``X`` and + ``Y``, which require ``X`` and ``Y`` to have the same type, + ignoring lvalue types in the process. For example, the + constraint ``T0 == X`` would be satisfied by assigning ``T0`` + the type ``X`` and by assigning ``T0`` the type ``@lvalue X``. **Subtyping** A subtype constraint requires the first type to be equivalent to or @@ -193,8 +191,8 @@ the Swift type system: A constraint that requires that the constrained type be DynamicLookup or an lvalue thereof. -Constraint Generation -`````````````````````````` +### Constraint Generation + The process of constraint generation produces a constraint system that relates the types of the various subexpressions within an expression. Programmatically, constraint generation walks an @@ -218,10 +216,10 @@ and types generated from the primary expression kinds are: When a name refers to a set of overloaded declarations, the selection of the appropriate declaration is handled by the - solver. This particular issue is discussed in the `Overloading`_ + solver. This particular issue is discussed in the [Overloading](#Overloading) section. Additionally, when the name refers to a generic function or a generic type, the declaration reference may introduce new type - variables; see the `Polymorphic Types`_ section for more information. + variables; see the [Polymorphic Types](#Polymorphic-Types) section for more information. **Member reference** A member reference expression ``a.b`` is assigned the type ``T0`` @@ -233,7 +231,7 @@ and types generated from the primary expression kinds are: Note that resolution of the member constraint can refer to a set of overloaded declarations; this is described further in the - `Overloading`_ section. + [Overloading](#Overloading) section. **Unresolved member reference** An unresolved member reference ``.name`` refers to a member of a @@ -249,7 +247,7 @@ and types generated from the primary expression kinds are: Note that the constraint system above actually has insufficient information to determine the type ``T0`` without additional - contextual information. The `Overloading`_ section describes how the + contextual information. The [Overloading](#Overloading) section describes how the overload-selection mechanism is used to resolve this problem. **Function application** @@ -327,8 +325,7 @@ and types generated from the primary expression kinds are: There are a number of other expression kinds within the language; see the constraint generator for their mapping to constraints. -Overloading -'''''''''''''''''''''''''' +#### Overloading Overloading is the process of giving multiple, different definitions to the same name. For example, we might overload a ``negate`` function @@ -351,10 +348,12 @@ in which each term binds that type variable (via an exact equality constraint) to the type produced by one of the overloads in the overload set. In our negate example, the disjunction is ``T0 := (Int) -> Int or T0 := (Double) -> Double``. The constraint -solver, discussed in the later section on `Constraint Solving`_, +solver, discussed in the later section on [Constraint Solving](#Constraint-Solving), explores both possible bindings, and the overloaded reference resolves to whichever binding results in a solution that satisfies all -constraints [#]_. +constraints. It is possible that both overloads will result in a solution, +in which case the solutions will be ranked based on the rules +discussed in the section [Comparing Solutions](#Comparing-Solutions). Overloading can be introduced both by expressions that refer to sets of overloaded declarations and by member constraints that end up @@ -383,8 +382,7 @@ latter case, the actual arguments are parsed as part of the unresolved member reference, so that a function application constraint describes their conversion to the input type ``T2``. -Polymorphic Types -'''''''''''''''''''''''''''''''''''''''''''''' +#### Polymorphic Types The Swift language includes generics, a system of constrained parameter polymorphism that enables polymorphic types and @@ -443,8 +441,8 @@ unbound generic type, i.e., one that does not have specified generic arguments, cannot be used except where the generic arguments can be inferred. -Constraint Solving ------------------------------ +### Constraint Solving + The primary purpose of the constraint solver is to take a given set of constraints and determine the most specific type binding for each of the type variables in the constraint system. As part of this determination, the @@ -465,8 +463,8 @@ going forward. This section will focus on the basic ideas behind the design of the solver, as well as the type rules that it applies. -Simplification -``````````````````` +#### Simplification + The constraint generation process introduces a number of constraints that can be immediately solved, either directly (because the solution is obvious and trivial) or by breaking the constraint down into a @@ -489,8 +487,7 @@ type properties, conjunctions, and disjunctions. Only the first three kinds of constraints have interesting simplification rules, and are discussed in the following sections. -Relational Constraints -'''''''''''''''''''''''''''''''''''''''''''''''' +##### Relational Constraints Relational constraints describe a relationship between two types. This category covers the equality, subtyping, and conversion constraints, @@ -507,20 +504,19 @@ constraint into two smaller constraints ``C < A`` and ``B < D`` by applying the conversion rule for function types. Similarly, one can destroy all of the various type constructors---tuple types, generic type specializations, lvalue types, etc.---to produce simpler -requirements, based on the type rules of the language [#]_. +requirements, based on the type rules of the language [1]. Relational constraints involving a type variable on one or both sides generally cannot be solved directly. Rather, these constraints inform the solving process later by providing possible type bindings, -described in the `Type Variable Bindings`_ section. The exception is +described in the [Type Variable Bindings](#Type-Variable-Bindings) section. The exception is an equality constraint between two type variables, e.g., ``T0 == T1``. These constraints are simplified by unifying the equivalence classes of ``T0`` and ``T1`` (using a basic union-find algorithm), such that the solver need only determine a binding for one of the type variables (and the other gets the same binding). -Member Constraints -''''''''''''''''''''''''''''''''''''''''''' +##### Member Constraints Member constraints specify that a certain type has a member of a given name and provide a binding for the type of that member. A member @@ -544,8 +540,8 @@ value, and ``C`` is the type of a reference to that entity. For a reference to a type, ``C`` will be a metatype of the declared type. -Strategies -``````````````````````````````` +#### Strategies + The basic approach to constraint solving is to simplify the constraints until they can no longer be simplified, then produce (and check) educated guesses about which declaration from an overload set @@ -567,8 +563,8 @@ away) or represent sets of assumptions that do not lead to a solution. The following sections describe the techniques used by the solver to produce derived constraint systems that explore the solution space. -Overload Selection -''''''''''''''''''''''''''''''''''''''''''''''''''''' +##### Overload Selection + Overload selection is the simplest way to make an assumption. For an overload set that introduced a disjunction constraint ``T0 := A1 or T0 := A2 or ... or T0 := AN`` into the constraint @@ -576,8 +572,8 @@ system, each term in the disjunction will be visited separately. Each solver state binds the type variable ``T0`` and explores whether the selected overload leads to a suitable solution. -Type Variable Bindings -''''''''''''''''''''''''''''''''''''''''''''''''''''' +##### Type Variable Bindings + A second way in which the solver makes assumptions is to guess at the concrete type to which a given type variable should be bound. That type binding is then introduced in a new, derived constraint system to @@ -588,8 +584,8 @@ does it perform an exhaustive search. Rather, it uses the constraints placed on that type variable to produce potential candidate types. There are several strategies employed by the solver. -Meets and Joins -.......................................... +###### Meets and Joins + A given type variable ``T0`` often has relational constraints placed on it that relate it to concrete types, e.g., ``T0 String { /* ... */ } @@ -732,7 +726,7 @@ checking problem:: var x : X f(10.5, x) - +``` This constraint system generates the constraints "``T(f)`` ==Fn ``T0 -> T1``" (for fresh variables ``T0`` and ``T1``), "``(T2, X) apply argument -> tuple element #1 -> conversion member - +``` When the solver selects a particular overload from the overload set, it records the selected overload based on the locator of the overload set. When it comes time to perform constraint application, the locator @@ -806,8 +800,8 @@ choices made in each solution. Naturally, all of these operations require locators to be unique, which occurs in the constraint system itself. -Simplifying Locators -''''''''''''''''''''''''''''' +##### Simplifying Locators + Locators provide the derivation of location information that follows the path of the solver, and can be used to query and recover the important decisions made by the solver. However, the locators @@ -816,9 +810,9 @@ AST node for the purposes of identifying the corresponding source location. For example, the failed constraint "``Int`` conforms to ``ExpressibleByFloatLiteral``" can most specifically by centered on the floating-point literal ``10.5``, but its locator is:: - +``` function application -> apply argument -> tuple element #0 - +``` The process of locator simplification maps a locator to its most specific AST node. Essentially, it starts at the anchor of the locator (in this case, the application ``f(10.5, x)``) and then walks @@ -833,17 +827,17 @@ Simplification does not always exhaust the complete path. For example, consider a slight modification to our example, so that the argument to ``f`` is provided by another call, we get a different result entirely:: - +``` func f(_ i : Int, s : String) { } func g() -> (f : Float, x : X) { } f(g()) - +``` Here, the failing constraint is ``Float apply argument -> tuple element #0 - +``` When we simplify this locator, we start with ``f(g())``. The "apply argument" derivation step takes us to the argument expression ``g()``. Here, however, there is no subexpression for the first tuple @@ -853,8 +847,8 @@ simplified locator:: function application of g -> tuple element #0 -Performance ------------------ +### Performance + The performance of the type checker is dependent on a number of factors, but the chief concerns are the size of the solution space (which is exponential in the worst case) and the effectiveness of the @@ -862,8 +856,8 @@ solver in exploring that solution space. This section describes some of the techniques used to improve solver performance, many of which can doubtless be improved. -Constraint Graph -```````````````` +#### Constraint Graph + The constraint graph describes the relationships among type variables in the constraint system. Each vertex in the constraint graph corresponds to a single type variable. The edges of the graph @@ -885,8 +879,8 @@ the connected components are then combined into a complete solution. Additionally, the constraint graph is used to direct simplification, described below. -Simplification Worklist -``````````````````````` +#### Simplification Worklist + When the solver has attempted a type variable binding, that binding often leads to additional simplifications in the constraint system. The solver will query the constraint graph to determine which @@ -898,8 +892,8 @@ is exhausted, simplification has completed. The use of the worklist eliminates the need to reprocess constraints that could not have changed because the type variables they mention have not changed. -Solver Scopes -````````````` +#### Solver Scopes + The solver proceeds through the solution space in a depth-first manner. Whenever the solver is about to make a guess---such as a speculative type variable binding or the selection of a term from a @@ -920,8 +914,8 @@ solver scope stores a marker to the current top of the stack, and popping that solver scope reverses all of the operations on that stack until it hits the marker. -Online Scoring -`````````````` +#### Online Scoring + As the solver evaluates potential solutions, it keeps track of the score of the current solution and of the best complete solution found thus far. If the score of the current solution is ever greater than @@ -935,8 +929,8 @@ literal types over other literal types, and prefer cheaper conversions to more expensive conversions. However, some of the rules are fairly ad hoc, and could benefit from more study. -Arena Memory Management -``````````````````````` +#### Arena Memory Management + Each constraint system introduces its own memory allocation arena, making allocations cheap and deallocation essentially free. The allocation arena extends all the way into the AST context, so that @@ -945,36 +939,24 @@ allocated within the constraint system's arena rather than the permanent arena. Most data structures involved in constraint solving use this same arena. -Diagnostics ------------------ -The diagnostics produced by the type checker are currently -terrible. We plan to do something about this, eventually. We also -believe that we can implement some heroics, such as spell-checking -that takes into account the surrounding expression to only provide -well-typed suggestions. +## Diagnostics + +Swift 5.2 introduced a new diagnostic framework, which is described +in detail in this +[blog post](https://swift.org/blog/new-diagnostic-arch-overview/). -.. [#] It is possible that both overloads will result in a solution, - in which case the solutions will be ranked based on the rules - discussed in the section `Comparing Solutions`_. +## Footnotes -.. [#] As of the time of this writing, the type rules of Swift have +[1]: As of the time of this writing, the type rules of Swift have not specifically been documented outside of the source code. The constraints-based type checker contains a function ``matchTypes`` that documents and implements each of these rules. A future revision of this document will provide a more readily-accessible version. -.. [#] More accurately, as of this writing, "will compute". The solver +[2]: More accurately, as of this writing, "will compute". The solver doesn't current compute meets and joins properly. Rather, it arbitrarily picks one of the constraints "below" to start with. -.. [#] Again, as of this writing, the solver doesn't actually compute +[3]: Again, as of this writing, the solver doesn't actually compute meets and joins, so the solver continues until it runs out of - supertypes to enumerate. - -New Diagnostic Architecture ---------------------------- - -We are currently working on porting type-check based diagnostics over -to the new diagnostic framework, which is described in detail in this -`blog post -`_. + supertypes to enumerate. \ No newline at end of file From 91fd19d6db8f7b80ea0d562bd20436119021e117 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 1 Oct 2020 13:05:32 -0700 Subject: [PATCH 158/745] [Docs] Correct a few code blocks in TypeChecker.md --- docs/TypeChecker.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/TypeChecker.md b/docs/TypeChecker.md index dfc0927ec1f4d..e01d00d170cf2 100644 --- a/docs/TypeChecker.md +++ b/docs/TypeChecker.md @@ -367,12 +367,12 @@ issue noted in the prior section is that this constraint does not give the solver enough information to determine ``T0`` without guesswork. However, we note that the type of an enum member actually has a regular structure. For example, consider the ``Optional`` type:: - +```swift enum Optional { case none case some(T) } - +``` The type of ``Optional.none`` is ``Optional``, while the type of ``Optional.some`` is ``(T) -> Optional``. In fact, the type of an enum element can have one of two forms: it can be ``T0``, @@ -388,12 +388,12 @@ The Swift language includes generics, a system of constrained parameter polymorphism that enables polymorphic types and functions. For example, one can implement a ``min`` function as, e.g.,:: - +```swift func min(x: T, y: T) -> T { if y < x { return y } return x } - +``` Here, ``T`` is a generic parameter that can be replaced with any concrete type, so long as that type conforms to the protocol ``Comparable``. The type of ``min`` is (internally) written as `` T -> T``. Uses of generic types are also immediately opened by the constraint solver. For example, consider the following generic dictionary type:: - +```swift class Dictionary { // ... } - +``` When the constraint solver encounters the expression ``Dictionary()``, it opens up the type ``Dictionary``---which has not been provided with any specific generic arguments---to the type @@ -496,9 +496,9 @@ relationship constraints proceeds by comparing the structure of the two types and applying the typing rules of the Swift language to generate additional constraints. For example, if the constraint is a conversion constraint:: - +``` A -> B D - +``` then both types are function types, and we can break down this constraint into two smaller constraints ``C < A`` and ``B < D`` by applying the conversion rule for function types. Similarly, one can @@ -715,7 +715,7 @@ well as during diagnostics emission, it is important to track the relationship between the constraints and the actual AST nodes from which they originally came. For example, consider the following type checking problem:: -``` +```swift struct X { // user-defined conversions func [conversion] __conversion () -> String { /* ... */ } @@ -827,7 +827,7 @@ Simplification does not always exhaust the complete path. For example, consider a slight modification to our example, so that the argument to ``f`` is provided by another call, we get a different result entirely:: -``` +```swift func f(_ i : Int, s : String) { } func g() -> (f : Float, x : X) { } @@ -844,9 +844,9 @@ argument" derivation step takes us to the argument expression element of ``g()``, because it's simple part of the tuple returned from ``g``. At this point, simplification ceases, and creates the simplified locator:: - +``` function application of g -> tuple element #0 - +``` ### Performance The performance of the type checker is dependent on a number of From 74765a8ba82a14d3fce03e156049f65845c2cbfa Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 1 Oct 2020 13:09:00 -0700 Subject: [PATCH 159/745] Remove Type Body Fingerprints Flags This infrastructure has more than proven itself. Drop the code paths and tests supporting the status quo. --- include/swift/Basic/LangOptions.h | 6 - include/swift/Driver/Compilation.h | 7 - .../Driver/FineGrainedDependencyDriverGraph.h | 9 +- include/swift/Option/Options.td | 8 - lib/AST/DeclContext.cpp | 2 +- lib/Driver/Compilation.cpp | 6 +- lib/Driver/Driver.cpp | 6 - .../FineGrainedDependencyDriverGraph.cpp | 6 - lib/Driver/ToolChains.cpp | 2 - lib/Frontend/CompilerInvocation.cpp | 5 - lib/IDE/CompletionInstance.cpp | 6 - lib/Parse/ParseDecl.cpp | 9 +- .../class-fingerprint.swift | 45 +- .../enum-fingerprint.swift | 45 +- .../extension-adds-member.swift | 47 - .../protocol-fingerprint.swift | 45 +- .../struct-fingerprint.swift | 45 +- ...erprints.swift => added_method-type.swift} | 4 +- test/InterfaceHash/added_method.swift | 23 - ...s_private_property-type-fingerprints.swift | 53 -- ...added_private_class_private_property.swift | 38 +- ...ate_class_property-type-fingerprints.swift | 53 -- .../added_private_class_property.swift | 38 +- ...m_private_property-type-fingerprints.swift | 56 -- .../added_private_enum_private_property.swift | 39 +- ...vate_enum_property-type-fingerprints.swift | 55 -- .../added_private_enum_property.swift | 38 +- ...ded_private_method-type-fingerprints.swift | 55 -- test/InterfaceHash/added_private_method.swift | 38 +- ...method_value_types-type-fingerprints.swift | 88 -- .../added_private_method_value_types.swift | 53 +- ...te_protocol_method-type-fingerprints.swift | 50 - .../added_private_protocol_method.swift | 38 +- ..._protocol_property-type-fingerprints.swift | 50 - .../added_private_protocol_property.swift | 38 +- ...t_private_property-type-fingerprints.swift | 56 -- ...dded_private_struct_private_property.swift | 38 +- ...te_struct_property-type-fingerprints.swift | 56 -- .../added_private_struct_property.swift | 38 +- ...edited_method_body-type-fingerprints.swift | 31 - test/InterfaceHash/edited_method_body.swift | 18 +- ...ed_property_getter-type-fingerprints.swift | 33 - .../edited_property_getter.swift | 19 +- unittests/Driver/CMakeLists.txt | 1 - .../FineGrainedDependencyGraphTests.cpp | 242 +++-- ...peBodyFingerprintsDependencyGraphTests.cpp | 894 ------------------ .../rdar23148987-type-fingerprints.swift | 61 -- .../Driver/Dependencies/rdar23148987.swift | 6 +- 48 files changed, 581 insertions(+), 2018 deletions(-) rename test/InterfaceHash/{added_method-type-fingerprints.swift => added_method-type.swift} (86%) delete mode 100644 test/InterfaceHash/added_method.swift delete mode 100644 test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift delete mode 100644 test/InterfaceHash/added_private_class_property-type-fingerprints.swift delete mode 100644 test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift delete mode 100644 test/InterfaceHash/added_private_enum_property-type-fingerprints.swift delete mode 100644 test/InterfaceHash/added_private_method-type-fingerprints.swift delete mode 100644 test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift delete mode 100644 test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift delete mode 100644 test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift delete mode 100644 test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift delete mode 100644 test/InterfaceHash/added_private_struct_property-type-fingerprints.swift delete mode 100644 test/InterfaceHash/edited_method_body-type-fingerprints.swift delete mode 100644 test/InterfaceHash/edited_property_getter-type-fingerprints.swift delete mode 100644 unittests/Driver/TypeBodyFingerprintsDependencyGraphTests.cpp delete mode 100644 validation-test/Driver/Dependencies/rdar23148987-type-fingerprints.swift diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 40d8c719a02d1..bd704cb29c4d9 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -330,12 +330,6 @@ namespace swift { /// of active clauses aren't hoisted into the enclosing scope. bool DisablePoundIfEvaluation = false; - /// Instead of hashing tokens inside of NominalType and ExtensionBodies into - /// the interface hash, hash them into per-iterable-decl-context - /// fingerprints. Fine-grained dependency types won't dirty every provides - /// in a file when the user adds a member to, e.g., a struct. - bool EnableTypeFingerprints = true; - /// When using fine-grained dependencies, emit dot files for every swiftdeps /// file. bool EmitFineGrainedDependencySourcefileDotFiles = false; diff --git a/include/swift/Driver/Compilation.h b/include/swift/Driver/Compilation.h index d970a9cf834cd..5e6e1972f9dfd 100644 --- a/include/swift/Driver/Compilation.h +++ b/include/swift/Driver/Compilation.h @@ -265,9 +265,6 @@ class Compilation { const bool OnlyOneDependencyFile; private: - /// Is the parser recording token hashes for each type body? - const bool EnableTypeFingerprints; - /// Helpful for debugging, but slows down the driver. So, only turn on when /// needed. const bool VerifyFineGrainedDependencyGraphAfterEveryImport; @@ -319,8 +316,6 @@ class Compilation { bool ShowDriverTimeCompilation = false, std::unique_ptr Stats = nullptr, bool OnlyOneDependencyFile = false, - bool EnableTypeFingerprints = - LangOptions().EnableTypeFingerprints, bool VerifyFineGrainedDependencyGraphAfterEveryImport = false, bool EmitFineGrainedDependencyDotFileAfterEveryImport = false, bool FineGrainedDependenciesIncludeIntrafileOnes = false, @@ -386,8 +381,6 @@ class Compilation { } void disableIncrementalBuild(Twine why); - bool getEnableTypeFingerprints() const { return EnableTypeFingerprints; } - bool getVerifyFineGrainedDependencyGraphAfterEveryImport() const { return VerifyFineGrainedDependencyGraphAfterEveryImport; } diff --git a/include/swift/Driver/FineGrainedDependencyDriverGraph.h b/include/swift/Driver/FineGrainedDependencyDriverGraph.h index ba461842bb36e..07029becc8be8 100644 --- a/include/swift/Driver/FineGrainedDependencyDriverGraph.h +++ b/include/swift/Driver/FineGrainedDependencyDriverGraph.h @@ -189,8 +189,6 @@ class ModuleDepGraph { const bool verifyFineGrainedDependencyGraphAfterEveryImport; const bool emitFineGrainedDependencyDotFileAfterEveryImport; - const bool EnableTypeFingerprints; - private: /// If tracing dependencies, holds a vector used to hold the current path /// def - use/def - use/def - ... @@ -296,14 +294,12 @@ class ModuleDepGraph { /// \p stats may be null ModuleDepGraph(const bool verifyFineGrainedDependencyGraphAfterEveryImport, const bool emitFineGrainedDependencyDotFileAfterEveryImport, - const bool EnableTypeFingerprints, const bool shouldTraceDependencies, UnifiedStatsReporter *stats) : verifyFineGrainedDependencyGraphAfterEveryImport( verifyFineGrainedDependencyGraphAfterEveryImport), emitFineGrainedDependencyDotFileAfterEveryImport( emitFineGrainedDependencyDotFileAfterEveryImport), - EnableTypeFingerprints(EnableTypeFingerprints), currentPathIfTracing( shouldTraceDependencies ? llvm::Optional>( @@ -314,11 +310,10 @@ class ModuleDepGraph { } /// For unit tests. - ModuleDepGraph(const bool EnableTypeFingerprints, - const bool EmitDotFilesForDebugging = false) + ModuleDepGraph(const bool EmitDotFilesForDebugging = false) : ModuleDepGraph( true, /*emitFineGrainedDependencyDotFileAfterEveryImport=*/ - EmitDotFilesForDebugging, EnableTypeFingerprints, false, nullptr) {} + EmitDotFilesForDebugging, false, nullptr) {} //============================================================================ // MARK: ModuleDepGraph - updating from a switdeps file diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index a1c2b34fc1f59..d60baf2396495 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -139,14 +139,6 @@ def driver_always_rebuild_dependents : Flag<["-"], "driver-always-rebuild-dependents">, InternalDebugOpt, HelpText<"Always rebuild dependents of files that have been modified">; -def enable_type_fingerprints : -Flag<["-"], "enable-type-fingerprints">, Flags<[FrontendOption, HelpHidden]>, -HelpText<"Enable per-nominal and extension body fingerprints">; - -def disable_type_fingerprints : -Flag<["-"], "disable-type-fingerprints">, Flags<[FrontendOption, HelpHidden]>, -HelpText<"Disable per-nominal and extension body fingerprints">; - def enable_only_one_dependency_file : Flag<["-"], "enable-only-one-dependency-file">, Flags<[DoesNotAffectIncrementalBuild]>, HelpText<"Enables incremental build optimization that only produces one dependencies file">; diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index 8aa916c6e128e..25224941d99af 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -933,7 +933,7 @@ bool IterableDeclContext::areTokensHashedForThisBodyInsteadOfInterfaceHash() // corresponding to the fingerprinted nominal dependency node. if (isa(this)) return false; - return getASTContext().LangOpts.EnableTypeFingerprints; + return true; } /// Return the DeclContext to compare when checking private access in diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index 1b1e57753b784..31f3d0ce4f1dd 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -121,7 +121,6 @@ Compilation::Compilation(DiagnosticEngine &Diags, bool ShowDriverTimeCompilation, std::unique_ptr StatsReporter, bool OnlyOneDependencyFile, - bool EnableTypeFingerprints, bool VerifyFineGrainedDependencyGraphAfterEveryImport, bool EmitFineGrainedDependencyDotFileAfterEveryImport, bool FineGrainedDependenciesIncludeIntrafileOnes, @@ -149,7 +148,6 @@ Compilation::Compilation(DiagnosticEngine &Diags, Stats(std::move(StatsReporter)), FilelistThreshold(FilelistThreshold), OnlyOneDependencyFile(OnlyOneDependencyFile), - EnableTypeFingerprints(EnableTypeFingerprints), VerifyFineGrainedDependencyGraphAfterEveryImport( VerifyFineGrainedDependencyGraphAfterEveryImport), EmitFineGrainedDependencyDotFileAfterEveryImport( @@ -829,12 +827,12 @@ namespace driver { FineGrainedDepGraph( Comp.getVerifyFineGrainedDependencyGraphAfterEveryImport(), Comp.getEmitFineGrainedDependencyDotFileAfterEveryImport(), - Comp.getEnableTypeFingerprints(), Comp.getTraceDependencies(), + Comp.getTraceDependencies(), Comp.getStatsReporter()), FineGrainedDepGraphForRanges( Comp.getVerifyFineGrainedDependencyGraphAfterEveryImport(), Comp.getEmitFineGrainedDependencyDotFileAfterEveryImport(), - Comp.getEnableTypeFingerprints(), Comp.getTraceDependencies(), + Comp.getTraceDependencies(), Comp.getStatsReporter()), TQ(std::move(TaskQueue)) {} diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 6394cb4f65807..783d297595fb6 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1014,11 +1014,6 @@ Driver::buildCompilation(const ToolChain &TC, ArgList->hasFlag(options::OPT_enable_only_one_dependency_file, options::OPT_disable_only_one_dependency_file, false); - const bool EnableTypeFingerprints = - ArgList->hasFlag(options::OPT_enable_type_fingerprints, - options::OPT_disable_type_fingerprints, - LangOptions().EnableTypeFingerprints); - const bool VerifyFineGrainedDependencyGraphAfterEveryImport = ArgList->hasArg( options:: OPT_driver_verify_fine_grained_dependency_graph_after_every_import); @@ -1050,7 +1045,6 @@ Driver::buildCompilation(const ToolChain &TC, ShowDriverTimeCompilation, std::move(StatsReporter), OnlyOneDependencyFile, - EnableTypeFingerprints, VerifyFineGrainedDependencyGraphAfterEveryImport, EmitFineGrainedDependencyDotFileAfterEveryImport, FineGrainedDependenciesIncludeIntrafileOnes, diff --git a/lib/Driver/FineGrainedDependencyDriverGraph.cpp b/lib/Driver/FineGrainedDependencyDriverGraph.cpp index dc2a9afcf929b..d5ab5a5228b57 100644 --- a/lib/Driver/FineGrainedDependencyDriverGraph.cpp +++ b/lib/Driver/FineGrainedDependencyDriverGraph.cpp @@ -369,12 +369,6 @@ ModuleDepGraph::integrateSourceFileDepGraphNode( const SourceFileDepGraph &g, const SourceFileDepGraphNode *integrand, const PreexistingNodeIfAny preexistingMatch, const StringRef swiftDepsOfJob) { - - if (!EnableTypeFingerprints && - integrand->getKey().getKind() != NodeKind::sourceFileProvide && - integrand->getFingerprint()) - return None; - if (!integrand->getIsProvides()) return NullablePtr(); // depends are captured by // recordWhatUseDependsUpon below diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 8d5d0b30d7ede..a03ea4fe52e9f 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -259,8 +259,6 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_O_Group); inputArgs.AddLastArg(arguments, options::OPT_RemoveRuntimeAsserts); inputArgs.AddLastArg(arguments, options::OPT_AssumeSingleThreaded); - inputArgs.AddLastArg(arguments, options::OPT_enable_type_fingerprints); - inputArgs.AddLastArg(arguments, options::OPT_disable_type_fingerprints); inputArgs.AddLastArg(arguments, options::OPT_fine_grained_dependency_include_intrafile); inputArgs.AddLastArg(arguments, diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index c4f1c698ac7bd..2c8f39248235c 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -438,11 +438,6 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.VerifySyntaxTree = true; } - Opts.EnableTypeFingerprints = - Args.hasFlag(options::OPT_enable_type_fingerprints, - options::OPT_disable_type_fingerprints, - LangOptions().EnableTypeFingerprints); - if (Args.hasArg(OPT_emit_fine_grained_dependency_sourcefile_dot_files)) Opts.EmitFineGrainedDependencySourcefileDotFiles = true; diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 9c4ba9940e0e1..eaad0b6499293 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -322,8 +322,6 @@ bool CompletionInstance::performCachedOperationIfPossible( LangOptions langOpts = CI.getASTContext().LangOpts; langOpts.DisableParserLookup = true; - // Ensure all non-function-body tokens are hashed into the interface hash - langOpts.EnableTypeFingerprints = false; TypeCheckerOptions typeckOpts = CI.getASTContext().TypeCheckerOpts; SearchPathOptions searchPathOpts = CI.getASTContext().SearchPathOpts; DiagnosticEngine tmpDiags(tmpSM); @@ -599,10 +597,6 @@ bool swift::ide::CompletionInstance::performOperation( // source text. That breaks an invariant of syntax tree building. Invocation.getLangOptions().BuildSyntaxTree = false; - // Since caching uses the interface hash, and since per type fingerprints - // weaken that hash, disable them here: - Invocation.getLangOptions().EnableTypeFingerprints = false; - // We don't need token list. Invocation.getLangOptions().CollectParsedToken = false; diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index fa9ace4441e27..d3076ba6a979f 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -4551,8 +4551,13 @@ Parser::parseDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, Diag<> ErrorDiag, // If we're hashing the type body separately, record the curly braces but // nothing inside for the interface hash. + // + // FIXME: There's no real reason code completion cannot also use this code + // path. But it seems to cause lazy parsing in contexts that the current + // implementation does not expect. Optional>> MemberHashingScope; - if (IDC->areTokensHashedForThisBodyInsteadOfInterfaceHash()) { + if (IDC->areTokensHashedForThisBodyInsteadOfInterfaceHash() && + !L->isCodeCompletion()) { recordTokenHash("{"); recordTokenHash("}"); MemberHashingScope.emplace(CurrentTokenHash, llvm::MD5()); @@ -4587,7 +4592,7 @@ Parser::parseDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, Diag<> ErrorDiag, if (RBLoc.isInvalid()) hadError = true; - if (!Context.LangOpts.EnableTypeFingerprints) + if (L->isCodeCompletion()) return std::make_pair(decls, None); llvm::MD5::MD5Result result; diff --git a/test/Frontend/PrivateFingerprints/class-fingerprint.swift b/test/Frontend/PrivateFingerprints/class-fingerprint.swift index 67b29178057e4..e244cc3f123a2 100644 --- a/test/Frontend/PrivateFingerprints/class-fingerprint.swift +++ b/test/Frontend/PrivateFingerprints/class-fingerprint.swift @@ -1,48 +1,6 @@ - // Test per-type-body fingerprints for classes // -// ============================================================================= -// Without the fingerprints -// ============================================================================= - -// Establish status quo - - -// RUN: %empty-directory(%t) -// RUN: cp %S/Inputs/class-fingerprint/* %t -// RUN: cp %t/definesAB{-before,}.swift - -// Seeing weird failure on CI, so set the mod times -// RUN: touch -t 200101010101 %t/*.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output1 - -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB1.swiftdeps - -// Change one type, but uses of all types get recompiled - -// RUN: cp %t/definesAB{-after,}.swift - -// Seeing weird failure on CI, so ensure that definesAB.swift is newer -// RUN: touch -t 200201010101 %t/* -// RUN: touch -t 200101010101 %t/*.swift -// RUN: touch -t 200301010101 %t/definesAB.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output2 - -// Save for debugging: -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB1.swiftdeps - -// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output2 - -// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: definesAB.o <= definesAB.swift} -// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesA.o <= usesA.swift} - -// ============================================================================= -// With the fingerprints -// ============================================================================= - // Establish status quo // RUN: %empty-directory(%t) @@ -71,3 +29,6 @@ // only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB4.swiftdeps // RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output4 + +// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: definesAB.o <= definesAB.swift} +// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesA.o <= usesA.swift} diff --git a/test/Frontend/PrivateFingerprints/enum-fingerprint.swift b/test/Frontend/PrivateFingerprints/enum-fingerprint.swift index 138d16f211a88..a56b2443890a6 100644 --- a/test/Frontend/PrivateFingerprints/enum-fingerprint.swift +++ b/test/Frontend/PrivateFingerprints/enum-fingerprint.swift @@ -1,48 +1,6 @@ - // Test per-type-body fingerprints for enums // -// ============================================================================= -// Without the fingerprints -// ============================================================================= - -// Establish status quo - - -// RUN: %empty-directory(%t) -// RUN: cp %S/Inputs/enum-fingerprint/* %t -// RUN: cp %t/definesAB{-before,}.swift - -// Seeing weird failure on CI, so set the mod times -// RUN: touch -t 200101010101 %t/*.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output1 - -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB1.swiftdeps - -// Change one type, but uses of all types get recompiled - -// RUN: cp %t/definesAB{-after,}.swift - -// Seeing weird failure on CI, so ensure that definesAB.swift is newer -// RUN: touch -t 200201010101 %t/* -// RUN: touch -t 200101010101 %t/*.swift -// RUN: touch -t 200301010101 %t/definesAB.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output2 - -// Save for debugging: -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB1.swiftdeps - -// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output2 - -// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: definesAB.o <= definesAB.swift} -// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesA.o <= usesA.swift} - -// ============================================================================= -// With the fingerprints -// ============================================================================= - // Establish status quo // RUN: %empty-directory(%t) @@ -71,3 +29,6 @@ // only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB4.swiftdeps // RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output4 + +// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: definesAB.o <= definesAB.swift} +// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesA.o <= usesA.swift} diff --git a/test/Frontend/PrivateFingerprints/extension-adds-member.swift b/test/Frontend/PrivateFingerprints/extension-adds-member.swift index 63726480b3c37..73a8e650dd340 100644 --- a/test/Frontend/PrivateFingerprints/extension-adds-member.swift +++ b/test/Frontend/PrivateFingerprints/extension-adds-member.swift @@ -1,56 +1,9 @@ - // Test per-type-body fingerprints using simple extensions // // If the parser is allowed to use a body fingerprint for an extension // this test will fail because usesA.swift won't be recompiled for the // last step. - -// ============================================================================= -// Without the fingerprints -// ============================================================================= - -// Establish status quo - - -// RUN: %empty-directory(%t) -// RUN: cp %S/Inputs/extension-adds-member/* %t -// RUN: cp %t/definesAB{-before,}.swift - -// Seeing weird failure on CI, so set the mod times -// RUN: touch -t 200101010101 %t/*.swift - - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >& %t/output1 - - -// Change one type, but uses of all types get recompiled - -// RUN: cp %t/definesAB{-after,}.swift - -// Seeing weird failure on CI, so ensure that definesAB.swift is newer -// RUN: touch -t 200201010101 %t/* -// RUN: touch -t 200101010101 %t/*.swift -// RUN: touch -t 200301010101 %t/definesAB.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >& %t/output2 - - -// This test checks for the status quo; it would be OK to be more conservative - -// RUN: %FileCheck -check-prefix=CHECK-RECOMPILED-WO %s < %t/output2 -// CHECK-RECOMPILED-WO: {compile: definesAB.o <= definesAB.swift} -// CHECK-RECOMPILED-WO: {compile: usesA.o <= usesA.swift} -// CHECK-RECOMPILED-WO: {compile: usesB.o <= usesB.swift} - -// RUN: %FileCheck -check-prefix=CHECK-NOT-RECOMPILED-WO %s < %t/output2 -// CHECK-NOT-RECOMPILED-WO-NOT: {compile: main.o <= main.swift} - - -// ============================================================================= -// With the fingerprints -// ============================================================================= - // Establish status quo // RUN: %empty-directory(%t) diff --git a/test/Frontend/PrivateFingerprints/protocol-fingerprint.swift b/test/Frontend/PrivateFingerprints/protocol-fingerprint.swift index a48649f0b11ee..10c62c62ed6fb 100644 --- a/test/Frontend/PrivateFingerprints/protocol-fingerprint.swift +++ b/test/Frontend/PrivateFingerprints/protocol-fingerprint.swift @@ -1,48 +1,6 @@ - // Test per-type-body fingerprints: the protocol case. // -// ============================================================================= -// Without the fingerprints -// ============================================================================= - -// Establish status quo - - -// RUN: %empty-directory(%t) -// RUN: cp %S/Inputs/protocol-fingerprint/* %t -// RUN: cp %t/definesAB{-before,}.swift - -// Seeing weird failure on CI, so set the mod times -// RUN: touch -t 200101010101 %t/*.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output1 - -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB1.swiftdeps - -// Change one type, but uses of all types get recompiled - -// RUN: cp %t/definesAB{-after,}.swift - -// Seeing weird failure on CI, so ensure that definesAB.swift is newer -// RUN: touch -t 200201010101 %t/* -// RUN: touch -t 200101010101 %t/*.swift -// RUN: touch -t 200301010101 %t/definesAB.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output2 - -// Save for debugging: -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB1.swiftdeps - -// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output2 - -// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: definesAB.o <= definesAB.swift} -// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesA.o <= usesA.swift} - -// ============================================================================= -// With the fingerprints -// ============================================================================= - // Establish status quo // RUN: %empty-directory(%t) @@ -71,3 +29,6 @@ // only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB4.swiftdeps // RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output4 + +// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: definesAB.o <= definesAB.swift} +// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesA.o <= usesA.swift} diff --git a/test/Frontend/PrivateFingerprints/struct-fingerprint.swift b/test/Frontend/PrivateFingerprints/struct-fingerprint.swift index a02cef5fd649e..1010e8fcc7eb2 100644 --- a/test/Frontend/PrivateFingerprints/struct-fingerprint.swift +++ b/test/Frontend/PrivateFingerprints/struct-fingerprint.swift @@ -1,48 +1,6 @@ - // Test per-type-body fingerprints for structs // -// ============================================================================= -// Without the fingerprints -// ============================================================================= - -// Establish status quo - - -// RUN: %empty-directory(%t) -// RUN: cp %S/Inputs/struct-fingerprint/* %t -// RUN: cp %t/definesAB{-before,}.swift - -// Seeing weird failure on CI, so set the mod times -// RUN: touch -t 200101010101 %t/*.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output1 - -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB1.swiftdeps - -// Change one type, but uses of all types get recompiled - -// RUN: cp %t/definesAB{-after,}.swift - -// Seeing weird failure on CI, so ensure that definesAB.swift is newer -// RUN: touch -t 200201010101 %t/* -// RUN: touch -t 200101010101 %t/*.swift -// RUN: touch -t 200301010101 %t/definesAB.swift - -// RUN: cd %t && %target-swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesAB.swift usesA.swift usesB.swift -module-name main -output-file-map ofm.json >&output2 - -// Save for debugging: -// only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB1.swiftdeps - -// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output2 - -// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: definesAB.o <= definesAB.swift} -// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesA.o <= usesA.swift} - -// ============================================================================= -// With the fingerprints -// ============================================================================= - // Establish status quo // RUN: %empty-directory(%t) @@ -71,3 +29,6 @@ // only-run-for-debugging: cp %t/usesB.swiftdeps %t/usesB4.swiftdeps // RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output4 + +// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: definesAB.o <= definesAB.swift} +// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesA.o <= usesA.swift} diff --git a/test/InterfaceHash/added_method-type-fingerprints.swift b/test/InterfaceHash/added_method-type.swift similarity index 86% rename from test/InterfaceHash/added_method-type-fingerprints.swift rename to test/InterfaceHash/added_method-type.swift index 6776e086a7559..0f7eb261f21b3 100644 --- a/test/InterfaceHash/added_method-type-fingerprints.swift +++ b/test/InterfaceHash/added_method-type.swift @@ -8,10 +8,10 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s // RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main // RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps // RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main // RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps // RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs diff --git a/test/InterfaceHash/added_method.swift b/test/InterfaceHash/added_method.swift deleted file mode 100644 index 28dd75f2d4329..0000000000000 --- a/test/InterfaceHash/added_method.swift +++ /dev/null @@ -1,23 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash - -// BEGIN a.swift -class C { - func f2() -> Int { - return 0 - } -} - -// BEGIN b.swift -class C { - func f2() -> Int { - return 0 - } - - func f3() -> Int { - return 1 - } -} diff --git a/test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift b/test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift deleted file mode 100644 index 5ba9627ce2731..0000000000000 --- a/test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift +++ /dev/null @@ -1,53 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -private class C { - func f2() -> Int { - return 0 - } -} - -// BEGIN b.swift -private class C { - func f2() -> Int { - return 0 - } - - private var x: Int = 0 -} - -// Since C is a type or extension, the interface hash ought to not get the -// changed token hash. - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_class_private_property.swift b/test/InterfaceHash/added_private_class_private_property.swift index e3ca285b2cb10..dfa6721eface6 100644 --- a/test/InterfaceHash/added_private_class_private_property.swift +++ b/test/InterfaceHash/added_private_class_private_property.swift @@ -1,8 +1,17 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift private class C { @@ -19,3 +28,26 @@ private class C { private var x: Int = 0 } + +// Since C is a type or extension, the interface hash ought to not get the +// changed token hash. + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_class_property-type-fingerprints.swift b/test/InterfaceHash/added_private_class_property-type-fingerprints.swift deleted file mode 100644 index d087848dd323f..0000000000000 --- a/test/InterfaceHash/added_private_class_property-type-fingerprints.swift +++ /dev/null @@ -1,53 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -class C { - func f2() -> Int { - return 0 - } -} - -// BEGIN b.swift -class C { - func f2() -> Int { - return 0 - } - - private var x: Int = 0 -} - -// Since C is a type or extension, the interface hash ought to not get the -// changed token hash. - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_class_property.swift b/test/InterfaceHash/added_private_class_property.swift index cdeb03d8944a7..1734f85565ef4 100644 --- a/test/InterfaceHash/added_private_class_property.swift +++ b/test/InterfaceHash/added_private_class_property.swift @@ -1,8 +1,17 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift class C { @@ -19,3 +28,26 @@ class C { private var x: Int = 0 } + +// Since C is a type or extension, the interface hash ought to not get the +// changed token hash. + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift b/test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift deleted file mode 100644 index ed898ffdce3d1..0000000000000 --- a/test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift +++ /dev/null @@ -1,56 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - - -// BEGIN a.swift -private enum A { - case x, y - func f2() -> Int { - return 0 - } -} - -// BEGIN b.swift -private enum A { - case x, y - func f2() -> Int { - return 0 - } - - var foo: Int { return 0 } -} - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_enum_private_property.swift b/test/InterfaceHash/added_private_enum_private_property.swift index a35d20b2928d0..078b78eef37ca 100644 --- a/test/InterfaceHash/added_private_enum_private_property.swift +++ b/test/InterfaceHash/added_private_enum_private_property.swift @@ -1,8 +1,21 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + // BEGIN a.swift private enum A { @@ -21,3 +34,23 @@ private enum A { var foo: Int { return 0 } } + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_enum_property-type-fingerprints.swift b/test/InterfaceHash/added_private_enum_property-type-fingerprints.swift deleted file mode 100644 index 0589feefd6214..0000000000000 --- a/test/InterfaceHash/added_private_enum_property-type-fingerprints.swift +++ /dev/null @@ -1,55 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -enum A { - case x, y - func f2() -> Int { - return 0 - } -} - -// BEGIN b.swift -enum A { - case x, y - func f2() -> Int { - return 0 - } - - private var foo: Int { return 0 } -} - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_enum_property.swift b/test/InterfaceHash/added_private_enum_property.swift index 370272d91d251..5993da28a924c 100644 --- a/test/InterfaceHash/added_private_enum_property.swift +++ b/test/InterfaceHash/added_private_enum_property.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift enum A { @@ -21,3 +33,23 @@ enum A { private var foo: Int { return 0 } } + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_method-type-fingerprints.swift b/test/InterfaceHash/added_private_method-type-fingerprints.swift deleted file mode 100644 index f6b8236e94b64..0000000000000 --- a/test/InterfaceHash/added_private_method-type-fingerprints.swift +++ /dev/null @@ -1,55 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -class C { - func f2() -> Int { - return 0 - } -} - -// BEGIN b.swift -class C { - func f2() -> Int { - return 0 - } - - private func f3() -> Int { - return 1 - } -} - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_method.swift b/test/InterfaceHash/added_private_method.swift index a07bed690cf16..77dcb2b0d2f68 100644 --- a/test/InterfaceHash/added_private_method.swift +++ b/test/InterfaceHash/added_private_method.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift class C { @@ -21,3 +33,23 @@ class C { return 1 } } + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift b/test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift deleted file mode 100644 index 8499fbd0ea835..0000000000000 --- a/test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift +++ /dev/null @@ -1,88 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -struct A { - func f2() -> Int { - return 0 - } -} - -enum B { - case x, y - func f2() -> Int { - return 0 - } -} - -// BEGIN b.swift -struct A { - func f2() -> Int { - return 0 - } - - private func f3() -> Int { - return 1 - } -} - -enum B { - case x, y - func f2() -> Int { - return 0 - } - - private func f3() -> Int { - return 1 - } -} - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' B true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' B true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' B true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' B true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1B{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1B{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1B{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1B{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1B{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1B{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1B{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1B{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_method_value_types.swift b/test/InterfaceHash/added_private_method_value_types.swift index 29ff2314d0c1e..e685f9da9afcd 100644 --- a/test/InterfaceHash/added_private_method_value_types.swift +++ b/test/InterfaceHash/added_private_method_value_types.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift struct A { @@ -39,3 +51,38 @@ enum B { return 1 } } + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' B true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' B true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' B true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' B true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1B{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1B{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift b/test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift deleted file mode 100644 index f029cdb8b803d..0000000000000 --- a/test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift +++ /dev/null @@ -1,50 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -private protocol P { - func f2() -> Int - var y: Int { get set } -} - -// BEGIN b.swift -private protocol P { - func f2() -> Int - func f3() -> Int - var y: Int { get set } -} - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' P true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' P true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' P true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' P true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1P{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1P{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_protocol_method.swift b/test/InterfaceHash/added_private_protocol_method.swift index 965c82b67af79..d7a03ef2dedd1 100644 --- a/test/InterfaceHash/added_private_protocol_method.swift +++ b/test/InterfaceHash/added_private_protocol_method.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift private protocol P { @@ -16,3 +28,23 @@ private protocol P { func f3() -> Int var y: Int { get set } } + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' P true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1P{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1P{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift b/test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift deleted file mode 100644 index 4a51a0df7957b..0000000000000 --- a/test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift +++ /dev/null @@ -1,50 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -private protocol P { - func f2() -> Int - var y: Int { get set } -} - -// BEGIN b.swift -private protocol P { - func f2() -> Int - var x: Int { get set } - var y: Int { get set } -} - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' P true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' P true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' P true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' P true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1P{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1P{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1P{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_protocol_property.swift b/test/InterfaceHash/added_private_protocol_property.swift index f47353d2d6521..ce76fffde3ff9 100644 --- a/test/InterfaceHash/added_private_protocol_property.swift +++ b/test/InterfaceHash/added_private_protocol_property.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift private protocol P { @@ -16,3 +28,23 @@ private protocol P { var x: Int { get set } var y: Int { get set } } + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' P true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1P{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1P{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift b/test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift deleted file mode 100644 index b5d20cf3b317c..0000000000000 --- a/test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift +++ /dev/null @@ -1,56 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -struct S { - func f2() -> Int { - return 0 - } - - var y: Int = 0 -} - -// BEGIN b.swift -struct S { - func f2() -> Int { - return 0 - } - - private var x: Int = 0 - var y: Int = 0 -} - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' S true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' S true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' S true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' S true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1S{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1S{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_struct_private_property.swift b/test/InterfaceHash/added_private_struct_private_property.swift index 7bba778652d58..94efb4498aa36 100644 --- a/test/InterfaceHash/added_private_struct_private_property.swift +++ b/test/InterfaceHash/added_private_struct_private_property.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift struct S { @@ -22,3 +34,23 @@ struct S { private var x: Int = 0 var y: Int = 0 } + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' S true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1S{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1S{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_struct_property-type-fingerprints.swift b/test/InterfaceHash/added_private_struct_property-type-fingerprints.swift deleted file mode 100644 index f27418b9036ff..0000000000000 --- a/test/InterfaceHash/added_private_struct_property-type-fingerprints.swift +++ /dev/null @@ -1,56 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs - -// BEGIN a.swift -private struct S { - func f2() -> Int { - return 0 - } - - var y: Int = 0 -} - -// BEGIN b.swift -private struct S { - func f2() -> Int { - return 0 - } - - var x: Int = 0 - var y: Int = 0 -} - -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH -// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT - -// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' S true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' S true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' S true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' S true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1S{{[^ ]+}} '' true - -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1S{{[^ ]+}} '' true -// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1S{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_struct_property.swift b/test/InterfaceHash/added_private_struct_property.swift index 49e58b356b684..82611277f8d1c 100644 --- a/test/InterfaceHash/added_private_struct_property.swift +++ b/test/InterfaceHash/added_private_struct_property.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: not cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs // BEGIN a.swift private struct S { @@ -22,3 +34,23 @@ private struct S { var x: Int = 0 var y: Int = 0 } + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' S true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1S{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1S{{[^ ]+}} '' true diff --git a/test/InterfaceHash/edited_method_body-type-fingerprints.swift b/test/InterfaceHash/edited_method_body-type-fingerprints.swift deleted file mode 100644 index 726a36d8ba931..0000000000000 --- a/test/InterfaceHash/edited_method_body-type-fingerprints.swift +++ /dev/null @@ -1,31 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: cmp %t/{a,b}-processed.swiftdeps - -// BEGIN a.swift -class C { - func f2() -> Int { - return 0 - } -} - -// BEGIN b.swift -class C { - func f2() -> Int { - return 1 - } -} diff --git a/test/InterfaceHash/edited_method_body.swift b/test/InterfaceHash/edited_method_body.swift index b0aebce44e500..016a835646d1a 100644 --- a/test/InterfaceHash/edited_method_body.swift +++ b/test/InterfaceHash/edited_method_body.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: cmp %t/{a,b}-processed.swiftdeps // BEGIN a.swift class C { diff --git a/test/InterfaceHash/edited_property_getter-type-fingerprints.swift b/test/InterfaceHash/edited_property_getter-type-fingerprints.swift deleted file mode 100644 index 9024944bd865c..0000000000000 --- a/test/InterfaceHash/edited_property_getter-type-fingerprints.swift +++ /dev/null @@ -1,33 +0,0 @@ -// REQUIRES: shell -// Also uses awk: -// XFAIL OS=windows - -// When adding a private protocol method, the interface hash should stay the same -// The per-type fingerprint should change - -// RUN: %empty-directory(%t) -// RUN: %{python} %utils/split_file.py -o %t %s -// RUN: cp %t/{a,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps -// RUN: cp %t/{b,x}.swift -// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main -// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps - -// RUN: cmp %t/{a,b}-processed.swiftdeps - -// BEGIN a.swift -class C { - var p: Int { - return 0 - } -} - -// BEGIN b.swift -class C { - var p: Int { - let x = 1 - return x - } -} - diff --git a/test/InterfaceHash/edited_property_getter.swift b/test/InterfaceHash/edited_property_getter.swift index deaeb8ccc08dc..05d99baac6493 100644 --- a/test/InterfaceHash/edited_property_getter.swift +++ b/test/InterfaceHash/edited_property_getter.swift @@ -1,8 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash -// RUN: cmp %t/a.hash %t/b.hash +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh %swift-dependency-tool %t/x.swiftdeps %t/b-processed.swiftdeps + +// RUN: cmp %t/{a,b}-processed.swiftdeps // BEGIN a.swift class C { @@ -18,3 +30,4 @@ class C { return x } } + diff --git a/unittests/Driver/CMakeLists.txt b/unittests/Driver/CMakeLists.txt index f045bc7fd8e6e..e9323a95c3c43 100644 --- a/unittests/Driver/CMakeLists.txt +++ b/unittests/Driver/CMakeLists.txt @@ -1,7 +1,6 @@ add_swift_unittest(SwiftDriverTests FineGrainedDependencyGraphTests.cpp MockingFineGrainedDependencyGraphs.cpp - TypeBodyFingerprintsDependencyGraphTests.cpp UnitTestSourceFileDepGraphFactory.cpp ) diff --git a/unittests/Driver/FineGrainedDependencyGraphTests.cpp b/unittests/Driver/FineGrainedDependencyGraphTests.cpp index becc5dd213d86..c0f49be7047d9 100644 --- a/unittests/Driver/FineGrainedDependencyGraphTests.cpp +++ b/unittests/Driver/FineGrainedDependencyGraphTests.cpp @@ -1,14 +1,33 @@ +//===--------------- FineGrainedDependencyGraphTests.cpp ------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + #include "MockingFineGrainedDependencyGraphs.h" #include "swift/Basic/ReferenceDependencyKeys.h" #include "swift/Driver/FineGrainedDependencyDriverGraph.h" #include "swift/Driver/Job.h" #include "gtest/gtest.h" -// A version of \c ModuleDepGraphTests.cpp that tests things with -// type-body fingerprints disabled. -// -// In order to get the test macros to work right, it seems that the tests -// must be copied-and-pasted, sigh. +// \c findJobsToRecompileWhenWholeJobChanges, +// \c findExternallyDependentUntracedJobs, and \c simulateReload +// may include jobs in their result that +// would be excluded in the coarse-grained graph. But since these will be jobs +// that have already been scheduled, downstream mechanisms will filter them out. + +// \c \c findExternallyDependentUntracedJobs may also return duplicates + +// To debug a test, create the \c ModuleDepGraph and pass true as the second +// argument to the constructor, then find the dot files in the directory +// where the tests run, +// and inspect them with, e.g. OmniGraffle. using namespace swift; using namespace fine_grained_dependencies; @@ -38,8 +57,8 @@ static bool contains(const Range &range, const T &value) { std::end(range); } -TEST(ModuleDepGraphWithoutFingerprints, BasicLoad) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, BasicLoad) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a->", "b->"}}}); simulateLoad(graph, &job1, {{NodeKind::nominal, {"c->", "d->"}}}); @@ -57,8 +76,8 @@ TEST(ModuleDepGraphWithoutFingerprints, BasicLoad) { {NodeKind::topLevel, {"b", "c", "d->", "a->"}}}); } -TEST(ModuleDepGraphWithoutFingerprints, IndependentNodes) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, IndependentNodes) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a0", "a->"}}}); simulateLoad(graph, &job1, {{NodeKind::topLevel, {"b0", "b->"}}}); @@ -86,8 +105,8 @@ TEST(ModuleDepGraphWithoutFingerprints, IndependentNodes) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); } -TEST(ModuleDepGraphWithoutFingerprints, IndependentDepKinds) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, IndependentDepKinds) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::nominal, {"a", "a->"}}}); simulateLoad(graph, &job1, {{NodeKind::topLevel, {"a", "b->"}}}); @@ -97,8 +116,8 @@ TEST(ModuleDepGraphWithoutFingerprints, IndependentDepKinds) { EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); } -TEST(ModuleDepGraphWithoutFingerprints, IndependentDepKinds2) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, IndependentDepKinds2) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::nominal, {"a->", "b"}}}); simulateLoad(graph, &job1, {{NodeKind::topLevel, {"b->", "a"}}}); @@ -108,8 +127,8 @@ TEST(ModuleDepGraphWithoutFingerprints, IndependentDepKinds2) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); } -TEST(ModuleDepGraphWithoutFingerprints, IndependentMembers) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, IndependentMembers) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::member, {"a,aa"}}}); simulateLoad(graph, &job1, {{NodeKind::member, {"a,bb->"}}}); @@ -125,8 +144,8 @@ TEST(ModuleDepGraphWithoutFingerprints, IndependentMembers) { EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job4)); } -TEST(ModuleDepGraphWithoutFingerprints, SimpleDependent) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, SimpleDependent) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a", "b", "c"}}}); simulateLoad(graph, &job1, {{NodeKind::topLevel, {"x->", "b->", "z->"}}}); @@ -143,8 +162,8 @@ TEST(ModuleDepGraphWithoutFingerprints, SimpleDependent) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); } -TEST(ModuleDepGraphWithoutFingerprints, SimpleDependentReverse) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, SimpleDependentReverse) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a->", "b->", "c->"}}}); simulateLoad(graph, &job1, {{NodeKind::topLevel, {"x", "b", "z"}}}); @@ -166,8 +185,8 @@ TEST(ModuleDepGraphWithoutFingerprints, SimpleDependentReverse) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); } -TEST(ModuleDepGraphWithoutFingerprints, SimpleDependent2) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, SimpleDependent2) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::nominal, {"a", "b", "c"}}}); simulateLoad(graph, &job1, {{NodeKind::nominal, {"x->", "b->", "z->"}}}); @@ -185,8 +204,8 @@ TEST(ModuleDepGraphWithoutFingerprints, SimpleDependent2) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); } -TEST(ModuleDepGraphWithoutFingerprints, SimpleDependent3) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, SimpleDependent3) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::nominal, {"a"}}, {NodeKind::topLevel, {"a"}}}); @@ -205,8 +224,8 @@ TEST(ModuleDepGraphWithoutFingerprints, SimpleDependent3) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); } -TEST(ModuleDepGraphWithoutFingerprints, SimpleDependent4) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, SimpleDependent4) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::nominal, {"a"}}}); simulateLoad(graph, &job1, @@ -225,8 +244,8 @@ TEST(ModuleDepGraphWithoutFingerprints, SimpleDependent4) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); } -TEST(ModuleDepGraphWithoutFingerprints, SimpleDependent5) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, SimpleDependent5) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::nominal, {"a"}}, {NodeKind::topLevel, {"a"}}}); @@ -247,8 +266,8 @@ TEST(ModuleDepGraphWithoutFingerprints, SimpleDependent5) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); } -TEST(ModuleDepGraphWithoutFingerprints, SimpleDependent6) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, SimpleDependent6) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::dynamicLookup, {"a", "b", "c"}}}); simulateLoad(graph, &job1, @@ -266,8 +285,8 @@ TEST(ModuleDepGraphWithoutFingerprints, SimpleDependent6) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); } -TEST(ModuleDepGraphWithoutFingerprints, SimpleDependentMember) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, SimpleDependentMember) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::member, {"a,aa", "b,bb", "c,cc"}}}); simulateLoad(graph, &job1, @@ -286,8 +305,8 @@ TEST(ModuleDepGraphWithoutFingerprints, SimpleDependentMember) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); } -TEST(ModuleDepGraphWithoutFingerprints, MultipleDependentsSame) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, MultipleDependentsSame) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::nominal, {"a", "b", "c"}}}); simulateLoad(graph, &job1, {{NodeKind::nominal, {"x->", "b->", "z->"}}}); @@ -309,8 +328,8 @@ TEST(ModuleDepGraphWithoutFingerprints, MultipleDependentsSame) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); } -TEST(ModuleDepGraphWithoutFingerprints, MultipleDependentsDifferent) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, MultipleDependentsDifferent) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::nominal, {"a", "b", "c"}}}); simulateLoad(graph, &job1, {{NodeKind::nominal, {"x->", "b->", "z->"}}}); @@ -332,8 +351,8 @@ TEST(ModuleDepGraphWithoutFingerprints, MultipleDependentsDifferent) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); } -TEST(ModuleDepGraphWithoutFingerprints, ChainedDependents) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, ChainedDependents) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::nominal, {"a", "b", "c"}}}); simulateLoad(graph, &job1, {{NodeKind::nominal, {"x->", "b->", "z"}}}); @@ -355,8 +374,8 @@ TEST(ModuleDepGraphWithoutFingerprints, ChainedDependents) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); } -TEST(ModuleDepGraphWithoutFingerprints, ChainedNoncascadingDependents) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, ChainedNoncascadingDependents) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::nominal, {"a", "b", "c"}}}); simulateLoad(graph, &job1, {{NodeKind::nominal, {"x->", "b->", "#z"}}}); @@ -378,8 +397,8 @@ TEST(ModuleDepGraphWithoutFingerprints, ChainedNoncascadingDependents) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); } -TEST(ModuleDepGraphWithoutFingerprints, ChainedNoncascadingDependents2) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, ChainedNoncascadingDependents2) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a", "b", "c"}}}); simulateLoad( @@ -397,8 +416,8 @@ TEST(ModuleDepGraphWithoutFingerprints, ChainedNoncascadingDependents2) { EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); } -TEST(ModuleDepGraphWithoutFingerprints, MarkTwoNodes) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, MarkTwoNodes) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a", "b"}}}); simulateLoad(graph, &job1, {{NodeKind::topLevel, {"a->", "z"}}}); @@ -435,8 +454,8 @@ TEST(ModuleDepGraphWithoutFingerprints, MarkTwoNodes) { EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job12)); } -TEST(ModuleDepGraphWithoutFingerprints, MarkOneNodeTwice) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, MarkOneNodeTwice) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::nominal, {"a"}}}); simulateLoad(graph, &job1, {{NodeKind::nominal, {"a->"}}}); @@ -461,8 +480,8 @@ TEST(ModuleDepGraphWithoutFingerprints, MarkOneNodeTwice) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); } -TEST(ModuleDepGraphWithoutFingerprints, MarkOneNodeTwice2) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, MarkOneNodeTwice2) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::nominal, {"a"}}}); simulateLoad(graph, &job1, {{NodeKind::nominal, {"a->"}}}); @@ -487,8 +506,8 @@ TEST(ModuleDepGraphWithoutFingerprints, MarkOneNodeTwice2) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); } -TEST(ModuleDepGraphWithoutFingerprints, ReloadDetectsChange) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, ReloadDetectsChange) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::nominal, {"a"}}}); simulateLoad(graph, &job1, {{NodeKind::nominal, {"a->"}}}); @@ -514,8 +533,8 @@ TEST(ModuleDepGraphWithoutFingerprints, ReloadDetectsChange) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); } -TEST(ModuleDepGraphWithoutFingerprints, NotTransitiveOnceMarked) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, NotTransitiveOnceMarked) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::nominal, {"a"}}}); simulateLoad(graph, &job1, {{NodeKind::nominal, {"a->"}}}); @@ -542,8 +561,8 @@ TEST(ModuleDepGraphWithoutFingerprints, NotTransitiveOnceMarked) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); } -TEST(ModuleDepGraphWithoutFingerprints, DependencyLoops) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, DependencyLoops) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a", "b", "c", "a->"}}}); simulateLoad(graph, &job1, @@ -569,8 +588,8 @@ TEST(ModuleDepGraphWithoutFingerprints, DependencyLoops) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); } -TEST(ModuleDepGraphWithoutFingerprints, MarkIntransitive) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, MarkIntransitive) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a", "b", "c"}}}); simulateLoad(graph, &job1, {{NodeKind::topLevel, {"x->", "b->", "z->"}}}); @@ -587,8 +606,8 @@ TEST(ModuleDepGraphWithoutFingerprints, MarkIntransitive) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); } -TEST(ModuleDepGraphWithoutFingerprints, MarkIntransitiveTwice) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, MarkIntransitiveTwice) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a", "b", "c"}}}); simulateLoad(graph, &job1, {{NodeKind::topLevel, {"x->", "b->", "z->"}}}); @@ -597,8 +616,8 @@ TEST(ModuleDepGraphWithoutFingerprints, MarkIntransitiveTwice) { EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); } -TEST(ModuleDepGraphWithoutFingerprints, MarkIntransitiveThenIndirect) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, MarkIntransitiveThenIndirect) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a", "b", "c"}}}); simulateLoad(graph, &job1, {{NodeKind::topLevel, {"x->", "b->", "z->"}}}); @@ -616,8 +635,8 @@ TEST(ModuleDepGraphWithoutFingerprints, MarkIntransitiveThenIndirect) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); } -TEST(ModuleDepGraphWithoutFingerprints, SimpleExternal) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, SimpleExternal) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::externalDepend, {"/foo->", "/bar->"}}}); @@ -637,8 +656,8 @@ TEST(ModuleDepGraphWithoutFingerprints, SimpleExternal) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); } -TEST(ModuleDepGraphWithoutFingerprints, SimpleExternal2) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, SimpleExternal2) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::externalDepend, {"/foo->", "/bar->"}}}); @@ -650,8 +669,8 @@ TEST(ModuleDepGraphWithoutFingerprints, SimpleExternal2) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); } -TEST(ModuleDepGraphWithoutFingerprints, ChainedExternal) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, ChainedExternal) { + ModuleDepGraph graph; simulateLoad( graph, &job0, @@ -680,8 +699,8 @@ TEST(ModuleDepGraphWithoutFingerprints, ChainedExternal) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); } -TEST(ModuleDepGraphWithoutFingerprints, ChainedExternalReverse) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, ChainedExternalReverse) { + ModuleDepGraph graph; simulateLoad( graph, &job0, @@ -711,8 +730,8 @@ TEST(ModuleDepGraphWithoutFingerprints, ChainedExternalReverse) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); } -TEST(ModuleDepGraphWithoutFingerprints, ChainedExternalPreMarked) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, ChainedExternalPreMarked) { + ModuleDepGraph graph; simulateLoad( graph, &job0, @@ -731,8 +750,8 @@ TEST(ModuleDepGraphWithoutFingerprints, ChainedExternalPreMarked) { EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); } -TEST(ModuleDepGraphWithoutFingerprints, MutualInterfaceHash) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, MutualInterfaceHash) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a", "b->"}}}); simulateLoad(graph, &job1, {{NodeKind::topLevel, {"a->", "b"}}}); @@ -740,8 +759,8 @@ TEST(ModuleDepGraphWithoutFingerprints, MutualInterfaceHash) { EXPECT_TRUE(contains(jobs, &job1)); } -TEST(ModuleDepGraphWithoutFingerprints, DisabledTypeBodyFingerprints) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, EnabledTypeBodyFingerprints) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::nominal, {"B2->"}}}); simulateLoad(graph, &job1, {{NodeKind::nominal, {"B1", "B2"}}}); @@ -756,8 +775,8 @@ TEST(ModuleDepGraphWithoutFingerprints, DisabledTypeBodyFingerprints) { } } -TEST(ModuleDepGraphWithoutFingerprints, BaselineForPrintsAndCrossType) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, BaselineForPrintsAndCrossType) { + ModuleDepGraph graph; // Because when A1 changes, B1 and not B2 is affected, only jobs1 and job2 // should be recompiled, except type fingerprints is off! @@ -778,16 +797,20 @@ TEST(ModuleDepGraphWithoutFingerprints, BaselineForPrintsAndCrossType) { } } -TEST(ModuleDepGraphWithoutFingerprints, LoadFailsWithFingerprint) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); - EXPECT_FALSE( +TEST(ModuleDepGraph, LoadPassesWithFingerprint) { + ModuleDepGraph graph; + EXPECT_TRUE( getChangesForSimulatedLoad(graph, &job0, {{NodeKind::nominal, {"A@1"}}})); } -TEST(ModuleDepGraphWithoutFingerprints, IgnoreFingerprints) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, UseFingerprints) { + ModuleDepGraph graph; - simulateLoad(graph, &job0, {{NodeKind::nominal, {"A1", "A2"}}}); + // Because when A1 changes, B1 and not B2 is affected, only jobs1 and job2 + // should be recompiled, except type fingerprints is off! + // Include a dependency on A1, to ensure it does not muck things up. + + simulateLoad(graph, &job0, {{NodeKind::nominal, {"A1@1", "A2@2", "A1->"}}}); simulateLoad(graph, &job1, {{NodeKind::nominal, {"B1", "A1->"}}}); simulateLoad(graph, &job2, {{NodeKind::nominal, {"C1", "A2->"}}}); simulateLoad(graph, &job3, {{NodeKind::nominal, {"D1"}}}); @@ -795,16 +818,16 @@ TEST(ModuleDepGraphWithoutFingerprints, IgnoreFingerprints) { { const auto jobs = simulateReload(graph, &job0, {{NodeKind::nominal, {"A1@11", "A2@2"}}}); - EXPECT_EQ(4u, jobs.size()); + EXPECT_EQ(3u, jobs.size()); EXPECT_TRUE(contains(jobs, &job0)); EXPECT_TRUE(contains(jobs, &job1)); EXPECT_TRUE(contains(jobs, &job2)); - EXPECT_TRUE(contains(jobs, &job3)); + EXPECT_FALSE(contains(jobs, &job3)); } } -TEST(ModuleDepGraphWithoutFingerprints, CrossTypeDependencyBaseline) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, CrossTypeDependencyBaseline) { + ModuleDepGraph graph; simulateLoad(graph, &job0, {{NodeKind::nominal, {"A"}}}); simulateLoad(graph, &job1, {{NodeKind::nominal, {"B", "C", "A->"}}}); simulateLoad(graph, &job2, {{NodeKind::nominal, {"B->"}}}); @@ -817,8 +840,8 @@ TEST(ModuleDepGraphWithoutFingerprints, CrossTypeDependencyBaseline) { EXPECT_TRUE(contains(jobs, &job3)); } -TEST(ModuleDepGraphWithoutFingerprints, CrossTypeDependency) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/false); +TEST(ModuleDepGraph, CrossTypeDependency) { + ModuleDepGraph graph; // Because of the cross-type dependency, A->B, // when A changes, only B is dirtied in job1. @@ -833,3 +856,48 @@ TEST(ModuleDepGraphWithoutFingerprints, CrossTypeDependency) { EXPECT_TRUE(contains(jobs, &job2)); EXPECT_FALSE(contains(jobs, &job3)); } + +TEST(ModuleDepGraph, CrossTypeDependencyBaselineWithFingerprints) { + ModuleDepGraph graph; + simulateLoad(graph, &job0, {{NodeKind::nominal, {"A1@1", "A2@2"}}}); + simulateLoad(graph, &job1, {{NodeKind::nominal, {"B1", "C1", "A1->"}}}); + simulateLoad(graph, &job2, {{NodeKind::nominal, {"B1->"}}}); + simulateLoad(graph, &job3, {{NodeKind::nominal, {"C1->"}}}); + simulateLoad(graph, &job4, {{NodeKind::nominal, {"B2", "C2", "A2->"}}}); + simulateLoad(graph, &job5, {{NodeKind::nominal, {"B2->"}}}); + simulateLoad(graph, &job6, {{NodeKind::nominal, {"C2->"}}}); + + const auto jobs = + simulateReload(graph, &job0, {{NodeKind::nominal, {"A1@11", "A2@2"}}}); + EXPECT_TRUE(contains(jobs, &job0)); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + EXPECT_TRUE(contains(jobs, &job3)); + EXPECT_FALSE(contains(jobs, &job4)); + EXPECT_FALSE(contains(jobs, &job5)); + EXPECT_FALSE(contains(jobs, &job6)); +} + +TEST(ModuleDepGraph, CrossTypeDependencyWithFingerprints) { + ModuleDepGraph graph; + // Because of the cross-type dependency, A->B, + // when A changes, only B is dirtied in job1. + + simulateLoad(graph, &job0, {{NodeKind::nominal, {"A1@1", "A2@2"}}}); + simulateLoad(graph, &job1, {{NodeKind::nominal, {"B1", "C1", "A1->B1"}}}); + simulateLoad(graph, &job2, {{NodeKind::nominal, {"B1->"}}}); + simulateLoad(graph, &job3, {{NodeKind::nominal, {"C1->"}}}); + simulateLoad(graph, &job4, {{NodeKind::nominal, {"B2", "C2", "A2->B2"}}}); + simulateLoad(graph, &job5, {{NodeKind::nominal, {"B2->"}}}); + simulateLoad(graph, &job6, {{NodeKind::nominal, {"C2->"}}}); + + const auto jobs = + simulateReload(graph, &job0, {{NodeKind::nominal, {"A1@11", "A2@2"}}}); + EXPECT_TRUE(contains(jobs, &job0)); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + EXPECT_FALSE(contains(jobs, &job3)); + EXPECT_FALSE(contains(jobs, &job4)); + EXPECT_FALSE(contains(jobs, &job5)); + EXPECT_FALSE(contains(jobs, &job6)); +} diff --git a/unittests/Driver/TypeBodyFingerprintsDependencyGraphTests.cpp b/unittests/Driver/TypeBodyFingerprintsDependencyGraphTests.cpp deleted file mode 100644 index 9e572b0c5fca4..0000000000000 --- a/unittests/Driver/TypeBodyFingerprintsDependencyGraphTests.cpp +++ /dev/null @@ -1,894 +0,0 @@ -#include "MockingFineGrainedDependencyGraphs.h" -#include "swift/Basic/ReferenceDependencyKeys.h" -#include "swift/Driver/FineGrainedDependencyDriverGraph.h" -#include "swift/Driver/Job.h" -#include "gtest/gtest.h" - -// This file adapts the unit tests from the older, coarse-grained, dependency -// graph to the new fine-grained graph. - -// \c findJobsToRecompileWhenWholeJobChanges, -// \c findExternallyDependentUntracedJobs, and \c simulateReload -// may include jobs in their result that -// would be excluded in the coarse-grained graph. But since these will be jobs -// that have already been scheduled, downstream mechanisms will filter them out. - -// \c \c findExternallyDependentUntracedJobs may also return duplicates - -// To debug a test, create the \c ModuleDepGraph and pass true as the second -// argument to the constructor, then find the dot files in the directory -// where the tests run, -// and inspect them with, e.g. OmniGraffle. - -using namespace swift; -using namespace fine_grained_dependencies; -using namespace mocking_fine_grained_dependency_graphs; -using Job = driver::Job; - -static OutputFileMap OFM; - -static Job - job0(OFM, "0"), - job1(OFM, "1"), - job2(OFM, "2"), - job3(OFM, "3"), - job4(OFM, "4"), - job5(OFM, "5"), - job6(OFM, "6"), - job7(OFM, "7"), - job8(OFM, "8"), - job9(OFM, "9"), - job10(OFM, "10"), - job11(OFM, "11"), - job12(OFM, "12"); - -template -static bool contains(const Range &range, const T &value) { - return std::find(std::begin(range), std::end(range), value) != - std::end(range); -} - -TEST(ModuleDepGraph, BasicLoad) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a->", "b->"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"c->", "d->"}}}); - simulateLoad(graph, &job2, {{NodeKind::topLevel, {"e", "f"}}}); - simulateLoad(graph, &job3, {{NodeKind::nominal, {"g", "h"}}}); - simulateLoad(graph, &job4, {{NodeKind::dynamicLookup, {"i", "j"}}}); - simulateLoad(graph, &job5, {{NodeKind::dynamicLookup, {"k->", "l->"}}}); - simulateLoad(graph, &job6, {{NodeKind::member, {"m,mm", "n,nn"}}}); - simulateLoad(graph, &job7, {{NodeKind::member, {"o,oo->", "p,pp->"}}}); - simulateLoad(graph, &job8, - {{NodeKind::externalDepend, {"/foo->", "/bar->"}}}); - - simulateLoad(graph, &job9, - {{NodeKind::nominal, {"a", "b", "c->", "d->"}}, - {NodeKind::topLevel, {"b", "c", "d->", "a->"}}}); -} - -TEST(ModuleDepGraph, IndependentNodes) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a0", "a->"}}}); - simulateLoad(graph, &job1, {{NodeKind::topLevel, {"b0", "b->"}}}); - simulateLoad(graph, &job2, {{NodeKind::topLevel, {"c0", "c->"}}}); - - EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); - - // Mark 0 again -- should be no change. - EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); - - EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job2).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); - - EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job1).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); -} - -TEST(ModuleDepGraph, IndependentDepKinds) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"a", "a->"}}}); - simulateLoad(graph, &job1, {{NodeKind::topLevel, {"a", "b->"}}}); - - EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); -} - -TEST(ModuleDepGraph, IndependentDepKinds2) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"a->", "b"}}}); - simulateLoad(graph, &job1, {{NodeKind::topLevel, {"b->", "a"}}}); - - EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job1).size()); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); -} - -TEST(ModuleDepGraph, IndependentMembers) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::member, {"a,aa"}}}); - simulateLoad(graph, &job1, {{NodeKind::member, {"a,bb->"}}}); - simulateLoad(graph, &job2, {{NodeKind::potentialMember, {"a"}}}); - simulateLoad(graph, &job3, {{NodeKind::member, {"b,aa->"}}}); - simulateLoad(graph, &job4, {{NodeKind::member, {"b,bb->"}}}); - - EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job3)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job4)); -} - -TEST(ModuleDepGraph, SimpleDependent) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a", "b", "c"}}}); - simulateLoad(graph, &job1, {{NodeKind::topLevel, {"x->", "b->", "z->"}}}); - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - - EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); -} - -TEST(ModuleDepGraph, SimpleDependentReverse) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a->", "b->", "c->"}}}); - simulateLoad(graph, &job1, {{NodeKind::topLevel, {"x", "b", "z"}}}); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job1); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job0)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - { - const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(1u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job0)); - } - EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); -} - -TEST(ModuleDepGraph, SimpleDependent2) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"a", "b", "c"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"x->", "b->", "z->"}}}); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - - EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); -} - -TEST(ModuleDepGraph, SimpleDependent3) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, - {{NodeKind::nominal, {"a"}}, {NodeKind::topLevel, {"a"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"a->"}}}); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - - EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); -} - -TEST(ModuleDepGraph, SimpleDependent4) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"a"}}}); - simulateLoad(graph, &job1, - {{NodeKind::nominal, {"a->"}}, {NodeKind::topLevel, {"a->"}}}); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - - EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); -} - -TEST(ModuleDepGraph, SimpleDependent5) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, - {{NodeKind::nominal, {"a"}}, {NodeKind::topLevel, {"a"}}}); - simulateLoad(graph, &job1, - {{NodeKind::nominal, {"a->"}}, {NodeKind::topLevel, {"a->"}}}); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); -} - -TEST(ModuleDepGraph, SimpleDependent6) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::dynamicLookup, {"a", "b", "c"}}}); - simulateLoad(graph, &job1, - {{NodeKind::dynamicLookup, {"x->", "b->", "z->"}}}); - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - - EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); -} - -TEST(ModuleDepGraph, SimpleDependentMember) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::member, {"a,aa", "b,bb", "c,cc"}}}); - simulateLoad(graph, &job1, - {{NodeKind::member, {"x,xx->", "b,bb->", "z,zz->"}}}); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - - EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); -} - -TEST(ModuleDepGraph, MultipleDependentsSame) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"a", "b", "c"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"x->", "b->", "z->"}}}); - simulateLoad(graph, &job2, {{NodeKind::nominal, {"q->", "b->", "s->"}}}); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(3u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - EXPECT_TRUE(contains(jobs, &job2)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); - - EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); -} - -TEST(ModuleDepGraph, MultipleDependentsDifferent) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"a", "b", "c"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"x->", "b->", "z->"}}}); - simulateLoad(graph, &job2, {{NodeKind::nominal, {"q->", "r->", "c->"}}}); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(3u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - EXPECT_TRUE(contains(jobs, &job2)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); - - EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); -} - -TEST(ModuleDepGraph, ChainedDependents) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"a", "b", "c"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"x->", "b->", "z"}}}); - simulateLoad(graph, &job2, {{NodeKind::nominal, {"z->"}}}); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(3u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - EXPECT_TRUE(contains(jobs, &job2)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); - - EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); -} - -TEST(ModuleDepGraph, ChainedNoncascadingDependents) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"a", "b", "c"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"x->", "b->", "#z"}}}); - simulateLoad(graph, &job2, {{NodeKind::nominal, {"#z->"}}}); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(3u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - EXPECT_TRUE(contains(jobs, &job2)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); - - EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); -} - -TEST(ModuleDepGraph, ChainedNoncascadingDependents2) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a", "b", "c"}}}); - simulateLoad( - graph, &job1, - {{NodeKind::topLevel, {"x->", "#b->"}}, {NodeKind::nominal, {"z"}}}); - simulateLoad(graph, &job2, {{NodeKind::nominal, {"z->"}}}); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); -} - -TEST(ModuleDepGraph, MarkTwoNodes) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a", "b"}}}); - simulateLoad(graph, &job1, {{NodeKind::topLevel, {"a->", "z"}}}); - simulateLoad(graph, &job2, {{NodeKind::topLevel, {"z->"}}}); - simulateLoad(graph, &job10, {{NodeKind::topLevel, {"y", "z", "q->"}}}); - simulateLoad(graph, &job11, {{NodeKind::topLevel, {"y->"}}}); - simulateLoad(graph, &job12, {{NodeKind::topLevel, {"q->", "q"}}}); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(3u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - EXPECT_TRUE(contains(jobs, &job2)); //????? - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job10)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job11)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job12)); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job10); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job10)); - EXPECT_TRUE(contains(jobs, &job11)); - EXPECT_FALSE(contains(jobs, &job2)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job10)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job11)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job12)); -} - -TEST(ModuleDepGraph, MarkOneNodeTwice) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"a"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"a->"}}}); - simulateLoad(graph, &job2, {{NodeKind::nominal, {"b->"}}}); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); - - { - auto jobs = simulateReload(graph, &job0, {{NodeKind::nominal, {"b"}}}); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job2)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); -} - -TEST(ModuleDepGraph, MarkOneNodeTwice2) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"a"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"a->"}}}); - simulateLoad(graph, &job2, {{NodeKind::nominal, {"b->"}}}); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); - - { - auto jobs = simulateReload(graph, &job0, {{NodeKind::nominal, {"a", "b"}}}); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job2)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); -} - -TEST(ModuleDepGraph, ReloadDetectsChange) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"a"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"a->"}}}); - simulateLoad(graph, &job2, {{NodeKind::nominal, {"b->"}}}); - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job1); - EXPECT_EQ(1u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); - - { - auto jobs = - simulateReload(graph, &job1, {{NodeKind::nominal, {"b", "a->"}}}); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - EXPECT_TRUE(contains(jobs, &job2)); - } - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); -} - -TEST(ModuleDepGraph, NotTransitiveOnceMarked) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"a"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"a->"}}}); - simulateLoad(graph, &job2, {{NodeKind::nominal, {"b->"}}}); - - { - const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job1); - EXPECT_EQ(1u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); - - { - const auto jobs = - simulateReload(graph, &job1, {{NodeKind::nominal, {"b", "a->"}}}); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - EXPECT_TRUE(contains(jobs, &job2)); - } - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); -} - -TEST(ModuleDepGraph, DependencyLoops) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a", "b", "c", "a->"}}}); - simulateLoad(graph, &job1, - {{NodeKind::topLevel, {"x", "x->", "b->", "z->"}}}); - simulateLoad(graph, &job2, {{NodeKind::topLevel, {"x->"}}}); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(3u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - EXPECT_TRUE(contains(jobs, &job2)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(0u, jobs.size()); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); -} - -TEST(ModuleDepGraph, MarkIntransitive) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a", "b", "c"}}}); - simulateLoad(graph, &job1, {{NodeKind::topLevel, {"x->", "b->", "z->"}}}); - - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); -} - -TEST(ModuleDepGraph, MarkIntransitiveTwice) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a", "b", "c"}}}); - simulateLoad(graph, &job1, {{NodeKind::topLevel, {"x->", "b->", "z->"}}}); - - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); -} - -TEST(ModuleDepGraph, MarkIntransitiveThenIndirect) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a", "b", "c"}}}); - simulateLoad(graph, &job1, {{NodeKind::topLevel, {"x->", "b->", "z->"}}}); - - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); - - { - auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job0)); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); -} - -TEST(ModuleDepGraph, SimpleExternal) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, - {{NodeKind::externalDepend, {"/foo->", "/bar->"}}}); - - EXPECT_TRUE(contains(graph.getExternalDependencies(), "/foo")); - EXPECT_TRUE(contains(graph.getExternalDependencies(), "/bar")); - - { - auto jobs = graph.findExternallyDependentUntracedJobs("/foo"); - EXPECT_EQ(jobs.size(), 1u); - EXPECT_TRUE(contains(jobs, &job0)); - } - - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - - EXPECT_EQ(0u, graph.findExternallyDependentUntracedJobs("/foo").size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); -} - -TEST(ModuleDepGraph, SimpleExternal2) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, - {{NodeKind::externalDepend, {"/foo->", "/bar->"}}}); - - EXPECT_EQ(1u, graph.findExternallyDependentUntracedJobs("/bar").size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - - EXPECT_EQ(0u, graph.findExternallyDependentUntracedJobs("/bar").size()); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); -} - -TEST(ModuleDepGraph, ChainedExternal) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad( - graph, &job0, - {{NodeKind::externalDepend, {"/foo->"}}, {NodeKind::topLevel, {"a"}}}); - simulateLoad( - graph, &job1, - {{NodeKind::externalDepend, {"/bar->"}}, {NodeKind::topLevel, {"a->"}}}); - - EXPECT_TRUE(contains(graph.getExternalDependencies(), "/foo")); - EXPECT_TRUE(contains(graph.getExternalDependencies(), "/bar")); - - { - auto jobs = graph.findExternallyDependentUntracedJobs("/foo"); - EXPECT_EQ(jobs.size(), 2u); - EXPECT_TRUE(contains(jobs, &job0)); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - - { - auto jobs = graph.findExternallyDependentUntracedJobs("/foo"); - EXPECT_EQ(jobs.size(), 0u); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); -} - -TEST(ModuleDepGraph, ChainedExternalReverse) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad( - graph, &job0, - {{NodeKind::externalDepend, {"/foo->"}}, {NodeKind::topLevel, {"a"}}}); - simulateLoad( - graph, &job1, - {{NodeKind::externalDepend, {"/bar->"}}, {NodeKind::topLevel, {"a->"}}}); - - { - auto jobs = graph.findExternallyDependentUntracedJobs("/bar"); - EXPECT_EQ(1u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - - EXPECT_EQ(0u, graph.findExternallyDependentUntracedJobs("/bar").size()); - EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); - - { - auto jobs = graph.findExternallyDependentUntracedJobs("/foo"); - EXPECT_EQ(1u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job0)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); -} - -TEST(ModuleDepGraph, ChainedExternalPreMarked) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad( - graph, &job0, - {{NodeKind::externalDepend, {"/foo->"}}, {NodeKind::topLevel, {"a"}}}); - simulateLoad( - graph, &job1, - {{NodeKind::externalDepend, {"/bar->"}}, {NodeKind::topLevel, {"a->"}}}); - - { - auto jobs = graph.findExternallyDependentUntracedJobs("/foo"); - EXPECT_EQ(2u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job0)); - EXPECT_TRUE(contains(jobs, &job1)); - } - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); - EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); -} - -TEST(ModuleDepGraph, MutualInterfaceHash) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - simulateLoad(graph, &job0, {{NodeKind::topLevel, {"a", "b->"}}}); - simulateLoad(graph, &job1, {{NodeKind::topLevel, {"a->", "b"}}}); - - const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_TRUE(contains(jobs, &job1)); -} - -TEST(ModuleDepGraph, EnabledTypeBodyFingerprints) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"B2->"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"B1", "B2"}}}); - simulateLoad(graph, &job2, {{NodeKind::nominal, {"B1->"}}}); - - { - const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job1); - EXPECT_EQ(3u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job0)); - EXPECT_TRUE(contains(jobs, &job1)); - EXPECT_TRUE(contains(jobs, &job2)); - } -} - -TEST(ModuleDepGraph, BaselineForPrintsAndCrossType) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - // Because when A1 changes, B1 and not B2 is affected, only jobs1 and job2 - // should be recompiled, except type fingerprints is off! - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"A1", "A2"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"B1", "A1->"}}}); - simulateLoad(graph, &job2, {{NodeKind::nominal, {"C1", "A2->"}}}); - simulateLoad(graph, &job3, {{NodeKind::nominal, {"D1"}}}); - - { - const auto jobs = simulateReload( - graph, &job0, {{NodeKind::nominal, {"A1", "A2"}}}, "changed"); - EXPECT_EQ(3u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job0)); - EXPECT_TRUE(contains(jobs, &job1)); - EXPECT_TRUE(contains(jobs, &job2)); - EXPECT_FALSE(contains(jobs, &job3)); - } -} - -TEST(ModuleDepGraph, LoadPassesWithFingerprint) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - EXPECT_TRUE( - getChangesForSimulatedLoad(graph, &job0, {{NodeKind::nominal, {"A@1"}}})); -} - -TEST(ModuleDepGraph, UseFingerprints) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - - // Because when A1 changes, B1 and not B2 is affected, only jobs1 and job2 - // should be recompiled, except type fingerprints is off! - // Include a dependency on A1, to ensure it does not muck things up. - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"A1@1", "A2@2", "A1->"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"B1", "A1->"}}}); - simulateLoad(graph, &job2, {{NodeKind::nominal, {"C1", "A2->"}}}); - simulateLoad(graph, &job3, {{NodeKind::nominal, {"D1"}}}); - - { - const auto jobs = - simulateReload(graph, &job0, {{NodeKind::nominal, {"A1@11", "A2@2"}}}); - EXPECT_EQ(3u, jobs.size()); - EXPECT_TRUE(contains(jobs, &job0)); - EXPECT_TRUE(contains(jobs, &job1)); - EXPECT_TRUE(contains(jobs, &job2)); - EXPECT_FALSE(contains(jobs, &job3)); - } -} - -TEST(ModuleDepGraph, CrossTypeDependencyBaseline) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - simulateLoad(graph, &job0, {{NodeKind::nominal, {"A"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"B", "C", "A->"}}}); - simulateLoad(graph, &job2, {{NodeKind::nominal, {"B->"}}}); - simulateLoad(graph, &job3, {{NodeKind::nominal, {"C->"}}}); - - const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_TRUE(contains(jobs, &job0)); - EXPECT_TRUE(contains(jobs, &job1)); - EXPECT_TRUE(contains(jobs, &job2)); - EXPECT_TRUE(contains(jobs, &job3)); -} - -TEST(ModuleDepGraph, CrossTypeDependency) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - // Because of the cross-type dependency, A->B, - // when A changes, only B is dirtied in job1. - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"A"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"B", "C", "A->B"}}}); - simulateLoad(graph, &job2, {{NodeKind::nominal, {"B->"}}}); - simulateLoad(graph, &job3, {{NodeKind::nominal, {"C->"}}}); - - const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); - EXPECT_TRUE(contains(jobs, &job0)); - EXPECT_TRUE(contains(jobs, &job1)); - EXPECT_TRUE(contains(jobs, &job2)); - EXPECT_FALSE(contains(jobs, &job3)); -} - -TEST(ModuleDepGraph, CrossTypeDependencyBaselineWithFingerprints) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - simulateLoad(graph, &job0, {{NodeKind::nominal, {"A1@1", "A2@2"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"B1", "C1", "A1->"}}}); - simulateLoad(graph, &job2, {{NodeKind::nominal, {"B1->"}}}); - simulateLoad(graph, &job3, {{NodeKind::nominal, {"C1->"}}}); - simulateLoad(graph, &job4, {{NodeKind::nominal, {"B2", "C2", "A2->"}}}); - simulateLoad(graph, &job5, {{NodeKind::nominal, {"B2->"}}}); - simulateLoad(graph, &job6, {{NodeKind::nominal, {"C2->"}}}); - - const auto jobs = - simulateReload(graph, &job0, {{NodeKind::nominal, {"A1@11", "A2@2"}}}); - EXPECT_TRUE(contains(jobs, &job0)); - EXPECT_TRUE(contains(jobs, &job1)); - EXPECT_TRUE(contains(jobs, &job2)); - EXPECT_TRUE(contains(jobs, &job3)); - EXPECT_FALSE(contains(jobs, &job4)); - EXPECT_FALSE(contains(jobs, &job5)); - EXPECT_FALSE(contains(jobs, &job6)); -} - -TEST(ModuleDepGraph, CrossTypeDependencyWithFingerprints) { - ModuleDepGraph graph(/*EnableTypeFingerprints=*/true); - // Because of the cross-type dependency, A->B, - // when A changes, only B is dirtied in job1. - - simulateLoad(graph, &job0, {{NodeKind::nominal, {"A1@1", "A2@2"}}}); - simulateLoad(graph, &job1, {{NodeKind::nominal, {"B1", "C1", "A1->B1"}}}); - simulateLoad(graph, &job2, {{NodeKind::nominal, {"B1->"}}}); - simulateLoad(graph, &job3, {{NodeKind::nominal, {"C1->"}}}); - simulateLoad(graph, &job4, {{NodeKind::nominal, {"B2", "C2", "A2->B2"}}}); - simulateLoad(graph, &job5, {{NodeKind::nominal, {"B2->"}}}); - simulateLoad(graph, &job6, {{NodeKind::nominal, {"C2->"}}}); - - const auto jobs = - simulateReload(graph, &job0, {{NodeKind::nominal, {"A1@11", "A2@2"}}}); - EXPECT_TRUE(contains(jobs, &job0)); - EXPECT_TRUE(contains(jobs, &job1)); - EXPECT_TRUE(contains(jobs, &job2)); - EXPECT_FALSE(contains(jobs, &job3)); - EXPECT_FALSE(contains(jobs, &job4)); - EXPECT_FALSE(contains(jobs, &job5)); - EXPECT_FALSE(contains(jobs, &job6)); -} diff --git a/validation-test/Driver/Dependencies/rdar23148987-type-fingerprints.swift b/validation-test/Driver/Dependencies/rdar23148987-type-fingerprints.swift deleted file mode 100644 index 3eefcdd073ba8..0000000000000 --- a/validation-test/Driver/Dependencies/rdar23148987-type-fingerprints.swift +++ /dev/null @@ -1,61 +0,0 @@ -// RUN: %empty-directory(%t) - -// RUN: cp %s %t/main.swift -// RUN: cp %S/Inputs/rdar23148987/helper-1.swift %t/helper.swift -// RUN: touch -t 201401240005 %t/*.swift - -// RUN: cd %t && %target-build-swift -enable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-1 %s - -// CHECK-1-NOT: warning -// CHECK-1: {{^{$}} -// CHECK-1: "kind": "began" -// CHECK-1: "name": "compile" -// CHECK-1: ".\/main.swift" -// CHECK-1: {{^}$}} - -// CHECK-1: {{^{$}} -// CHECK-1: "kind": "began" -// CHECK-1: "name": "compile" -// CHECK-1: ".\/helper.swift" -// CHECK-1: {{^}$}} - -// RUN: ls %t/ | %FileCheck -check-prefix=CHECK-LS %s - -// CHECK-LS-DAG: main.o -// CHECK-LS-DAG: helper.o - -// RUN: cd %t && %target-build-swift -enable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-1-SKIPPED %s - -// CHECK-1-SKIPPED-NOT: warning -// CHECK-1-SKIPPED: {{^{$}} -// CHECK-1-SKIPPED: "kind": "skipped" -// CHECK-1-SKIPPED: "name": "compile" -// CHECK-1-SKIPPED: ".\/main.swift" -// CHECK-1-SKIPPED: {{^}$}} - -// CHECK-1-SKIPPED: {{^{$}} -// CHECK-1-SKIPPED: "kind": "skipped" -// CHECK-1-SKIPPED: "name": "compile" -// CHECK-1-SKIPPED: ".\/helper.swift" -// CHECK-1-SKIPPED: {{^}$}} - -// RUN: cp %S/Inputs/rdar23148987/helper-2.swift %t/helper.swift -// RUN: touch -t 201401240006 %t/helper.swift -// RUN: cd %t && %target-build-swift -enable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 -driver-show-incremental -driver-show-job-lifecycle | %FileCheck -check-prefix=CHECK-2 %s - -// CHECK-2-NOT: warning -// CHECK-2: {{^{$}} -// CHECK-2: "kind": "began" -// CHECK-2: "name": "compile" -// CHECK-2: ".\/helper.swift" -// CHECK-2: {{^}$}} - -// CHECK-2: {{^{$}} -// CHECK-2: "kind": "began" -// CHECK-2: "name": "compile" -// CHECK-2: ".\/main.swift" -// CHECK-2: {{^}$}} - -func test(obj: Test) { - obj.foo() -} diff --git a/validation-test/Driver/Dependencies/rdar23148987.swift b/validation-test/Driver/Dependencies/rdar23148987.swift index 7ff7bcae38369..35aa9e75d74b6 100644 --- a/validation-test/Driver/Dependencies/rdar23148987.swift +++ b/validation-test/Driver/Dependencies/rdar23148987.swift @@ -4,7 +4,7 @@ // RUN: cp %S/Inputs/rdar23148987/helper-1.swift %t/helper.swift // RUN: touch -t 201401240005 %t/*.swift -// RUN: cd %t && %target-build-swift -disable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-1 %s +// RUN: cd %t && %target-build-swift -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-1 %s // CHECK-1-NOT: warning // CHECK-1: {{^{$}} @@ -24,7 +24,7 @@ // CHECK-LS-DAG: main.o // CHECK-LS-DAG: helper.o -// RUN: cd %t && %target-build-swift -disable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-1-SKIPPED %s +// RUN: cd %t && %target-build-swift -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-1-SKIPPED %s // CHECK-1-SKIPPED-NOT: warning // CHECK-1-SKIPPED: {{^{$}} @@ -41,7 +41,7 @@ // RUN: cp %S/Inputs/rdar23148987/helper-2.swift %t/helper.swift // RUN: touch -t 201401240006 %t/helper.swift -// RUN: cd %t && %target-build-swift -disable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-2 %s +// RUN: cd %t && %target-build-swift -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 -driver-show-incremental -driver-show-job-lifecycle | %FileCheck -check-prefix=CHECK-2 %s // CHECK-2-NOT: warning // CHECK-2: {{^{$}} From a664a33b52680bca4c80d6749583a86da6bac902 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 30 Sep 2020 20:16:39 -0700 Subject: [PATCH 160/745] SIL: Add instructions to represent async suspend points. `get_async_continuation[_addr]` begins a suspend operation by accessing the continuation value that can resume the task, which can then be used in a callback or event handler before executing `await_async_continuation` to suspend the task. --- include/swift/AST/ASTContext.h | 4 +- include/swift/AST/KnownObjCTypes.def | 37 ------ include/swift/AST/KnownSDKTypes.def | 43 +++++++ include/swift/SIL/SILBuilder.h | 26 ++++- include/swift/SIL/SILCloner.h | 35 ++++++ include/swift/SIL/SILInstruction.h | 101 +++++++++++++++++ include/swift/SIL/SILNodes.def | 12 ++ include/swift/SILOptimizer/Utils/SCCVisitor.h | 6 + lib/AST/ASTContext.cpp | 11 +- lib/IRGen/IRGenSIL.cpp | 14 +++ lib/SIL/IR/OperandOwnership.cpp | 4 + lib/SIL/IR/SILArgument.cpp | 1 + lib/SIL/IR/SILInstructions.cpp | 43 +++++++ lib/SIL/IR/SILPrinter.cpp | 22 ++++ lib/SIL/IR/ValueOwnership.cpp | 2 + lib/SIL/Parser/ParseSIL.cpp | 75 +++++++++++++ lib/SIL/Utils/BasicBlockUtils.cpp | 31 +++++ lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp | 1 + .../Transforms/DeadCodeElimination.cpp | 2 + lib/SILOptimizer/Transforms/SimplifyCFG.cpp | 3 + .../UtilityPasses/SerializeSILPass.cpp | 3 + lib/SILOptimizer/Utils/CFGOptUtils.cpp | 15 +++ lib/SILOptimizer/Utils/SILInliner.cpp | 7 ++ lib/Serialization/DeserializeSIL.cpp | 30 +++++ lib/Serialization/ModuleFormat.h | 2 +- lib/Serialization/SerializeSIL.cpp | 31 +++++ .../public/Concurrency/PartialAsyncTask.swift | 16 +++ test/SIL/Parser/async.sil | 106 +++++++++++++++++- test/SIL/Serialization/async.sil | 100 +++++++++++++++++ 29 files changed, 736 insertions(+), 47 deletions(-) delete mode 100644 include/swift/AST/KnownObjCTypes.def create mode 100644 include/swift/AST/KnownSDKTypes.def create mode 100644 test/SIL/Serialization/async.sil diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 79a48b324e588..a568f6c15fee3 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -499,13 +499,13 @@ class ASTContext final { /// Retrieve the type Swift.Never. CanType getNeverType() const; -#define KNOWN_OBJC_TYPE_DECL(MODULE, NAME, DECL_CLASS) \ +#define KNOWN_SDK_TYPE_DECL(MODULE, NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \ /** Retrieve the declaration of MODULE.NAME. */ \ DECL_CLASS *get##NAME##Decl() const; \ \ /** Retrieve the type of MODULE.NAME. */ \ Type get##NAME##Type() const; -#include "swift/AST/KnownObjCTypes.def" +#include "swift/AST/KnownSDKTypes.def" // Declare accessors for the known declarations. #define FUNC_DECL(Name, Id) \ diff --git a/include/swift/AST/KnownObjCTypes.def b/include/swift/AST/KnownObjCTypes.def deleted file mode 100644 index b77eb4f893554..0000000000000 --- a/include/swift/AST/KnownObjCTypes.def +++ /dev/null @@ -1,37 +0,0 @@ -//===--- KnownObjCTypes.def - Common Objective-C types --------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2019 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -// -// This xmacro generates code for common imported Objective-C types the -// compiler has special knowledge of. -// -//===----------------------------------------------------------------------===// - -#ifndef KNOWN_OBJC_TYPE_DECL -/// KNOWN_OBJC_TYPE_DECL(MODULE, NAME, DECL_CLASS) -/// -/// The macro is expanded for each known imported Objective-C type. MODULE is -/// bound to the name of the module the type comes from. NAME is bound to the -/// unqualified name of the type. DECL_CLASS is bound to the Decl subclass it -/// is expected to be an instance of. -#define KNOWN_OBJC_TYPE_DECL(MODULE, NAME, DECL_CLASS) -#endif - -KNOWN_OBJC_TYPE_DECL(Foundation, NSCopying, ProtocolDecl) -KNOWN_OBJC_TYPE_DECL(Foundation, NSError, ClassDecl) -KNOWN_OBJC_TYPE_DECL(Foundation, NSNumber, ClassDecl) -KNOWN_OBJC_TYPE_DECL(Foundation, NSValue, ClassDecl) - -KNOWN_OBJC_TYPE_DECL(ObjectiveC, NSObject, ClassDecl) -KNOWN_OBJC_TYPE_DECL(ObjectiveC, Selector, StructDecl) -KNOWN_OBJC_TYPE_DECL(ObjectiveC, ObjCBool, StructDecl) - -#undef KNOWN_OBJC_TYPE_DECL diff --git a/include/swift/AST/KnownSDKTypes.def b/include/swift/AST/KnownSDKTypes.def new file mode 100644 index 0000000000000..aaaa2c45411d1 --- /dev/null +++ b/include/swift/AST/KnownSDKTypes.def @@ -0,0 +1,43 @@ +//===--- KnownSDKTypes.def - Common SDK types -----------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This xmacro generates code for common SDK types the +// compiler has special knowledge of. +// +//===----------------------------------------------------------------------===// + +#ifndef KNOWN_SDK_TYPE_DECL +/// KNOWN_SDK_TYPE_DECL(MODULE, NAME, DECL_CLASS, NUM_GENERIC_ARGS) +/// +/// The macro is expanded for each known SDK type. MODULE is +/// bound to the name of the module the type comes from. NAME is bound to the +/// unqualified name of the type. DECL_CLASS is bound to the Decl subclass it +/// is expected to be an instance of. NUM_GENERIC_ARGS is the number of generic +/// parameters the decl ought to have. +#define KNOWN_SDK_TYPE_DECL(MODULE, NAME, DECL_CLASS, NUM_GENERIC_ARGS) +#endif + +KNOWN_SDK_TYPE_DECL(Foundation, NSCopying, ProtocolDecl, 0) +KNOWN_SDK_TYPE_DECL(Foundation, NSError, ClassDecl, 0) +KNOWN_SDK_TYPE_DECL(Foundation, NSNumber, ClassDecl, 0) +KNOWN_SDK_TYPE_DECL(Foundation, NSValue, ClassDecl, 0) + +KNOWN_SDK_TYPE_DECL(ObjectiveC, NSObject, ClassDecl, 0) +KNOWN_SDK_TYPE_DECL(ObjectiveC, Selector, StructDecl, 0) +KNOWN_SDK_TYPE_DECL(ObjectiveC, ObjCBool, StructDecl, 0) + +// TODO(async): These might move to the stdlib module when concurrency is +// standardized +KNOWN_SDK_TYPE_DECL(Concurrency, UnsafeContinuation, NominalTypeDecl, 1) +KNOWN_SDK_TYPE_DECL(Concurrency, UnsafeThrowingContinuation, NominalTypeDecl, 1) + +#undef KNOWN_SDK_TYPE_DECL diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 9b84da4bfae9d..83d93da9059c1 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -1934,6 +1934,20 @@ class SILBuilder { getSILDebugLocation(Loc), Operand, Index)); } + GetAsyncContinuationInst *createGetAsyncContinuation(SILLocation Loc, + SILType ContinuationTy) { + return insert(new (getModule()) GetAsyncContinuationInst(getSILDebugLocation(Loc), + ContinuationTy)); + } + + GetAsyncContinuationAddrInst *createGetAsyncContinuationAddr(SILLocation Loc, + SILValue Operand, + SILType ContinuationTy) { + return insert(new (getModule()) GetAsyncContinuationAddrInst(getSILDebugLocation(Loc), + Operand, + ContinuationTy)); + } + //===--------------------------------------------------------------------===// // Terminator SILInstruction Creation Methods //===--------------------------------------------------------------------===// @@ -1964,7 +1978,17 @@ class SILBuilder { YieldInst::create(getSILDebugLocation(loc), yieldedValues, resumeBB, unwindBB, getFunction())); } - + + AwaitAsyncContinuationInst *createAwaitAsyncContinuation(SILLocation loc, + SILValue continuation, + SILBasicBlock *resumeBB, + SILBasicBlock *errorBB) { + return insertTerminator( + new (getModule()) AwaitAsyncContinuationInst(getSILDebugLocation(loc), + continuation, + resumeBB, errorBB)); + } + CondBranchInst * createCondBranch(SILLocation Loc, SILValue Cond, SILBasicBlock *Target1, SILBasicBlock *Target2, diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 08880bac019d0..8e0a0110561d7 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -2928,6 +2928,41 @@ void SILCloner::visitDifferentiabilityWitnessFunctionInst( Inst->getWitnessKind(), Inst->getWitness())); } +template +void SILCloner +::visitGetAsyncContinuationInst(GetAsyncContinuationInst *Inst) { + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + recordClonedInstruction(Inst, + getBuilder().createGetAsyncContinuation( + getOpLocation(Inst->getLoc()), + getOpType(Inst->getType()))); +} + +template +void SILCloner +::visitGetAsyncContinuationAddrInst(GetAsyncContinuationAddrInst *Inst) { + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + recordClonedInstruction(Inst, + getBuilder().createGetAsyncContinuationAddr( + getOpLocation(Inst->getLoc()), + getOpValue(Inst->getOperand()), + getOpType(Inst->getType()))); +} + +template +void SILCloner +::visitAwaitAsyncContinuationInst(AwaitAsyncContinuationInst *Inst) { + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + recordClonedInstruction(Inst, + getBuilder().createAwaitAsyncContinuation( + getOpLocation(Inst->getLoc()), + getOpValue(Inst->getOperand()), + getOpBasicBlock(Inst->getResumeBB()), + Inst->getErrorBB() + ? getOpBasicBlock(Inst->getErrorBB()) + : nullptr)); +} + } // end namespace swift #endif diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 42b62c869c3f3..c336ed5ab5d8a 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -3039,6 +3039,60 @@ class KeyPathPattern final } }; +/// Accesses the continuation for an async task, to prepare a primitive suspend operation. +/// The continuation must be consumed by an AwaitAsyncContinuation instruction locally, +/// and must dynamically be resumed exactly once during the program's ensuing execution. +class GetAsyncContinuationInst final + : public InstructionBase +{ + friend SILBuilder; + + GetAsyncContinuationInst(SILDebugLocation Loc, + SILType ContinuationTy) + : InstructionBase(Loc, ContinuationTy) + {} + +public: + + /// Get the type of the value the async task receives on a resume. + CanType getFormalResumeType() const; + SILType getLoweredResumeType() const; + + /// True if the continuation can be used to resume the task by throwing an error. + bool throws() const; + + ArrayRef getAllOperands() const { return {}; } + MutableArrayRef getAllOperands() { return {}; } +}; + +/// Accesses the continuation for an async task, to prepare a primitive suspend operation. +/// The continuation must be consumed by an AwaitAsyncContinuation instruction locally, +/// and must dynamically be resumed exactly once during the program's ensuing execution. +/// +/// This variation of the instruction additionally takes an operand for the address of the +/// buffer that receives the incoming value when the continuation is resumed. +class GetAsyncContinuationAddrInst final + : public UnaryInstructionBase +{ + friend SILBuilder; + GetAsyncContinuationAddrInst(SILDebugLocation Loc, + SILValue Operand, + SILType ContinuationTy) + : UnaryInstructionBase(Loc, Operand, ContinuationTy) + {} + +public: + + /// Get the type of the value the async task receives on a resume. + CanType getFormalResumeType() const; + SILType getLoweredResumeType() const; + + /// True if the continuation can be used to resume the task by throwing an error. + bool throws() const; +}; + /// Instantiates a key path object. class KeyPathInst final : public InstructionBase getAllOperands() { return {}; } }; +/// Suspend execution of an async task until +/// essentially just a funny kind of return). +class AwaitAsyncContinuationInst final + : public UnaryInstructionBase +{ + friend SILBuilder; + + std::array Successors; + + AwaitAsyncContinuationInst(SILDebugLocation Loc, SILValue Continuation, + SILBasicBlock *resumeBB, + SILBasicBlock *errorBBOrNull) + : UnaryInstructionBase(Loc, Continuation), + Successors{{{this}, {this}}} + { + Successors[0] = resumeBB; + if (errorBBOrNull) + Successors[1] = errorBBOrNull; + } + +public: + /// Returns the basic block to which control is transferred when the task is + /// resumed normally. + /// + /// This basic block should take an argument of the continuation's resume type, + /// unless the continuation is formed by a \c GetAsyncContinuationAddrInst + /// that binds a specific memory location to receive the resume value. + SILBasicBlock *getResumeBB() const { return Successors[0].getBB(); } + + /// Returns the basic block to which control is transferred when the task is + /// resumed in an error state, or `nullptr` if the continuation does not support + /// failure. + /// + /// This basic block should take an argument of Error type. + SILBasicBlock *getErrorBB() const { + return Successors[1].getBB(); + } + + SuccessorListTy getSuccessors() { + if (getErrorBB()) + return Successors; + return SuccessorListTy(Successors.data(), 1); + } +}; + /// YieldInst - Yield control temporarily to the caller of this coroutine. /// /// This is a terminator because the caller can abort the coroutine, diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index d26054e66aed4..e79d11a57778b 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -713,6 +713,16 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) SINGLE_VALUE_INST(DifferentiabilityWitnessFunctionInst, differentiability_witness_function, SingleValueInstruction, None, DoesNotRelease) + + // Async + // TODO: The side effects declarations on this instruction could likely + // be tightened, though we want to be careful that passes that try to do + // code motion or eliminate this instruction don't do so without awareness of + // its structural requirements. + SINGLE_VALUE_INST(GetAsyncContinuationInst, get_async_continuation, + SingleValueInstruction, MayHaveSideEffects, MayRelease) + SINGLE_VALUE_INST(GetAsyncContinuationAddrInst, get_async_continuation_addr, + SingleValueInstruction, MayHaveSideEffects, MayRelease) // Key paths // TODO: The only "side effect" is potentially retaining the returned key path @@ -750,6 +760,8 @@ ABSTRACT_INST(TermInst, SILInstruction) TermInst, MayRead, DoesNotRelease) TERMINATOR(DynamicMethodBranchInst, dynamic_method_br, TermInst, None, DoesNotRelease) + TERMINATOR(AwaitAsyncContinuationInst, await_async_continuation, + TermInst, MayHaveSideEffects, MayRelease) DYNAMICCAST_TERMINATOR(CheckedCastBranchInst, checked_cast_br, TermInst, None, DoesNotRelease) DYNAMICCAST_TERMINATOR(CheckedCastAddrBranchInst, checked_cast_addr_br, diff --git a/include/swift/SILOptimizer/Utils/SCCVisitor.h b/include/swift/SILOptimizer/Utils/SCCVisitor.h index f505506765dea..e5f2dd665b201 100644 --- a/include/swift/SILOptimizer/Utils/SCCVisitor.h +++ b/include/swift/SILOptimizer/Utils/SCCVisitor.h @@ -116,6 +116,12 @@ class SCCVisitor { Operands.push_back(CBI->getFalseArgs()[Index]); return; } + + case TermKind::AwaitAsyncContinuationInst: { + auto *AACI = cast(Term); + Operands.push_back(AACI->getOperand()); + return; + } case TermKind::SwitchEnumInst: case TermKind::SwitchEnumAddrInst: diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index f09ac7b231b5c..db4d71185df92 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -191,10 +191,10 @@ struct ASTContext::Implementation { DECL_CLASS *NAME##Decl = nullptr; #include "swift/AST/KnownStdlibTypes.def" -#define KNOWN_OBJC_TYPE_DECL(MODULE, NAME, DECL_CLASS) \ +#define KNOWN_SDK_TYPE_DECL(MODULE, NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \ /** The declaration of MODULE.NAME. */ \ DECL_CLASS *NAME##Decl = nullptr; -#include "swift/AST/KnownObjCTypes.def" +#include "swift/AST/KnownSDKTypes.def" /// The declaration of '+' function for two RangeReplaceableCollection. FuncDecl *PlusFunctionOnRangeReplaceableCollection = nullptr; @@ -894,7 +894,7 @@ CanType ASTContext::getNeverType() const { return neverDecl->getDeclaredInterfaceType()->getCanonicalType(); } -#define KNOWN_OBJC_TYPE_DECL(MODULE, NAME, DECLTYPE) \ +#define KNOWN_SDK_TYPE_DECL(MODULE, NAME, DECLTYPE, GENERIC_ARGS) \ DECLTYPE *ASTContext::get##NAME##Decl() const { \ if (!getImpl().NAME##Decl) { \ if (ModuleDecl *M = getLoadedModule(Id_##MODULE)) { \ @@ -905,7 +905,8 @@ DECLTYPE *ASTContext::get##NAME##Decl() const { \ decls); \ if (decls.size() == 1 && isa(decls[0])) { \ auto decl = cast(decls[0]); \ - if (isa(decl) || decl->getGenericParams() == nullptr) { \ + if (isa(decl) \ + || (bool)decl->getGenericParams() == (bool)GENERIC_ARGS) { \ getImpl().NAME##Decl = decl; \ } \ } \ @@ -922,7 +923,7 @@ Type ASTContext::get##NAME##Type() const { \ return decl->getDeclaredInterfaceType(); \ } -#include "swift/AST/KnownObjCTypes.def" +#include "swift/AST/KnownSDKTypes.def" ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { // Check whether we've already looked for and cached this protocol. diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 43fa8b25592c6..98ef49825d5df 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1083,6 +1083,19 @@ class IRGenSILFunction : void visitCheckedCastValueBranchInst(CheckedCastValueBranchInst *i); void visitCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *i); + void visitGetAsyncContinuationInst(GetAsyncContinuationInst *i) { + //TODO(async) + llvm_unreachable("not implemented"); + } + void visitGetAsyncContinuationAddrInst(GetAsyncContinuationAddrInst *i) { + //TODO(async) + llvm_unreachable("not implemented"); + } + void visitAwaitAsyncContinuationInst(AwaitAsyncContinuationInst *i) { + //TODO(async) + llvm_unreachable("not implemented"); + } + void visitKeyPathInst(KeyPathInst *I); void visitDifferentiableFunctionInst(DifferentiableFunctionInst *i); @@ -1112,6 +1125,7 @@ class IRGenSILFunction : LOADABLE_REF_STORAGE_HELPER(Name) #include "swift/AST/ReferenceStorage.def" #undef LOADABLE_REF_STORAGE_HELPER + }; } // end anonymous namespace diff --git a/lib/SIL/IR/OperandOwnership.cpp b/lib/SIL/IR/OperandOwnership.cpp index 7f928667b8913..6b51e8b4f69f9 100644 --- a/lib/SIL/IR/OperandOwnership.cpp +++ b/lib/SIL/IR/OperandOwnership.cpp @@ -136,6 +136,8 @@ SHOULD_NEVER_VISIT_INST(Unwind) SHOULD_NEVER_VISIT_INST(ReleaseValue) SHOULD_NEVER_VISIT_INST(ReleaseValueAddr) SHOULD_NEVER_VISIT_INST(StrongRelease) +SHOULD_NEVER_VISIT_INST(GetAsyncContinuation) + #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ SHOULD_NEVER_VISIT_INST(StrongRetain##Name) \ SHOULD_NEVER_VISIT_INST(Name##Retain) @@ -174,6 +176,7 @@ CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, DestroyValue) CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, EndLifetime) CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, BeginCOWMutation) CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, EndCOWMutation) +CONSTANT_OWNERSHIP_INST(Owned, MustBeLive, AwaitAsyncContinuation) CONSTANT_OWNERSHIP_INST(None, MustBeLive, AbortApply) CONSTANT_OWNERSHIP_INST(None, MustBeLive, AddressToPointer) CONSTANT_OWNERSHIP_INST(None, MustBeLive, BeginAccess) @@ -189,6 +192,7 @@ CONSTANT_OWNERSHIP_INST(None, MustBeLive, DestroyAddr) CONSTANT_OWNERSHIP_INST(None, MustBeLive, EndAccess) CONSTANT_OWNERSHIP_INST(None, MustBeLive, EndApply) CONSTANT_OWNERSHIP_INST(None, MustBeLive, EndUnpairedAccess) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, GetAsyncContinuationAddr) CONSTANT_OWNERSHIP_INST(None, MustBeLive, IndexAddr) CONSTANT_OWNERSHIP_INST(None, MustBeLive, IndexRawPointer) CONSTANT_OWNERSHIP_INST(None, MustBeLive, InitBlockStorageHeader) diff --git a/lib/SIL/IR/SILArgument.cpp b/lib/SIL/IR/SILArgument.cpp index 1d447fbd00976..48affb815ab21 100644 --- a/lib/SIL/IR/SILArgument.cpp +++ b/lib/SIL/IR/SILArgument.cpp @@ -228,6 +228,7 @@ getSingleTerminatorOperandForPred(const SILBasicBlock *parentBlock, case TermKind::CheckedCastAddrBranchInst: case TermKind::DynamicMethodBranchInst: case TermKind::YieldInst: + case TermKind::AwaitAsyncContinuationInst: return SILValue(); case TermKind::BranchInst: return cast(predTermInst)->getArg(argIndex); diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index 1a69383a94168..5655e95219b8a 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1466,6 +1466,7 @@ TermInst::SuccessorListTy TermInst::getSuccessors() { bool TermInst::isFunctionExiting() const { switch (getTermKind()) { + case TermKind::AwaitAsyncContinuationInst: case TermKind::BranchInst: case TermKind::CondBranchInst: case TermKind::SwitchValueInst: @@ -1490,6 +1491,7 @@ bool TermInst::isFunctionExiting() const { bool TermInst::isProgramTerminating() const { switch (getTermKind()) { + case TermKind::AwaitAsyncContinuationInst: case TermKind::BranchInst: case TermKind::CondBranchInst: case TermKind::SwitchValueInst: @@ -2920,3 +2922,44 @@ DestructureTupleInst *DestructureTupleInst::create(const SILFunction &F, return ::new (Buffer) DestructureTupleInst(M, Loc, Operand, Types, OwnershipKinds); } + +CanType GetAsyncContinuationInst::getFormalResumeType() const { + // The resume type is the type argument to the continuation type. + return getType().castTo().getGenericArgs()[0]; +} + +SILType GetAsyncContinuationInst::getLoweredResumeType() const { + // The lowered resume type is the maximally-abstracted lowering of the + // formal resume type. + auto formalType = getFormalResumeType(); + auto &M = getFunction()->getModule(); + auto c = getFunction()->getTypeExpansionContext(); + return M.Types.getLoweredType(AbstractionPattern::getOpaque(), formalType, c); +} + +bool GetAsyncContinuationInst::throws() const { + // The continuation throws if it's an UnsafeThrowingContinuation + return getType().castTo()->getDecl() + == getFunction()->getASTContext().getUnsafeThrowingContinuationDecl(); +} + +CanType GetAsyncContinuationAddrInst::getFormalResumeType() const { + // The resume type is the type argument to the continuation type. + return getType().castTo().getGenericArgs()[0]; +} + +SILType GetAsyncContinuationAddrInst::getLoweredResumeType() const { + // The lowered resume type is the maximally-abstracted lowering of the + // formal resume type. + auto formalType = getFormalResumeType(); + auto &M = getFunction()->getModule(); + auto c = getFunction()->getTypeExpansionContext(); + return M.Types.getLoweredType(AbstractionPattern::getOpaque(), formalType, c); +} + +bool GetAsyncContinuationAddrInst::throws() const { + // The continuation throws if it's an UnsafeThrowingContinuation + return getType().castTo()->getDecl() + == getFunction()->getASTContext().getUnsafeThrowingContinuationDecl(); +} + diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 25ab673ede95f..f08d0cfc025f3 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -2073,6 +2073,28 @@ class SILPrinter : public SILInstructionVisitor { *this << ", resume " << Ctx.getID(YI->getResumeBB()) << ", unwind " << Ctx.getID(YI->getUnwindBB()); } + + void visitGetAsyncContinuationInst(GetAsyncContinuationInst *GI) { + if (GI->throws()) + *this << "[throws] "; + *this << '$' << GI->getFormalResumeType(); + } + + void visitGetAsyncContinuationAddrInst(GetAsyncContinuationAddrInst *GI) { + if (GI->throws()) + *this << "[throws] "; + *this << '$' << GI->getFormalResumeType() + << ", " << getIDAndType(GI->getOperand()); + } + + void visitAwaitAsyncContinuationInst(AwaitAsyncContinuationInst *AI) { + *this << getIDAndType(AI->getOperand()) + << ", resume " << Ctx.getID(AI->getResumeBB()); + + if (auto errorBB = AI->getErrorBB()) { + *this << ", error " << Ctx.getID(AI->getErrorBB()); + } + } void visitSwitchValueInst(SwitchValueInst *SII) { *this << getIDAndType(SII->getOperand()); diff --git a/lib/SIL/IR/ValueOwnership.cpp b/lib/SIL/IR/ValueOwnership.cpp index 47560bd48fba0..cbf602a1f94af 100644 --- a/lib/SIL/IR/ValueOwnership.cpp +++ b/lib/SIL/IR/ValueOwnership.cpp @@ -148,6 +148,8 @@ CONSTANT_OWNERSHIP_INST(None, DifferentiabilityWitnessFunction) CONSTANT_OWNERSHIP_INST(Unowned, RawPointerToRef) CONSTANT_OWNERSHIP_INST(Unowned, ObjCProtocol) CONSTANT_OWNERSHIP_INST(Unowned, ValueToBridgeObject) +CONSTANT_OWNERSHIP_INST(None, GetAsyncContinuation) +CONSTANT_OWNERSHIP_INST(Unowned, GetAsyncContinuationAddr) #undef CONSTANT_OWNERSHIP_INST #define CONSTANT_OR_NONE_OWNERSHIP_INST(OWNERSHIP, INST) \ diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 28e9fcf231b98..1d093aa9e7132 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -5239,6 +5239,81 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, InstLoc, witnessKind, witness, functionType); break; } + case SILInstructionKind::AwaitAsyncContinuationInst: { + // 'await_async_continuation' operand, 'resume' bb, 'error' bb + Identifier ResumeBBName, ErrorBBName{}; + SourceLoc ResumeNameLoc, ErrorNameLoc{}; + if (parseTypedValueRef(Val, B) + || P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") + || P.parseSpecificIdentifier("resume", diag::expected_tok_in_sil_instr, "resume") + || parseSILIdentifier(ResumeBBName, ResumeNameLoc, diag::expected_sil_block_name)) { + return true; + } + + if (P.consumeIf(tok::comma)) { + if (P.parseSpecificIdentifier("error", diag::expected_tok_in_sil_instr, "error") + || parseSILIdentifier(ErrorBBName, ErrorNameLoc, diag::expected_sil_block_name) + || parseSILDebugLocation(InstLoc, B)) { + return true; + } + } + + SILBasicBlock *resumeBB, *errorBB = nullptr; + resumeBB = getBBForReference(ResumeBBName, ResumeNameLoc); + if (!ErrorBBName.empty()) { + errorBB = getBBForReference(ErrorBBName, ErrorNameLoc); + } + ResultVal = B.createAwaitAsyncContinuation(InstLoc, Val, resumeBB, errorBB); + break; + } + case SILInstructionKind::GetAsyncContinuationInst: + case SILInstructionKind::GetAsyncContinuationAddrInst: { + // 'get_async_continuation' '[throws]'? type + // 'get_async_continuation_addr' '[throws]'? type ',' operand + bool throws = false; + if (P.consumeIf(tok::l_square)) { + if (P.parseToken(tok::kw_throws, diag::expected_tok_in_sil_instr, "throws") + || P.parseToken(tok::r_square, diag::expected_tok_in_sil_instr, "]")) + return true; + + throws = true; + } + + SILType resumeTy; + if (parseSILType(resumeTy)) { + return true; + } + + SILValue resumeBuffer; + if (Opcode == SILInstructionKind::GetAsyncContinuationAddrInst) { + if (P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") + || parseTypedValueRef(resumeBuffer, B)) { + return true; + } + } + + if (parseSILDebugLocation(InstLoc, B)) + return true; + + auto &M = B.getModule(); + NominalTypeDecl *continuationDecl = throws + ? M.getASTContext().getUnsafeThrowingContinuationDecl() + : M.getASTContext().getUnsafeContinuationDecl(); + + auto continuationTy = BoundGenericType::get(continuationDecl, Type(), + resumeTy.getASTType()); + auto continuationSILTy + = SILType::getPrimitiveObjectType(continuationTy->getCanonicalType()); + + if (Opcode == SILInstructionKind::GetAsyncContinuationAddrInst) { + ResultVal = B.createGetAsyncContinuationAddr(InstLoc, resumeBuffer, + continuationSILTy); + } else { + ResultVal = B.createGetAsyncContinuation(InstLoc, continuationSILTy); + } + break; + } + } return false; diff --git a/lib/SIL/Utils/BasicBlockUtils.cpp b/lib/SIL/Utils/BasicBlockUtils.cpp index 4548f8c99949f..bbae9f40edfbe 100644 --- a/lib/SIL/Utils/BasicBlockUtils.cpp +++ b/lib/SIL/Utils/BasicBlockUtils.cpp @@ -115,6 +115,37 @@ void swift::getEdgeArgs(TermInst *T, unsigned edgeIdx, SILBasicBlock *newEdgeBB, args.push_back(V); return; } + + case SILInstructionKind::AwaitAsyncContinuationInst: { + auto AACI = cast(T); + + switch (edgeIdx) { + case 0: + // resume BB. this takes the resume value argument if the operand is + // GetAsyncContinuation, or no argument if the operand is + // GetAsyncContinuationAddr + if (auto contOperand = dyn_cast(AACI->getOperand())) { + args.push_back( + newEdgeBB->createPhiArgument(contOperand->getLoweredResumeType(), + ValueOwnershipKind::Owned)); + } + return; + + case 1: { + assert(AACI->getErrorBB()); + auto &C = AACI->getFunction()->getASTContext(); + auto errorTy = C.getErrorDecl()->getDeclaredType(); + auto errorSILTy = SILType::getPrimitiveObjectType(errorTy->getCanonicalType()); + // error BB. this takes the error value argument + args.push_back(newEdgeBB->createPhiArgument(errorSILTy, + ValueOwnershipKind::Owned)); + return; + } + + default: + llvm_unreachable("only has at most two edges"); + } + } case SILInstructionKind::SwitchValueInst: { auto SEI = cast(T); diff --git a/lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp b/lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp index ff04e27b99264..62c501669051b 100644 --- a/lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp +++ b/lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp @@ -31,6 +31,7 @@ bool isARCSignificantTerminator(TermInst *TI) { case TermKind::ReturnInst: case TermKind::UnwindInst: case TermKind::YieldInst: + case TermKind::AwaitAsyncContinuationInst: case TermKind::TryApplyInst: case TermKind::SwitchValueInst: case TermKind::SwitchEnumInst: diff --git a/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp b/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp index 9737b457831a4..20ea063804885 100644 --- a/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp @@ -349,6 +349,7 @@ void DCE::markTerminatorArgsLive(SILBasicBlock *Pred, break; } + case TermKind::AwaitAsyncContinuationInst: case TermKind::TryApplyInst: { assert(ArgIndex == 0 && "Expect a single argument!"); break; @@ -412,6 +413,7 @@ void DCE::propagateLiveness(SILInstruction *I) { markValueLive(I->getOperand(0)); return; + case TermKind::AwaitAsyncContinuationInst: case TermKind::CheckedCastBranchInst: case TermKind::CheckedCastValueBranchInst: case TermKind::CheckedCastAddrBranchInst: diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index 176588416d0dd..2038abf6e7dde 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -2688,6 +2688,9 @@ bool SimplifyCFG::simplifyBlocks() { case TermKind::UnwindInst: case TermKind::YieldInst: break; + case TermKind::AwaitAsyncContinuationInst: + // TODO(async): Simplify AwaitAsyncContinuationInst + break; } // If the block has a cond_fail, try to move it to the predecessors. Changed |= tryMoveCondFailToPreds(BB); diff --git a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp index 88b831e20f2ba..ce9ebdb5a597d 100644 --- a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp +++ b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp @@ -336,6 +336,9 @@ static bool hasOpaqueArchetype(TypeExpansionContext context, case SILInstructionKind::DifferentiabilityWitnessFunctionInst: case SILInstructionKind::BeginCOWMutationInst: case SILInstructionKind::EndCOWMutationInst: + case SILInstructionKind::GetAsyncContinuationInst: + case SILInstructionKind::GetAsyncContinuationAddrInst: + case SILInstructionKind::AwaitAsyncContinuationInst: // Handle by operand and result check. break; diff --git a/lib/SILOptimizer/Utils/CFGOptUtils.cpp b/lib/SILOptimizer/Utils/CFGOptUtils.cpp index 213ebe5f15cf9..565fe1d1455bb 100644 --- a/lib/SILOptimizer/Utils/CFGOptUtils.cpp +++ b/lib/SILOptimizer/Utils/CFGOptUtils.cpp @@ -449,6 +449,20 @@ void swift::replaceBranchTarget(TermInst *t, SILBasicBlock *oldDest, yi->eraseFromParent(); return; } + + case TermKind::AwaitAsyncContinuationInst: { + auto ai = cast(t); + SILBasicBlock *resumeBB = + (oldDest == ai->getResumeBB() ? newDest : ai->getResumeBB()); + SILBasicBlock *errorBB = + (oldDest == ai->getErrorBB() ? newDest : ai->getErrorBB()); + + builder.createAwaitAsyncContinuation(ai->getLoc(), + ai->getOperand(), + resumeBB, errorBB); + ai->eraseFromParent(); + return; + } case TermKind::ReturnInst: case TermKind::ThrowInst: @@ -726,6 +740,7 @@ static bool isSafeNonExitTerminator(TermInst *ti) { // yield is special because it can do arbitrary, // potentially-process-terminating things. case TermKind::YieldInst: + case TermKind::AwaitAsyncContinuationInst: return false; case TermKind::TryApplyInst: return true; diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index b08f9832e0237..140657af95659 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -783,6 +783,12 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::EndCOWMutationInst: return InlineCost::Free; + // Turning the task reference into a continuation should be basically free. + // TODO(async): make sure this is true. + case SILInstructionKind::GetAsyncContinuationAddrInst: + case SILInstructionKind::GetAsyncContinuationInst: + return InlineCost::Free; + case SILInstructionKind::AbortApplyInst: case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: @@ -884,6 +890,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::DifferentiableFunctionExtractInst: case SILInstructionKind::LinearFunctionExtractInst: case SILInstructionKind::DifferentiabilityWitnessFunctionInst: + case SILInstructionKind::AwaitAsyncContinuationInst: #define COMMON_ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name) \ case SILInstructionKind::Name##ToRefInst: \ case SILInstructionKind::RefTo##Name##Inst: \ diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index b3024b0939170..b463b3d84cffd 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -1202,6 +1202,21 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, ResultInst = Builder.createMetatype( Loc, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); break; + + case SILInstructionKind::GetAsyncContinuationInst: + assert(RecordKind == SIL_ONE_TYPE && "Layout should be OneType."); + ResultInst = Builder.createGetAsyncContinuation(Loc, + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); + break; + + case SILInstructionKind::GetAsyncContinuationAddrInst: + assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND + && "Layout should be OneTypeOneOperand."); + ResultInst = Builder.createGetAsyncContinuationAddr(Loc, + getLocalValue(ValID, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn)), + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); + break; #define ONETYPE_ONEOPERAND_INST(ID) \ case SILInstructionKind::ID##Inst: \ @@ -2157,6 +2172,21 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, getBBForReference(Fn, ListOfValues[2]), FalseArgs); break; } + case SILInstructionKind::AwaitAsyncContinuationInst: { + // Format: continuation, resume block ID, error block ID if given + SILValue Cont = getLocalValue( + ListOfValues[0], + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); + + SILBasicBlock *resultBB = getBBForReference(Fn, ListOfValues[1]); + SILBasicBlock *errorBB = nullptr; + if (ListOfValues.size() >= 3) { + errorBB = getBBForReference(Fn, ListOfValues[2]); + } + + ResultInst = Builder.createAwaitAsyncContinuation(Loc, Cont, resultBB, errorBB); + break; + } case SILInstructionKind::SwitchEnumInst: case SILInstructionKind::SwitchEnumAddrInst: { // Format: condition, a list of cases (EnumElementDecl + Basic Block ID), diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 3497a404bcde4..9bed35d9f2a46 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 579; // incremental deps info block +const uint16_t SWIFTMODULE_VERSION_MINOR = 580; // async_continuation SIL insns /// A standard hash seed used for all string hashes in a serialized module. /// diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 492b18f98b64f..b651fcae6698d 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -1148,6 +1148,26 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { ListOfValues); break; } + case SILInstructionKind::AwaitAsyncContinuationInst: { + const AwaitAsyncContinuationInst *AACI + = cast(&SI); + + // Format: continuation, resume block ID, error block ID if given + SmallVector ListOfValues; + + ListOfValues.push_back(addValueRef(AACI->getOperand())); + ListOfValues.push_back(BasicBlockMap[AACI->getResumeBB()]); + if (auto errorBB = AACI->getErrorBB()) { + ListOfValues.push_back(BasicBlockMap[errorBB]); + } + SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, + SILAbbrCodes[SILOneTypeValuesLayout::Code], + (unsigned)SI.getKind(), + S.addTypeRef(AACI->getOperand()->getType().getASTType()), + (unsigned)AACI->getOperand()->getType().getCategory(), + ListOfValues); + break; + } case SILInstructionKind::SwitchEnumInst: case SILInstructionKind::SwitchEnumAddrInst: { // Format: condition, a list of cases (EnumElementDecl + Basic Block ID), @@ -1546,6 +1566,17 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { open.getOperand()); break; } + case SILInstructionKind::GetAsyncContinuationAddrInst: { + auto &gaca = cast(SI); + writeOneTypeOneOperandLayout(gaca.getKind(), 0, gaca.getType(), + gaca.getOperand()); + break; + } + case SILInstructionKind::GetAsyncContinuationInst: { + auto &gaca = cast(SI); + writeOneTypeLayout(gaca.getKind(), 0, gaca.getType()); + break; + } // Conversion instructions (and others of similar form). #define LOADABLE_REF_STORAGE(Name, ...) \ case SILInstructionKind::RefTo##Name##Inst: \ diff --git a/stdlib/public/Concurrency/PartialAsyncTask.swift b/stdlib/public/Concurrency/PartialAsyncTask.swift index fe55edc48a189..1f3413f9dd928 100644 --- a/stdlib/public/Concurrency/PartialAsyncTask.swift +++ b/stdlib/public/Concurrency/PartialAsyncTask.swift @@ -18,3 +18,19 @@ public struct PartialAsyncTask { public func run() { } } + + +public struct UnsafeContinuation { + private var context: UnsafeRawPointer + + public func resume(_: T) { } +} + +public struct UnsafeThrowingContinuation { + private var context: UnsafeRawPointer + + public func resume(_: T) { } + public func fail(_: Error) { } +} + + diff --git a/test/SIL/Parser/async.sil b/test/SIL/Parser/async.sil index f65cee1ca6e16..992fb8d34a95e 100644 --- a/test/SIL/Parser/async.sil +++ b/test/SIL/Parser/async.sil @@ -1,9 +1,11 @@ // RUN: %target-sil-opt -enable-sil-verify-all=true %s | %target-sil-opt -enable-sil-verify-all=true | %FileCheck %s import Builtin +import Swift +import _Concurrency // CHECK: sil @not_async_test : $@convention(thin) () -> () { -sil @not_async_test : $() -> () { +sil @not_async_test : $@convention(thin) () -> () { bb0: %0 = tuple () return %0 : $() @@ -29,3 +31,105 @@ bb0(%fn : $@async () -> ()): %0 = tuple () return %0 : $() } + +// CHECK: sil @async_continuation : $@convention(thin) @async () -> () { +sil @async_continuation : $@async () -> () { +// CHECK-NEXT: bb +entry: + // CHECK-NEXT: [[CONT:%.*]] = get_async_continuation $Builtin.Int32 + %c = get_async_continuation $Builtin.Int32 + // CHECK-NEXT: // function_ref + // CHECK-NEXT: function_ref + %f = function_ref @not_async_test : $@convention(thin) () -> () + // CHECK-NEXT: apply + apply %f() : $@convention(thin) () -> () + // CHECK-NEXT: await_async_continuation [[CONT]] : $UnsafeContinuation, resume [[RESUME:bb[0-9]+]] + await_async_continuation %c : $UnsafeContinuation, resume bb1 + // CHECK-NEXT: {{^ *$}} + // CHECK-NEXT: [[RESUME]]([[RVALUE:%.*]] : $Builtin.Int32): +bb1(%r : $Builtin.Int32): + return undef : $() +} + +// CHECK: sil @async_continuation_throws : $@convention(thin) @async () -> () { +sil @async_continuation_throws : $@async () -> () { +// CHECK-NEXT: bb +entry: + // CHECK-NEXT: [[CONT:%.*]] = get_async_continuation [throws] $Builtin.Int32 + %c = get_async_continuation [throws] $Builtin.Int32 + // CHECK-NEXT: // function_ref + // CHECK-NEXT: function_ref + %f = function_ref @not_async_test : $@convention(thin) () -> () + // CHECK-NEXT: apply + apply %f() : $@convention(thin) () -> () + // CHECK-NEXT: await_async_continuation [[CONT]] : $UnsafeThrowingContinuation, resume [[RESUME:bb[0-9]+]], error [[ERROR:bb[0-9]+]] + await_async_continuation %c : $UnsafeThrowingContinuation, resume bb1, error bb2 + // CHECK-NEXT: {{^ *$}} + // CHECK-NEXT: [[RESUME]]([[RVALUE:%.*]] : $Builtin.Int32): +bb1(%r : $Builtin.Int32): + // CHECK-NEXT: br + br bb3 + // CHECK-NEXT: {{^ *$}} + // CHECK-NEXT: [[ERROR]]([[EVALUE:%.*]] : $Error): +bb2(%e : $Error): + // CHECK-NEXT: br + br bb3 + +bb3: + return undef : $() +} + +// CHECK: sil @async_continuation_addr : $@convention(thin) @async () -> () { +sil @async_continuation_addr : $@async () -> () { +// CHECK-NEXT: bb +entry: + // CHECK: [[SLOT:%.*]] = alloc_stack + %a = alloc_stack $Builtin.Int32 + // CHECK-NEXT: [[CONT:%.*]] = get_async_continuation_addr $Builtin.Int32, [[SLOT]] : $*Builtin.Int32 + %c = get_async_continuation_addr $Builtin.Int32, %a : $*Builtin.Int32 + // CHECK-NEXT: // function_ref + // CHECK-NEXT: function_ref + %f = function_ref @not_async_test : $@convention(thin) () -> () + // CHECK-NEXT: apply + apply %f() : $@convention(thin) () -> () + // CHECK-NEXT: await_async_continuation [[CONT]] : $UnsafeContinuation, resume [[RESUME:bb[0-9]+]] + await_async_continuation %c : $UnsafeContinuation, resume bb1 + // CHECK-NEXT: {{^ *$}} + // CHECK-NEXT: [[RESUME]]: +bb1: + dealloc_stack %a : $*Builtin.Int32 + return undef : $() +} + +// CHECK: sil @async_continuation_throws_addr : $@convention(thin) @async () -> () { +sil @async_continuation_throws_addr : $@async () -> () { +// CHECK-NEXT: bb +entry: + // CHECK: [[SLOT:%.*]] = alloc_stack + %a = alloc_stack $Builtin.Int32 + // CHECK-NEXT: [[CONT:%.*]] = get_async_continuation_addr [throws] $Builtin.Int32, [[SLOT]] : $*Builtin.Int32 + %c = get_async_continuation_addr [throws] $Builtin.Int32, %a : $*Builtin.Int32 + // CHECK-NEXT: // function_ref + // CHECK-NEXT: function_ref + %f = function_ref @not_async_test : $@convention(thin) () -> () + // CHECK-NEXT: apply + apply %f() : $@convention(thin) () -> () + // CHECK-NEXT: await_async_continuation [[CONT]] : $UnsafeThrowingContinuation, resume [[RESUME:bb[0-9]+]], error [[ERROR:bb[0-9]+]] + await_async_continuation %c : $UnsafeThrowingContinuation, resume bb1, error bb2 + // CHECK-NEXT: {{^ *$}} + // CHECK-NEXT: [[RESUME]]: +bb1: + // CHECK-NEXT: dealloc_stack + dealloc_stack %a : $*Builtin.Int32 + // CHECK-NEXT: br + br bb3 + // CHECK-NEXT: {{^ *$}} + // CHECK-NEXT: [[ERROR]]([[EVALUE:%.*]] : $Error): +bb2(%e : $Error): + dealloc_stack %a : $*Builtin.Int32 + br bb3 + +bb3: + return undef : $() +} + diff --git a/test/SIL/Serialization/async.sil b/test/SIL/Serialization/async.sil new file mode 100644 index 0000000000000..f9def530cc70c --- /dev/null +++ b/test/SIL/Serialization/async.sil @@ -0,0 +1,100 @@ +// First parse this and then emit a *.sib. Then read in the *.sib, then recreate +// RUN: %empty-directory(%t) +// RUN: %target-sil-opt %s -emit-sib -o %t/tmp.sib -module-name async +// RUN: %target-sil-opt %t/tmp.sib -module-name async | %FileCheck %s +// REQUIRES: concurrency + +import Builtin +import Swift +import _Concurrency + +sil [serialized] @aaa : $@convention(thin) () -> () { +entry: + %a = function_ref @async_continuation : $@convention(thin) @async () -> () + %b = function_ref @async_continuation_throws : $@convention(thin) @async () -> () + %c = function_ref @async_continuation_addr : $@convention(thin) @async () -> () + %d = function_ref @async_continuation_throws_addr : $@convention(thin) @async () -> () + return undef : $() +} + +// CHECK: sil [serialized] @async_continuation : $@convention(thin) @async () -> () { +sil [serialized] @async_continuation : $@async () -> () { +// CHECK-NEXT: bb +entry: + // CHECK-NEXT: [[CONT:%.*]] = get_async_continuation $Builtin.Int32 + %c = get_async_continuation $Builtin.Int32 + // CHECK-NEXT: await_async_continuation [[CONT]] : $UnsafeContinuation, resume [[RESUME:bb[0-9]+]] + await_async_continuation %c : $UnsafeContinuation, resume bb1 + // CHECK-NEXT: {{^ *$}} + // CHECK-NEXT: [[RESUME]]([[RVALUE:%.*]] : $Builtin.Int32): +bb1(%r : $Builtin.Int32): + return undef : $() +} + +// CHECK: sil [serialized] @async_continuation_throws : $@convention(thin) @async () -> () { +sil [serialized] @async_continuation_throws : $@async () -> () { +// CHECK-NEXT: bb +entry: + // CHECK-NEXT: [[CONT:%.*]] = get_async_continuation [throws] $Builtin.Int32 + %c = get_async_continuation [throws] $Builtin.Int32 + // CHECK-NEXT: await_async_continuation [[CONT]] : $UnsafeThrowingContinuation, resume [[RESUME:bb[0-9]+]], error [[ERROR:bb[0-9]+]] + await_async_continuation %c : $UnsafeThrowingContinuation, resume bb1, error bb2 + // CHECK-NEXT: {{^ *$}} + // CHECK-NEXT: [[ERROR]]([[EVALUE:%.*]] : $Error): +bb2(%e : $Error): + // CHECK-NEXT: br + br bb3 + // CHECK-NEXT: {{^ *$}} + // CHECK-NEXT: [[RESUME]]([[RVALUE:%.*]] : $Builtin.Int32): +bb1(%r : $Builtin.Int32): + // CHECK-NEXT: br + br bb3 + +bb3: + return undef : $() +} + +// CHECK: sil [serialized] @async_continuation_addr : $@convention(thin) @async () -> () { +sil [serialized] @async_continuation_addr : $@async () -> () { +// CHECK-NEXT: bb +entry: + // CHECK: [[SLOT:%.*]] = alloc_stack + %a = alloc_stack $Builtin.Int32 + // CHECK-NEXT: [[CONT:%.*]] = get_async_continuation_addr $Builtin.Int32, [[SLOT]] : $*Builtin.Int32 + %c = get_async_continuation_addr $Builtin.Int32, %a : $*Builtin.Int32 + // CHECK-NEXT: await_async_continuation [[CONT]] : $UnsafeContinuation, resume [[RESUME:bb[0-9]+]] + await_async_continuation %c : $UnsafeContinuation, resume bb1 + // CHECK-NEXT: {{^ *$}} + // CHECK-NEXT: [[RESUME]]: +bb1: + dealloc_stack %a : $*Builtin.Int32 + return undef : $() +} + +// CHECK: sil [serialized] @async_continuation_throws_addr : $@convention(thin) @async () -> () { +sil [serialized] @async_continuation_throws_addr : $@async () -> () { +// CHECK-NEXT: bb +entry: + // CHECK: [[SLOT:%.*]] = alloc_stack + %a = alloc_stack $Builtin.Int32 + // CHECK-NEXT: [[CONT:%.*]] = get_async_continuation_addr [throws] $Builtin.Int32, [[SLOT]] : $*Builtin.Int32 + %c = get_async_continuation_addr [throws] $Builtin.Int32, %a : $*Builtin.Int32 + // CHECK-NEXT: await_async_continuation [[CONT]] : $UnsafeThrowingContinuation, resume [[RESUME:bb[0-9]+]], error [[ERROR:bb[0-9]+]] + await_async_continuation %c : $UnsafeThrowingContinuation, resume bb1, error bb2 + // CHECK-NEXT: {{^ *$}} + // CHECK-NEXT: [[ERROR]]([[EVALUE:%.*]] : $Error): +bb2(%e : $Error): + // CHECK-NEXT: dealloc_stack + dealloc_stack %a : $*Builtin.Int32 + // CHECK-NEXT: br + br bb3 + // CHECK-NEXT: {{^ *$}} + // CHECK-NEXT: [[RESUME]]: +bb1: + dealloc_stack %a : $*Builtin.Int32 + br bb3 + +bb3: + return undef : $() +} + From d576b4e9261d80fdac70ed5f0d84601acdb5c48e Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 23 Sep 2020 17:10:20 -0400 Subject: [PATCH 161/745] Sema: Move preCheckExpression() into its own file --- lib/Sema/CMakeLists.txt | 1 + lib/Sema/PreCheckExpr.cpp | 1882 +++++++++++++++++++++++++++++ lib/Sema/TypeCheckConstraints.cpp | 1857 ---------------------------- 3 files changed, 1883 insertions(+), 1857 deletions(-) create mode 100644 lib/Sema/PreCheckExpr.cpp diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index 6d83a9b33d58b..633a64b014896 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -34,6 +34,7 @@ add_swift_host_library(swiftSema STATIC MiscDiagnostics.cpp PCMacro.cpp PlaygroundTransform.cpp + PreCheckExpr.cpp ResilienceDiagnostics.cpp SourceLoader.cpp TypeCheckAccess.cpp diff --git a/lib/Sema/PreCheckExpr.cpp b/lib/Sema/PreCheckExpr.cpp new file mode 100644 index 0000000000000..7cf0c1a0b0a86 --- /dev/null +++ b/lib/Sema/PreCheckExpr.cpp @@ -0,0 +1,1882 @@ +//===--- PreCheckExpr.cpp - Expression pre-checking pass ------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Pre-checking resolves unqualified name references, type expressions and +// operators. +// +//===----------------------------------------------------------------------===// + +#include "ConstraintSystem.h" +#include "TypeChecker.h" +#include "TypeCheckType.h" +#include "TypoCorrection.h" +#include "swift/AST/ASTVisitor.h" +#include "swift/AST/ASTWalker.h" +#include "swift/AST/DiagnosticsParse.h" +#include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" +#include "swift/AST/ParameterList.h" +#include "swift/AST/PropertyWrappers.h" +#include "swift/AST/ProtocolConformance.h" +#include "swift/AST/SubstitutionMap.h" +#include "swift/AST/TypeCheckRequests.h" +#include "swift/Parse/Confusables.h" +#include "swift/Parse/Lexer.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" + +using namespace swift; +using namespace constraints; + +//===----------------------------------------------------------------------===// +// High-level entry points. +//===----------------------------------------------------------------------===// + +static unsigned getNumArgs(ValueDecl *value) { + if (auto *func = dyn_cast(value)) + return func->getParameters()->size(); + return ~0U; +} + +static bool matchesDeclRefKind(ValueDecl *value, DeclRefKind refKind) { + switch (refKind) { + // An ordinary reference doesn't ignore anything. + case DeclRefKind::Ordinary: + return true; + + // A binary-operator reference only honors FuncDecls with a certain type. + case DeclRefKind::BinaryOperator: + return (getNumArgs(value) == 2); + + case DeclRefKind::PrefixOperator: + return (!value->getAttrs().hasAttribute() && + getNumArgs(value) == 1); + + case DeclRefKind::PostfixOperator: + return (value->getAttrs().hasAttribute() && + getNumArgs(value) == 1); + } + llvm_unreachable("bad declaration reference kind"); +} + +static bool containsDeclRefKind(LookupResult &lookupResult, + DeclRefKind refKind) { + for (auto candidate : lookupResult) { + ValueDecl *D = candidate.getValueDecl(); + if (!D) + continue; + if (matchesDeclRefKind(D, refKind)) + return true; + } + return false; +} + +/// Emit a diagnostic with a fixit hint for an invalid binary operator, showing +/// how to split it according to splitCandidate. +static void diagnoseBinOpSplit(ASTContext &Context, UnresolvedDeclRefExpr *UDRE, + std::pair splitCandidate, + Diag diagID) { + + unsigned splitLoc = splitCandidate.first; + bool isBinOpFirst = splitCandidate.second; + StringRef nameStr = UDRE->getName().getBaseIdentifier().str(); + auto startStr = nameStr.substr(0, splitLoc); + auto endStr = nameStr.drop_front(splitLoc); + + // One valid split found, it is almost certainly the right answer. + auto diag = Context.Diags.diagnose( + UDRE->getLoc(), diagID, Context.getIdentifier(startStr), + Context.getIdentifier(endStr), isBinOpFirst); + // Highlight the whole operator. + diag.highlight(UDRE->getLoc()); + // Insert whitespace on the left if the binop is at the start, or to the + // right if it is end. + if (isBinOpFirst) + diag.fixItInsert(UDRE->getLoc(), " "); + else + diag.fixItInsertAfter(UDRE->getLoc(), " "); + + // Insert a space between the operators. + diag.fixItInsert(UDRE->getLoc().getAdvancedLoc(splitLoc), " "); +} + +/// If we failed lookup of a binary operator, check to see it to see if +/// it is a binary operator juxtaposed with a unary operator (x*-4) that +/// needs whitespace. If so, emit specific diagnostics for it and return true, +/// otherwise return false. +static bool diagnoseOperatorJuxtaposition(UnresolvedDeclRefExpr *UDRE, + DeclContext *DC) { + Identifier name = UDRE->getName().getBaseIdentifier(); + StringRef nameStr = name.str(); + if (!name.isOperator() || nameStr.size() < 2) + return false; + + bool isBinOp = UDRE->getRefKind() == DeclRefKind::BinaryOperator; + + // If this is a binary operator, relex the token, to decide whether it has + // whitespace around it or not. If it does "x +++ y", then it isn't likely to + // be a case where a space was forgotten. + auto &Context = DC->getASTContext(); + if (isBinOp) { + auto tok = Lexer::getTokenAtLocation(Context.SourceMgr, UDRE->getLoc()); + if (tok.getKind() != tok::oper_binary_unspaced) + return false; + } + + // Okay, we have a failed lookup of a multicharacter operator. Check to see if + // lookup succeeds if part is split off, and record the matches found. + // + // In the case of a binary operator, the bool indicated is false if the + // first half of the split is the unary operator (x!*4) or true if it is the + // binary operator (x*+4). + std::vector> WorkableSplits; + + // Check all the potential splits. + for (unsigned splitLoc = 1, e = nameStr.size(); splitLoc != e; ++splitLoc) { + // For it to be a valid split, the start and end section must be valid + // operators, splitting a unicode code point isn't kosher. + auto startStr = nameStr.substr(0, splitLoc); + auto endStr = nameStr.drop_front(splitLoc); + if (!Lexer::isOperator(startStr) || !Lexer::isOperator(endStr)) + continue; + + DeclNameRef startName(Context.getIdentifier(startStr)); + DeclNameRef endName(Context.getIdentifier(endStr)); + + // Perform name lookup for the first and second pieces. If either fail to + // be found, then it isn't a valid split. + auto startLookup = TypeChecker::lookupUnqualified( + DC, startName, UDRE->getLoc(), defaultUnqualifiedLookupOptions); + if (!startLookup) continue; + auto endLookup = TypeChecker::lookupUnqualified(DC, endName, UDRE->getLoc(), + defaultUnqualifiedLookupOptions); + if (!endLookup) continue; + + // If the overall operator is a binary one, then we're looking at + // juxtaposed binary and unary operators. + if (isBinOp) { + // Look to see if the candidates found could possibly match. + if (containsDeclRefKind(startLookup, DeclRefKind::PostfixOperator) && + containsDeclRefKind(endLookup, DeclRefKind::BinaryOperator)) + WorkableSplits.push_back({ splitLoc, false }); + + if (containsDeclRefKind(startLookup, DeclRefKind::BinaryOperator) && + containsDeclRefKind(endLookup, DeclRefKind::PrefixOperator)) + WorkableSplits.push_back({ splitLoc, true }); + } else { + // Otherwise, it is two of the same kind, e.g. "!!x" or "!~x". + if (containsDeclRefKind(startLookup, UDRE->getRefKind()) && + containsDeclRefKind(endLookup, UDRE->getRefKind())) + WorkableSplits.push_back({ splitLoc, false }); + } + } + + switch (WorkableSplits.size()) { + case 0: + // No splits found, can't produce this diagnostic. + return false; + case 1: + // One candidate: produce an error with a fixit on it. + if (isBinOp) + diagnoseBinOpSplit(Context, UDRE, WorkableSplits[0], + diag::unspaced_binary_operator_fixit); + else + Context.Diags.diagnose( + UDRE->getLoc().getAdvancedLoc(WorkableSplits[0].first), + diag::unspaced_unary_operator); + return true; + + default: + // Otherwise, we have to produce a series of notes listing the various + // options. + Context.Diags + .diagnose(UDRE->getLoc(), isBinOp ? diag::unspaced_binary_operator + : diag::unspaced_unary_operator) + .highlight(UDRE->getLoc()); + + if (isBinOp) { + for (auto candidateSplit : WorkableSplits) + diagnoseBinOpSplit(Context, UDRE, candidateSplit, + diag::unspaced_binary_operators_candidate); + } + return true; + } +} + +static bool diagnoseRangeOperatorMisspell(DiagnosticEngine &Diags, + UnresolvedDeclRefExpr *UDRE) { + auto name = UDRE->getName().getBaseIdentifier(); + if (!name.isOperator()) + return false; + + auto corrected = StringRef(); + if (name.str() == ".." || name.str() == "...." || + name.str() == ".…" || name.str() == "…" || name.str() == "….") + corrected = "..."; + else if (name.str() == "...<" || name.str() == "....<" || + name.str() == "…<") + corrected = "..<"; + + if (!corrected.empty()) { + Diags + .diagnose(UDRE->getLoc(), diag::cannot_find_in_scope_corrected, + UDRE->getName(), true, corrected) + .highlight(UDRE->getSourceRange()) + .fixItReplace(UDRE->getSourceRange(), corrected); + + return true; + } + return false; +} + +static bool diagnoseIncDecOperator(DiagnosticEngine &Diags, + UnresolvedDeclRefExpr *UDRE) { + auto name = UDRE->getName().getBaseIdentifier(); + if (!name.isOperator()) + return false; + + auto corrected = StringRef(); + if (name.str() == "++") + corrected = "+= 1"; + else if (name.str() == "--") + corrected = "-= 1"; + + if (!corrected.empty()) { + Diags + .diagnose(UDRE->getLoc(), diag::cannot_find_in_scope_corrected, + UDRE->getName(), true, corrected) + .highlight(UDRE->getSourceRange()); + + return true; + } + return false; +} + +static bool findNonMembers(ArrayRef lookupResults, + DeclRefKind refKind, bool breakOnMember, + SmallVectorImpl &ResultValues, + llvm::function_ref isValid) { + bool AllDeclRefs = true; + for (auto Result : lookupResults) { + // If we find a member, then all of the results aren't non-members. + bool IsMember = + (Result.getBaseDecl() && !isa(Result.getBaseDecl())); + if (IsMember) { + AllDeclRefs = false; + if (breakOnMember) + break; + continue; + } + + ValueDecl *D = Result.getValueDecl(); + if (!isValid(D)) + return false; + + if (matchesDeclRefKind(D, refKind)) + ResultValues.push_back(D); + } + + return AllDeclRefs; +} + +/// Find the next element in a chain of members. If this expression is (or +/// could be) the base of such a chain, this will return \c nullptr. +static Expr *getMemberChainSubExpr(Expr *expr) { + assert(expr && "getMemberChainSubExpr called with null expr!"); + if (auto *UDE = dyn_cast(expr)) { + return UDE->getBase(); + } else if (auto *CE = dyn_cast(expr)) { + return CE->getFn(); + } else if (auto *BOE = dyn_cast(expr)) { + return BOE->getSubExpr(); + } else if (auto *FVE = dyn_cast(expr)) { + return FVE->getSubExpr(); + } else if (auto *SE = dyn_cast(expr)) { + return SE->getBase(); + } else if (auto *CCE = dyn_cast(expr)) { + return CCE->getBase(); + } else { + return nullptr; + } +} + +UnresolvedMemberExpr *TypeChecker::getUnresolvedMemberChainBase(Expr *expr) { + if (auto *subExpr = getMemberChainSubExpr(expr)) + return getUnresolvedMemberChainBase(subExpr); + else + return dyn_cast(expr); +} + +/// Whether this expression is a member of a member chain. +static bool isMemberChainMember(Expr *expr) { + return getMemberChainSubExpr(expr) != nullptr; +} +/// Whether this expression sits at the end of a chain of member accesses. +static bool isMemberChainTail(Expr *expr, Expr *parent) { + assert(expr && "isMemberChainTail called with null expr!"); + // If this expression's parent is not itself part of a chain (or, this expr + // has no parent expr), this must be the tail of the chain. + return parent == nullptr || !isMemberChainMember(parent); +} + +/// Bind an UnresolvedDeclRefExpr by performing name lookup and +/// returning the resultant expression. Context is the DeclContext used +/// for the lookup. +Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, + DeclContext *DC, + bool replaceInvalidRefsWithErrors) { + // Process UnresolvedDeclRefExpr by doing an unqualified lookup. + DeclNameRef Name = UDRE->getName(); + SourceLoc Loc = UDRE->getLoc(); + + auto errorResult = [&]() -> Expr * { + if (replaceInvalidRefsWithErrors) + return new (DC->getASTContext()) ErrorExpr(UDRE->getSourceRange()); + return UDRE; + }; + + // Perform standard value name lookup. + NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; + // TODO: Include all of the possible members to give a solver a + // chance to diagnose name shadowing which requires explicit + // name/module qualifier to access top-level name. + lookupOptions |= NameLookupFlags::IncludeOuterResults; + + if (Loc.isInvalid()) + DC = DC->getModuleScopeContext(); + + auto Lookup = TypeChecker::lookupUnqualified(DC, Name, Loc, lookupOptions); + + auto &Context = DC->getASTContext(); + if (!Lookup) { + // If we failed lookup of an operator, check to see if this is a range + // operator misspelling. Otherwise try to diagnose a juxtaposition + // e.g. (x*-4) that needs whitespace. + if (diagnoseRangeOperatorMisspell(Context.Diags, UDRE) || + diagnoseIncDecOperator(Context.Diags, UDRE) || + diagnoseOperatorJuxtaposition(UDRE, DC)) { + return errorResult(); + } + + // Try ignoring access control. + NameLookupOptions relookupOptions = lookupOptions; + relookupOptions |= NameLookupFlags::IgnoreAccessControl; + auto inaccessibleResults = + TypeChecker::lookupUnqualified(DC, Name, Loc, relookupOptions); + if (inaccessibleResults) { + // FIXME: What if the unviable candidates have different levels of access? + const ValueDecl *first = inaccessibleResults.front().getValueDecl(); + Context.Diags.diagnose( + Loc, diag::candidate_inaccessible, first->getBaseName(), + first->getFormalAccessScope().accessLevelForDiagnostics()); + + // FIXME: If any of the candidates (usually just one) are in the same + // module we could offer a fix-it. + for (auto lookupResult : inaccessibleResults) { + auto *VD = lookupResult.getValueDecl(); + VD->diagnose(diag::decl_declared_here, VD->getName()); + } + + // Don't try to recover here; we'll get more access-related diagnostics + // downstream if the type of the inaccessible decl is also inaccessible. + return errorResult(); + } + + // TODO: Name will be a compound name if it was written explicitly as + // one, but we should also try to propagate labels into this. + DeclNameLoc nameLoc = UDRE->getNameLoc(); + + Identifier simpleName = Name.getBaseIdentifier(); + const char *buffer = simpleName.get(); + llvm::SmallString<64> expectedIdentifier; + bool isConfused = false; + uint32_t codepoint; + uint32_t firstConfusableCodepoint = 0; + int totalCodepoints = 0; + int offset = 0; + while ((codepoint = validateUTF8CharacterAndAdvance(buffer, + buffer + + strlen(buffer))) + != ~0U) { + int length = (buffer - simpleName.get()) - offset; + if (auto expectedCodepoint = + confusable::tryConvertConfusableCharacterToASCII(codepoint)) { + if (firstConfusableCodepoint == 0) { + firstConfusableCodepoint = codepoint; + } + isConfused = true; + expectedIdentifier += expectedCodepoint; + } else { + expectedIdentifier += (char)codepoint; + } + + totalCodepoints++; + + offset += length; + } + + auto emitBasicError = [&] { + Context.Diags + .diagnose(Loc, diag::cannot_find_in_scope, Name, + Name.isOperator()) + .highlight(UDRE->getSourceRange()); + }; + + if (!isConfused) { + if (Name.isSimpleName(Context.Id_Self)) { + if (DeclContext *typeContext = DC->getInnermostTypeContext()){ + Type SelfType = typeContext->getSelfInterfaceType(); + + if (typeContext->getSelfClassDecl()) + SelfType = DynamicSelfType::get(SelfType, Context); + SelfType = DC->mapTypeIntoContext(SelfType); + return new (Context) + TypeExpr(new (Context) FixedTypeRepr(SelfType, Loc)); + } + } + + TypoCorrectionResults corrections(Name, nameLoc); + TypeChecker::performTypoCorrection(DC, UDRE->getRefKind(), Type(), + lookupOptions, corrections); + + if (auto typo = corrections.claimUniqueCorrection()) { + auto diag = Context.Diags.diagnose( + Loc, diag::cannot_find_in_scope_corrected, Name, + Name.isOperator(), typo->CorrectedName.getBaseIdentifier().str()); + diag.highlight(UDRE->getSourceRange()); + typo->addFixits(diag); + } else { + emitBasicError(); + } + + corrections.noteAllCandidates(); + } else { + emitBasicError(); + + if (totalCodepoints == 1) { + auto charNames = confusable::getConfusableAndBaseCodepointNames( + firstConfusableCodepoint); + Context.Diags + .diagnose(Loc, diag::single_confusable_character, + UDRE->getName().isOperator(), simpleName.str(), + charNames.first, expectedIdentifier, charNames.second) + .fixItReplace(Loc, expectedIdentifier); + } else { + Context.Diags + .diagnose(Loc, diag::confusable_character, + UDRE->getName().isOperator(), simpleName.str(), + expectedIdentifier) + .fixItReplace(Loc, expectedIdentifier); + } + } + + // TODO: consider recovering from here. We may want some way to suppress + // downstream diagnostics, though. + + return errorResult(); + } + + // FIXME: Need to refactor the way we build an AST node from a lookup result! + + SmallVector ResultValues; + ValueDecl *localDeclAfterUse = nullptr; + auto isValid = [&](ValueDecl *D) { + // FIXME: The source-location checks won't make sense once + // EnableASTScopeLookup is the default. + // + // Note that we allow forward references to types, because they cannot + // capture. + if (Loc.isValid() && D->getLoc().isValid() && + D->getDeclContext()->isLocalContext() && + D->getDeclContext() == DC && + Context.SourceMgr.isBeforeInBuffer(Loc, D->getLoc()) && + !isa(D)) { + localDeclAfterUse = D; + return false; + } + return true; + }; + bool AllDeclRefs = + findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), + /*breakOnMember=*/true, ResultValues, isValid); + + // If local declaration after use is found, check outer results for + // better matching candidates. + if (localDeclAfterUse) { + auto innerDecl = localDeclAfterUse; + while (localDeclAfterUse) { + if (Lookup.outerResults().empty()) { + Context.Diags.diagnose(Loc, diag::use_local_before_declaration, Name); + Context.Diags.diagnose(innerDecl, diag::decl_declared_here, + localDeclAfterUse->getName()); + Expr *error = new (Context) ErrorExpr(UDRE->getSourceRange()); + return error; + } + + Lookup.shiftDownResults(); + ResultValues.clear(); + localDeclAfterUse = nullptr; + AllDeclRefs = + findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), + /*breakOnMember=*/true, ResultValues, isValid); + } + } + + // If we have an unambiguous reference to a type decl, form a TypeExpr. + if (Lookup.size() == 1 && UDRE->getRefKind() == DeclRefKind::Ordinary && + isa(Lookup[0].getValueDecl())) { + auto *D = cast(Lookup[0].getValueDecl()); + // FIXME: This is odd. + if (isa(D)) { + return new (Context) DeclRefExpr(D, UDRE->getNameLoc(), + /*Implicit=*/false, + AccessSemantics::Ordinary, + D->getInterfaceType()); + } + + auto *LookupDC = Lookup[0].getDeclContext(); + if (UDRE->isImplicit()) { + return TypeExpr::createImplicitForDecl( + UDRE->getNameLoc(), D, LookupDC, + LookupDC->mapTypeIntoContext(D->getInterfaceType())); + } else { + return TypeExpr::createForDecl(UDRE->getNameLoc(), D, LookupDC); + } + } + + if (AllDeclRefs) { + // Diagnose uses of operators that found no matching candidates. + if (ResultValues.empty()) { + assert(UDRE->getRefKind() != DeclRefKind::Ordinary); + Context.Diags.diagnose( + Loc, diag::use_nonmatching_operator, Name, + UDRE->getRefKind() == DeclRefKind::BinaryOperator + ? 0 + : UDRE->getRefKind() == DeclRefKind::PrefixOperator ? 1 : 2); + return new (Context) ErrorExpr(UDRE->getSourceRange()); + } + + // For operators, sort the results so that non-generic operations come + // first. + // Note: this is part of a performance hack to prefer non-generic operators + // to generic operators, because the former is far more efficient to check. + if (UDRE->getRefKind() != DeclRefKind::Ordinary) { + std::stable_sort(ResultValues.begin(), ResultValues.end(), + [&](ValueDecl *x, ValueDecl *y) -> bool { + auto xGeneric = x->getInterfaceType()->getAs(); + auto yGeneric = y->getInterfaceType()->getAs(); + if (static_cast(xGeneric) != static_cast(yGeneric)) { + return xGeneric? false : true; + } + + if (!xGeneric) + return false; + + unsigned xDepth = xGeneric->getGenericParams().back()->getDepth(); + unsigned yDepth = yGeneric->getGenericParams().back()->getDepth(); + return xDepth < yDepth; + }); + } + + return buildRefExpr(ResultValues, DC, UDRE->getNameLoc(), + UDRE->isImplicit(), UDRE->getFunctionRefKind()); + } + + ResultValues.clear(); + bool AllMemberRefs = true; + ValueDecl *Base = nullptr; + DeclContext *BaseDC = nullptr; + for (auto Result : Lookup) { + auto ThisBase = Result.getBaseDecl(); + + // Track the base for member declarations. + if (ThisBase && !isa(ThisBase)) { + auto Value = Result.getValueDecl(); + ResultValues.push_back(Value); + if (Base && ThisBase != Base) { + AllMemberRefs = false; + break; + } + + Base = ThisBase; + BaseDC = Result.getDeclContext(); + continue; + } + + AllMemberRefs = false; + break; + } + + if (AllMemberRefs) { + Expr *BaseExpr; + if (auto PD = dyn_cast(Base)) { + auto selfParam = PD->getGenericParams()->getParams().front(); + BaseExpr = TypeExpr::createImplicitForDecl( + UDRE->getNameLoc(), selfParam, + /*DC*/ nullptr, + DC->mapTypeIntoContext(selfParam->getInterfaceType())); + } else if (auto NTD = dyn_cast(Base)) { + BaseExpr = TypeExpr::createImplicitForDecl( + UDRE->getNameLoc(), NTD, BaseDC, + DC->mapTypeIntoContext(NTD->getInterfaceType())); + } else { + BaseExpr = new (Context) DeclRefExpr(Base, UDRE->getNameLoc(), + /*Implicit=*/true); + } + + llvm::SmallVector outerAlternatives; + (void)findNonMembers(Lookup.outerResults(), UDRE->getRefKind(), + /*breakOnMember=*/false, outerAlternatives, + /*isValid=*/[](ValueDecl *choice) -> bool { + return !choice->isInvalid(); + }); + + // Otherwise, form an UnresolvedDotExpr and sema will resolve it based on + // type information. + return new (Context) UnresolvedDotExpr( + BaseExpr, SourceLoc(), Name, UDRE->getNameLoc(), UDRE->isImplicit(), + Context.AllocateCopy(outerAlternatives)); + } + + // FIXME: If we reach this point, the program we're being handed is likely + // very broken, but it's still conceivable that this may happen due to + // invalid shadowed declarations. + // + // Make sure we emit a diagnostic, since returning an ErrorExpr without + // producing one will break things downstream. + Context.Diags.diagnose(Loc, diag::ambiguous_decl_ref, Name); + for (auto Result : Lookup) { + auto *Decl = Result.getValueDecl(); + Context.Diags.diagnose(Decl, diag::decl_declared_here, Decl->getName()); + } + return new (Context) ErrorExpr(UDRE->getSourceRange()); +} + +/// If an expression references 'self.init' or 'super.init' in an +/// initializer context, returns the implicit 'self' decl of the constructor. +/// Otherwise, return nil. +VarDecl * +TypeChecker::getSelfForInitDelegationInConstructor(DeclContext *DC, + UnresolvedDotExpr *ctorRef) { + // If the reference isn't to a constructor, we're done. + if (ctorRef->getName().getBaseName() != DeclBaseName::createConstructor()) + return nullptr; + + if (auto ctorContext = + dyn_cast_or_null(DC->getInnermostMethodContext())) { + auto nestedArg = ctorRef->getBase(); + if (auto inout = dyn_cast(nestedArg)) + nestedArg = inout->getSubExpr(); + if (nestedArg->isSuperExpr()) + return ctorContext->getImplicitSelfDecl(); + if (auto declRef = dyn_cast(nestedArg)) + if (declRef->getDecl()->getName() == DC->getASTContext().Id_self) + return ctorContext->getImplicitSelfDecl(); + } + return nullptr; +} + +namespace { + /// Update the function reference kind based on adding a direct call to a + /// callee with this kind. + FunctionRefKind addingDirectCall(FunctionRefKind kind) { + switch (kind) { + case FunctionRefKind::Unapplied: + return FunctionRefKind::SingleApply; + + case FunctionRefKind::SingleApply: + case FunctionRefKind::DoubleApply: + return FunctionRefKind::DoubleApply; + + case FunctionRefKind::Compound: + return FunctionRefKind::Compound; + } + + llvm_unreachable("Unhandled FunctionRefKind in switch."); + } + + /// Update a direct callee expression node that has a function reference kind + /// based on seeing a call to this callee. + templategetFunctionRefKind())> + void tryUpdateDirectCalleeImpl(E *callee, int) { + callee->setFunctionRefKind(addingDirectCall(callee->getFunctionRefKind())); + } + + /// Version of tryUpdateDirectCalleeImpl for when the callee + /// expression type doesn't carry a reference. + template + void tryUpdateDirectCalleeImpl(E *callee, ...) { } + + /// The given expression is the direct callee of a call expression; mark it to + /// indicate that it has been called. + void markDirectCallee(Expr *callee) { + while (true) { + // Look through identity expressions. + if (auto identity = dyn_cast(callee)) { + callee = identity->getSubExpr(); + continue; + } + + // Look through unresolved 'specialize' expressions. + if (auto specialize = dyn_cast(callee)) { + callee = specialize->getSubExpr(); + continue; + } + + // Look through optional binding. + if (auto bindOptional = dyn_cast(callee)) { + callee = bindOptional->getSubExpr(); + continue; + } + + // Look through forced binding. + if (auto force = dyn_cast(callee)) { + callee = force->getSubExpr(); + continue; + } + + // Calls compose. + if (auto call = dyn_cast(callee)) { + callee = call->getFn(); + continue; + } + + // We're done. + break; + } + + // Cast the callee to its most-specific class, then try to perform an + // update. If the expression node has a declaration reference in it, the + // update will succeed. Otherwise, we're done propagating. + switch (callee->getKind()) { +#define EXPR(Id, Parent) \ + case ExprKind::Id: \ + tryUpdateDirectCalleeImpl(cast(callee), 0); \ + break; +#include "swift/AST/ExprNodes.def" + } + } + + class PreCheckExpression : public ASTWalker { + ASTContext &Ctx; + DeclContext *DC; + + Expr *ParentExpr; + + /// Indicates whether pre-check is allowed to insert + /// implicit `ErrorExpr` in place of invalid references. + bool UseErrorExprs; + + /// A stack of expressions being walked, used to determine where to + /// insert RebindSelfInConstructorExpr nodes. + llvm::SmallVector ExprStack; + + /// The 'self' variable to use when rebinding 'self' in a constructor. + VarDecl *UnresolvedCtorSelf = nullptr; + + /// The expression that will be wrapped by a RebindSelfInConstructorExpr + /// node when visited. + Expr *UnresolvedCtorRebindTarget = nullptr; + + /// The expressions that are direct arguments of call expressions. + llvm::SmallPtrSet CallArgs; + + /// Simplify expressions which are type sugar productions that got parsed + /// as expressions due to the parser not knowing which identifiers are + /// type names. + TypeExpr *simplifyTypeExpr(Expr *E); + + /// Simplify unresolved dot expressions which are nested type productions. + TypeExpr *simplifyNestedTypeExpr(UnresolvedDotExpr *UDE); + + TypeExpr *simplifyUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *USE); + + /// Simplify a key path expression into a canonical form. + void resolveKeyPathExpr(KeyPathExpr *KPE); + + /// Simplify constructs like `UInt32(1)` into `1 as UInt32` if + /// the type conforms to the expected literal protocol. + Expr *simplifyTypeConstructionWithLiteralArg(Expr *E); + + /// In Swift < 5, diagnose and correct invalid multi-argument or + /// argument-labeled interpolations. + void correctInterpolationIfStrange(InterpolatedStringLiteralExpr *ISLE) { + // These expressions are valid in Swift 5+. + if (getASTContext().isSwiftVersionAtLeast(5)) + return; + + /// Diagnoses appendInterpolation(...) calls with multiple + /// arguments or argument labels and corrects them. + class StrangeInterpolationRewriter : public ASTWalker { + ASTContext &Context; + + public: + StrangeInterpolationRewriter(ASTContext &Ctx) : Context(Ctx) {} + + virtual bool walkToDeclPre(Decl *D) override { + // We don't want to look inside decls. + return false; + } + + virtual std::pair walkToExprPre(Expr *E) override { + // One InterpolatedStringLiteralExpr should never be nested inside + // another except as a child of a CallExpr, and we don't recurse into + // the children of CallExprs. + assert(!isa(E) && + "StrangeInterpolationRewriter found nested interpolation?"); + + // We only care about CallExprs. + if (!isa(E)) + return { true, E }; + + auto call = cast(E); + if (auto callee = dyn_cast(call->getFn())) { + if (callee->getName().getBaseName() == + Context.Id_appendInterpolation) { + Expr *newArg = nullptr; + SourceLoc lParen, rParen; + + if (call->getNumArguments() > 1) { + auto *args = cast(call->getArg()); + + lParen = args->getLParenLoc(); + rParen = args->getRParenLoc(); + Expr *secondArg = args->getElement(1); + + Context.Diags + .diagnose(secondArg->getLoc(), + diag::string_interpolation_list_changing) + .highlightChars(secondArg->getLoc(), rParen); + Context.Diags + .diagnose(secondArg->getLoc(), + diag::string_interpolation_list_insert_parens) + .fixItInsertAfter(lParen, "(") + .fixItInsert(rParen, ")"); + + newArg = args; + } + else if(call->getNumArguments() == 1 && + call->getArgumentLabels().front() != Identifier()) { + auto *args = cast(call->getArg()); + newArg = args->getElement(0); + + lParen = args->getLParenLoc(); + rParen = args->getRParenLoc(); + + SourceLoc argLabelLoc = call->getArgumentLabelLoc(0), + argLoc = newArg->getStartLoc(); + + Context.Diags + .diagnose(argLabelLoc, + diag::string_interpolation_label_changing) + .highlightChars(argLabelLoc, argLoc); + Context.Diags + .diagnose(argLabelLoc, + diag::string_interpolation_remove_label, + call->getArgumentLabels().front()) + .fixItRemoveChars(argLabelLoc, argLoc); + } + + // If newArg is no longer null, we need to build a new + // appendInterpolation(_:) call that takes it to replace the bad + // appendInterpolation(...) call. + if (newArg) { + auto newCallee = new (Context) UnresolvedDotExpr( + callee->getBase(), /*dotloc=*/SourceLoc(), + DeclNameRef(Context.Id_appendInterpolation), + /*nameloc=*/DeclNameLoc(), /*Implicit=*/true); + + E = CallExpr::create(Context, newCallee, lParen, {newArg}, + {Identifier()}, {SourceLoc()}, rParen, + /*trailingClosures=*/{}, + /*implicit=*/false); + } + } + } + + // There is never a CallExpr between an InterpolatedStringLiteralExpr + // and an un-typechecked appendInterpolation(...) call, so whether we + // changed E or not, we don't need to recurse any deeper. + return { false, E }; + } + }; + + ISLE->getAppendingExpr()->walk( + StrangeInterpolationRewriter(getASTContext())); + } + + public: + PreCheckExpression(DeclContext *dc, Expr *parent, + bool replaceInvalidRefsWithErrors) + : Ctx(dc->getASTContext()), DC(dc), ParentExpr(parent), + UseErrorExprs(replaceInvalidRefsWithErrors) {} + + ASTContext &getASTContext() const { return Ctx; } + + bool walkToClosureExprPre(ClosureExpr *expr); + + bool shouldWalkCaptureInitializerExpressions() override { return true; } + + VarDecl *getImplicitSelfDeclForSuperContext(SourceLoc Loc) { + auto *methodContext = DC->getInnermostMethodContext(); + if (!methodContext) { + Ctx.Diags.diagnose(Loc, diag::super_not_in_class_method); + return nullptr; + } + + // Do an actual lookup for 'self' in case it shows up in a capture list. + auto *methodSelf = methodContext->getImplicitSelfDecl(); + auto *lookupSelf = ASTScope::lookupSingleLocalDecl(DC->getParentSourceFile(), + Ctx.Id_self, Loc); + if (lookupSelf && lookupSelf != methodSelf) { + // FIXME: This is the wrong diagnostic for if someone manually declares a + // variable named 'self' using backticks. + Ctx.Diags.diagnose(Loc, diag::super_in_closure_with_capture); + Ctx.Diags.diagnose(lookupSelf->getLoc(), + diag::super_in_closure_with_capture_here); + return nullptr; + } + + return methodSelf; + } + + std::pair walkToExprPre(Expr *expr) override { + // If this is a call, record the argument expression. + if (auto call = dyn_cast(expr)) { + if (!isa(expr)) { + CallArgs.insert(call->getArg()); + } + } + + // FIXME(diagnostics): `InOutType` could appear here as a result + // of successful re-typecheck of the one of the sub-expressions e.g. + // `let _: Int = { (s: inout S) in s.bar() }`. On the first + // attempt to type-check whole expression `s.bar()` - is going + // to have a base which points directly to declaration of `S`. + // But when diagnostics attempts to type-check `s.bar()` standalone + // its base would be tranformed into `InOutExpr -> DeclRefExr`, + // and `InOutType` is going to be recorded in constraint system. + // One possible way to fix this (if diagnostics still use typecheck) + // might be to make it so self is not wrapped into `InOutExpr` + // but instead used as @lvalue type in some case of mutable members. + if (!expr->isImplicit()) { + if (isa(expr) || isa(expr)) { + auto *LE = cast(expr); + if (auto *IOE = dyn_cast(LE->getBase())) + LE->setBase(IOE->getSubExpr()); + } + + if (auto *DSCE = dyn_cast(expr)) { + if (auto *IOE = dyn_cast(DSCE->getBase())) + DSCE->setBase(IOE->getSubExpr()); + } + } + + // Local function used to finish up processing before returning. Every + // return site should call through here. + auto finish = [&](bool recursive, Expr *expr) { + // If we're going to recurse, record this expression on the stack. + if (recursive) + ExprStack.push_back(expr); + + return std::make_pair(recursive, expr); + }; + + // Resolve 'super' references. + if (auto *superRef = dyn_cast(expr)) { + auto loc = superRef->getLoc(); + auto *selfDecl = getImplicitSelfDeclForSuperContext(loc); + if (selfDecl == nullptr) + return finish(true, new (Ctx) ErrorExpr(loc)); + + superRef->setSelf(selfDecl); + return finish(true, superRef); + } + + // For closures, type-check the patterns and result type as written, + // but do not walk into the body. That will be type-checked after + // we've determine the complete function type. + if (auto closure = dyn_cast(expr)) + return finish(walkToClosureExprPre(closure), expr); + + if (auto unresolved = dyn_cast(expr)) { + TypeChecker::checkForForbiddenPrefix( + getASTContext(), unresolved->getName().getBaseName()); + return finish(true, TypeChecker::resolveDeclRefExpr(unresolved, DC, + UseErrorExprs)); + } + + // Let's try to figure out if `InOutExpr` is out of place early + // otherwise there is a risk of producing solutions which can't + // be later applied to AST and would result in the crash in some + // cases. Such expressions are only allowed in argument positions + // of function/operator calls. + if (isa(expr)) { + // If this is an implicit `inout` expression we assume that + // compiler knowns what it's doing. + if (expr->isImplicit()) + return finish(true, expr); + + auto parents = ParentExpr->getParentMap(); + + auto result = parents.find(expr); + if (result != parents.end()) { + auto *parent = result->getSecond(); + + if (isa(parent)) + return finish(true, expr); + + if (isa(parent) || isa(parent)) { + auto call = parents.find(parent); + if (call != parents.end()) { + if (isa(call->getSecond()) || + isa(call->getSecond())) + return finish(true, expr); + + if (isa(call->getSecond())) { + getASTContext().Diags.diagnose( + expr->getStartLoc(), + diag::cannot_pass_inout_arg_to_subscript); + return finish(false, nullptr); + } + } + } + } + + getASTContext().Diags.diagnose(expr->getStartLoc(), + diag::extraneous_address_of); + return finish(false, nullptr); + } + + if (auto *ISLE = dyn_cast(expr)) + correctInterpolationIfStrange(ISLE); + + return finish(true, expr); + } + + Expr *walkToExprPost(Expr *expr) override { + // Remove this expression from the stack. + assert(ExprStack.back() == expr); + ExprStack.pop_back(); + + // Mark the direct callee as being a callee. + if (auto *call = dyn_cast(expr)) + markDirectCallee(call->getFn()); + + // Fold sequence expressions. + if (auto *seqExpr = dyn_cast(expr)) { + auto result = TypeChecker::foldSequence(seqExpr, DC); + return result->walk(*this); + } + + // Type check the type parameters in an UnresolvedSpecializeExpr. + if (auto *us = dyn_cast(expr)) { + if (auto *typeExpr = simplifyUnresolvedSpecializeExpr(us)) + return typeExpr; + } + + // If we're about to step out of a ClosureExpr, restore the DeclContext. + if (auto *ce = dyn_cast(expr)) { + assert(DC == ce && "DeclContext imbalance"); + DC = ce->getParent(); + } + + // A 'self.init' or 'super.init' application inside a constructor will + // evaluate to void, with the initializer's result implicitly rebound + // to 'self'. Recognize the unresolved constructor expression and + // determine where to place the RebindSelfInConstructorExpr node. + // When updating this logic, also update + // RebindSelfInConstructorExpr::getCalledConstructor. + auto &ctx = getASTContext(); + if (auto unresolvedDot = dyn_cast(expr)) { + if (auto self = TypeChecker::getSelfForInitDelegationInConstructor( + DC, unresolvedDot)) { + // Walk our ancestor expressions looking for the appropriate place + // to insert the RebindSelfInConstructorExpr. + Expr *target = nullptr; + bool foundApply = false; + bool foundRebind = false; + for (auto ancestor : llvm::reverse(ExprStack)) { + if (isa(ancestor)) { + // If we already have a rebind, then we're re-typechecking an + // expression and are done. + foundRebind = true; + break; + } + + // Recognize applications. + if (auto apply = dyn_cast(ancestor)) { + // If we already saw an application, we're done. + if (foundApply) + break; + + // If the function being called is not our unresolved initializer + // reference, we're done. + if (apply->getSemanticFn() != unresolvedDot) + break; + + foundApply = true; + target = ancestor; + continue; + } + + // Look through identity, force-value, and 'try' expressions. + if (isa(ancestor) || + isa(ancestor) || + isa(ancestor)) { + if (!CallArgs.count(ancestor)) { + if (target) + target = ancestor; + continue; + } + } + + // No other expression kinds are permitted. + break; + } + + // If we found a rebind target, note the insertion point. + if (target && !foundRebind) { + UnresolvedCtorRebindTarget = target; + UnresolvedCtorSelf = self; + } + } + } + + // If the expression we've found is the intended target of an + // RebindSelfInConstructorExpr, wrap it in the + // RebindSelfInConstructorExpr. + if (expr == UnresolvedCtorRebindTarget) { + expr = new (ctx) + RebindSelfInConstructorExpr(expr, UnresolvedCtorSelf); + UnresolvedCtorRebindTarget = nullptr; + return expr; + } + + // Double check if there are any BindOptionalExpr remaining in the + // tree (see comment below for more details), if there are no BOE + // expressions remaining remove OptionalEvaluationExpr from the tree. + if (auto OEE = dyn_cast(expr)) { + bool hasBindOptional = false; + OEE->forEachChildExpr([&](Expr *expr) -> Expr * { + if (isa(expr)) + hasBindOptional = true; + // If at least a single BOE was found, no reason + // to walk any further in the tree. + return hasBindOptional ? nullptr : expr; + }); + + return hasBindOptional ? OEE : OEE->getSubExpr(); + } + + // Check if there are any BindOptionalExpr in the tree which + // wrap DiscardAssignmentExpr, such situation corresponds to syntax + // like - `_? = `, since it doesn't really make + // sense to have optional assignment to discarded LValue which can + // never be optional, we can remove BOE from the tree and avoid + // generating any of the unnecessary constraints. + if (auto BOE = dyn_cast(expr)) { + if (auto DAE = dyn_cast(BOE->getSubExpr())) + return DAE; + } + + // If this is a sugared type that needs to be folded into a single + // TypeExpr, do it. + if (auto *simplified = simplifyTypeExpr(expr)) + return simplified; + + if (auto KPE = dyn_cast(expr)) { + resolveKeyPathExpr(KPE); + return KPE; + } + + if (auto *simplified = simplifyTypeConstructionWithLiteralArg(expr)) + return simplified; + + // If we find an unresolved member chain, wrap it in an + // UnresolvedMemberChainResultExpr (unless this has already been done). + auto *parent = Parent.getAsExpr(); + if (isMemberChainTail(expr, parent)) + if (auto *UME = TypeChecker::getUnresolvedMemberChainBase(expr)) + if (!parent || !isa(parent)) + return new (ctx) UnresolvedMemberChainResultExpr(expr, UME); + + return expr; + } + + std::pair walkToStmtPre(Stmt *stmt) override { + return { true, stmt }; + } + }; +} // end anonymous namespace + +/// Perform prechecking of a ClosureExpr before we dive into it. This returns +/// true when we want the body to be considered part of this larger expression. +bool PreCheckExpression::walkToClosureExprPre(ClosureExpr *closure) { + auto *PL = closure->getParameters(); + + // Validate the parameters. + bool hadParameterError = false; + + // If we encounter an error validating the parameter list, don't bail. + // Instead, go on to validate any potential result type, and bail + // afterwards. This allows for better diagnostics, and keeps the + // closure expression type well-formed. + for (auto param : *PL) { + hadParameterError |= param->isInvalid(); + } + + if (hadParameterError) + return false; + + // If we won't be checking the body of the closure, don't walk into it here. + if (!shouldTypeCheckInEnclosingExpression(closure)) + return false; + + // Update the current DeclContext to be the closure we're about to + // recurse into. + assert((closure->getParent() == DC || + closure->getParent()->isChildContextOf(DC)) && + "Decl context isn't correct"); + DC = closure; + return true; +} + +TypeExpr *PreCheckExpression::simplifyNestedTypeExpr(UnresolvedDotExpr *UDE) { + if (!UDE->getName().isSimpleName() || + UDE->getName().isSpecial()) + return nullptr; + + auto Name = UDE->getName(); + auto NameLoc = UDE->getNameLoc().getBaseNameLoc(); + + // Qualified type lookup with a module base is represented as a DeclRefExpr + // and not a TypeExpr. + if (auto *DRE = dyn_cast(UDE->getBase())) { + if (auto *TD = dyn_cast(DRE->getDecl())) { + // See if the type has a member type with this name. + auto Result = TypeChecker::lookupMemberType( + DC, TD->getDeclaredInterfaceType(), Name, + defaultMemberLookupOptions); + + // If there is no nested type with this name, we have a lookup of + // a non-type member, so leave the expression as-is. + if (Result.size() == 1) { + return TypeExpr::createForMemberDecl( + DRE->getNameLoc(), TD, UDE->getNameLoc(), Result.front().Member); + } + } + + return nullptr; + } + + auto *TyExpr = dyn_cast(UDE->getBase()); + if (!TyExpr) + return nullptr; + + auto *InnerTypeRepr = TyExpr->getTypeRepr(); + if (!InnerTypeRepr) + return nullptr; + + // Fold 'T.Protocol' into a protocol metatype. + if (Name.isSimpleName(getASTContext().Id_Protocol)) { + auto *NewTypeRepr = + new (getASTContext()) ProtocolTypeRepr(InnerTypeRepr, NameLoc); + return new (getASTContext()) TypeExpr(NewTypeRepr); + } + + // Fold 'T.Type' into an existential metatype if 'T' is a protocol, + // or an ordinary metatype otherwise. + if (Name.isSimpleName(getASTContext().Id_Type)) { + auto *NewTypeRepr = + new (getASTContext()) MetatypeTypeRepr(InnerTypeRepr, NameLoc); + return new (getASTContext()) TypeExpr(NewTypeRepr); + } + + // Fold 'T.U' into a nested type. + if (auto *ITR = dyn_cast(InnerTypeRepr)) { + // Resolve the TypeRepr to get the base type for the lookup. + // Disable availability diagnostics here, because the final + // TypeRepr will be resolved again when generating constraints. + const auto options = + TypeResolutionOptions(TypeResolverContext::InExpression) | + TypeResolutionFlags::AllowUnavailable; + const auto resolution = + TypeResolution::forContextual(DC, options, [](auto unboundTy) { + // FIXME: Don't let unbound generic types escape type resolution. + // For now, just return the unbound generic type. + return unboundTy; + }); + const auto BaseTy = resolution.resolveType(InnerTypeRepr); + + if (BaseTy->mayHaveMembers()) { + // See if there is a member type with this name. + auto Result = + TypeChecker::lookupMemberType(DC, BaseTy, Name, + defaultMemberLookupOptions); + + // If there is no nested type with this name, we have a lookup of + // a non-type member, so leave the expression as-is. + if (Result.size() == 1) { + return TypeExpr::createForMemberDecl(ITR, UDE->getNameLoc(), + Result.front().Member); + } + } + } + + return nullptr; +} + +TypeExpr *PreCheckExpression::simplifyUnresolvedSpecializeExpr( + UnresolvedSpecializeExpr *us) { + // If this is a reference type a specialized type, form a TypeExpr. + // The base should be a TypeExpr that we already resolved. + if (auto *te = dyn_cast(us->getSubExpr())) { + if (auto *ITR = dyn_cast_or_null(te->getTypeRepr())) { + return TypeExpr::createForSpecializedDecl(ITR, + us->getUnresolvedParams(), + SourceRange(us->getLAngleLoc(), + us->getRAngleLoc()), + getASTContext()); + } + } + + return nullptr; +} + +/// Simplify expressions which are type sugar productions that got parsed +/// as expressions due to the parser not knowing which identifiers are +/// type names. +TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { + // Don't try simplifying a call argument, because we don't want to + // simplify away the required ParenExpr/TupleExpr. + if (CallArgs.count(E) > 0) return nullptr; + + // Fold member types. + if (auto *UDE = dyn_cast(E)) { + return simplifyNestedTypeExpr(UDE); + } + + // Fold T? into an optional type when T is a TypeExpr. + if (isa(E) || isa(E)) { + TypeExpr *TyExpr; + SourceLoc QuestionLoc; + if (auto *OOE = dyn_cast(E)) { + TyExpr = dyn_cast(OOE->getSubExpr()); + QuestionLoc = OOE->getLoc(); + } else { + TyExpr = dyn_cast(cast(E)->getSubExpr()); + QuestionLoc = cast(E)->getQuestionLoc(); + } + if (!TyExpr) return nullptr; + + auto *InnerTypeRepr = TyExpr->getTypeRepr(); + assert(!TyExpr->isImplicit() && InnerTypeRepr && + "This doesn't work on implicit TypeExpr's, " + "the TypeExpr should have been built correctly in the first place"); + + // The optional evaluation is passed through. + if (isa(E)) + return TyExpr; + + auto *NewTypeRepr = + new (getASTContext()) OptionalTypeRepr(InnerTypeRepr, QuestionLoc); + return new (getASTContext()) TypeExpr(NewTypeRepr); + } + + // Fold T! into an IUO type when T is a TypeExpr. + if (auto *FVE = dyn_cast(E)) { + auto *TyExpr = dyn_cast(FVE->getSubExpr()); + if (!TyExpr) return nullptr; + + auto *InnerTypeRepr = TyExpr->getTypeRepr(); + assert(!TyExpr->isImplicit() && InnerTypeRepr && + "This doesn't work on implicit TypeExpr's, " + "the TypeExpr should have been built correctly in the first place"); + + auto *NewTypeRepr = new (getASTContext()) + ImplicitlyUnwrappedOptionalTypeRepr(InnerTypeRepr, + FVE->getExclaimLoc()); + return new (getASTContext()) TypeExpr(NewTypeRepr); + } + + // Fold (T) into a type T with parens around it. + if (auto *PE = dyn_cast(E)) { + auto *TyExpr = dyn_cast(PE->getSubExpr()); + if (!TyExpr) return nullptr; + + TupleTypeReprElement InnerTypeRepr[] = { TyExpr->getTypeRepr() }; + assert(!TyExpr->isImplicit() && InnerTypeRepr[0].Type && + "SubscriptExpr doesn't work on implicit TypeExpr's, " + "the TypeExpr should have been built correctly in the first place"); + + auto *NewTypeRepr = TupleTypeRepr::create(getASTContext(), InnerTypeRepr, + PE->getSourceRange()); + return new (getASTContext()) TypeExpr(NewTypeRepr); + } + + // Fold a tuple expr like (T1,T2) into a tuple type (T1,T2). + if (auto *TE = dyn_cast(E)) { + if (TE->hasTrailingClosure() || + // FIXME: Decide what to do about (). It could be a type or an expr. + TE->getNumElements() == 0) + return nullptr; + + SmallVector Elts; + unsigned EltNo = 0; + for (auto Elt : TE->getElements()) { + auto *eltTE = dyn_cast(Elt); + if (!eltTE) return nullptr; + TupleTypeReprElement elt; + assert(eltTE->getTypeRepr() && !eltTE->isImplicit() && + "This doesn't work on implicit TypeExpr's, the " + "TypeExpr should have been built correctly in the first place"); + + // If the tuple element has a label, propagate it. + elt.Type = eltTE->getTypeRepr(); + Identifier name = TE->getElementName(EltNo); + if (!name.empty()) { + elt.Name = name; + elt.NameLoc = TE->getElementNameLoc(EltNo); + } + + Elts.push_back(elt); + ++EltNo; + } + auto *NewTypeRepr = TupleTypeRepr::create( + getASTContext(), Elts, TE->getSourceRange(), SourceLoc(), Elts.size()); + return new (getASTContext()) TypeExpr(NewTypeRepr); + } + + + // Fold [T] into an array type. + if (auto *AE = dyn_cast(E)) { + if (AE->getElements().size() != 1) + return nullptr; + + auto *TyExpr = dyn_cast(AE->getElement(0)); + if (!TyExpr) + return nullptr; + + auto *NewTypeRepr = new (getASTContext()) + ArrayTypeRepr(TyExpr->getTypeRepr(), + SourceRange(AE->getLBracketLoc(), AE->getRBracketLoc())); + return new (getASTContext()) TypeExpr(NewTypeRepr); + } + + // Fold [K : V] into a dictionary type. + if (auto *DE = dyn_cast(E)) { + if (DE->getElements().size() != 1) + return nullptr; + + TypeRepr *keyTypeRepr, *valueTypeRepr; + + if (auto EltTuple = dyn_cast(DE->getElement(0))) { + auto *KeyTyExpr = dyn_cast(EltTuple->getElement(0)); + if (!KeyTyExpr) + return nullptr; + + auto *ValueTyExpr = dyn_cast(EltTuple->getElement(1)); + if (!ValueTyExpr) + return nullptr; + + keyTypeRepr = KeyTyExpr->getTypeRepr(); + valueTypeRepr = ValueTyExpr->getTypeRepr(); + } else { + auto *TE = dyn_cast(DE->getElement(0)); + if (!TE) return nullptr; + + auto *TRE = dyn_cast_or_null(TE->getTypeRepr()); + if (!TRE || TRE->getEllipsisLoc().isValid()) return nullptr; + while (TRE->isParenType()) { + TRE = dyn_cast_or_null(TRE->getElementType(0)); + if (!TRE || TRE->getEllipsisLoc().isValid()) return nullptr; + } + + assert(TRE->getElements().size() == 2); + keyTypeRepr = TRE->getElementType(0); + valueTypeRepr = TRE->getElementType(1); + } + + auto *NewTypeRepr = new (getASTContext()) DictionaryTypeRepr( + keyTypeRepr, valueTypeRepr, + /*FIXME:colonLoc=*/SourceLoc(), + SourceRange(DE->getLBracketLoc(), DE->getRBracketLoc())); + return new (getASTContext()) TypeExpr(NewTypeRepr); + } + + // Reinterpret arrow expr T1 -> T2 as function type. + // FIXME: support 'inout', etc. + if (auto *AE = dyn_cast(E)) { + if (!AE->isFolded()) return nullptr; + + auto diagnoseMissingParens = [](ASTContext &ctx, TypeRepr *tyR) { + bool isVoid = false; + if (const auto Void = dyn_cast(tyR)) { + if (Void->getNameRef().isSimpleName(ctx.Id_Void)) { + isVoid = true; + } + } + + if (isVoid) { + ctx.Diags.diagnose(tyR->getStartLoc(), diag::function_type_no_parens) + .fixItReplace(tyR->getStartLoc(), "()"); + } else { + ctx.Diags.diagnose(tyR->getStartLoc(), diag::function_type_no_parens) + .highlight(tyR->getSourceRange()) + .fixItInsert(tyR->getStartLoc(), "(") + .fixItInsertAfter(tyR->getEndLoc(), ")"); + } + }; + + auto &ctx = getASTContext(); + auto extractInputTypeRepr = [&](Expr *E) -> TupleTypeRepr * { + if (!E) + return nullptr; + if (auto *TyE = dyn_cast(E)) { + auto ArgRepr = TyE->getTypeRepr(); + if (auto *TTyRepr = dyn_cast(ArgRepr)) + return TTyRepr; + diagnoseMissingParens(ctx, ArgRepr); + return TupleTypeRepr::create(ctx, {ArgRepr}, ArgRepr->getSourceRange()); + } + if (auto *TE = dyn_cast(E)) + if (TE->getNumElements() == 0) + return TupleTypeRepr::createEmpty(getASTContext(), + TE->getSourceRange()); + + // When simplifying a type expr like "(P1 & P2) -> (P3 & P4) -> Int", + // it may have been folded at the same time; recursively simplify it. + if (auto ArgsTypeExpr = simplifyTypeExpr(E)) { + auto ArgRepr = ArgsTypeExpr->getTypeRepr(); + if (auto *TTyRepr = dyn_cast(ArgRepr)) + return TTyRepr; + diagnoseMissingParens(ctx, ArgRepr); + return TupleTypeRepr::create(ctx, {ArgRepr}, ArgRepr->getSourceRange()); + } + return nullptr; + }; + + auto extractTypeRepr = [&](Expr *E) -> TypeRepr * { + if (!E) + return nullptr; + if (auto *TyE = dyn_cast(E)) + return TyE->getTypeRepr(); + if (auto *TE = dyn_cast(E)) + if (TE->getNumElements() == 0) + return TupleTypeRepr::createEmpty(ctx, TE->getSourceRange()); + + // When simplifying a type expr like "P1 & P2 -> P3 & P4 -> Int", + // it may have been folded at the same time; recursively simplify it. + if (auto ArgsTypeExpr = simplifyTypeExpr(E)) + return ArgsTypeExpr->getTypeRepr(); + return nullptr; + }; + + TupleTypeRepr *ArgsTypeRepr = extractInputTypeRepr(AE->getArgsExpr()); + if (!ArgsTypeRepr) { + ctx.Diags.diagnose(AE->getArgsExpr()->getLoc(), + diag::expected_type_before_arrow); + auto ArgRange = AE->getArgsExpr()->getSourceRange(); + auto ErrRepr = new (ctx) ErrorTypeRepr(ArgRange); + ArgsTypeRepr = + TupleTypeRepr::create(ctx, {ErrRepr}, ArgRange); + } + + TypeRepr *ResultTypeRepr = extractTypeRepr(AE->getResultExpr()); + if (!ResultTypeRepr) { + ctx.Diags.diagnose(AE->getResultExpr()->getLoc(), + diag::expected_type_after_arrow); + ResultTypeRepr = new (ctx) + ErrorTypeRepr(AE->getResultExpr()->getSourceRange()); + } + + auto NewTypeRepr = new (ctx) + FunctionTypeRepr(nullptr, ArgsTypeRepr, AE->getAsyncLoc(), + AE->getThrowsLoc(), AE->getArrowLoc(), ResultTypeRepr); + return new (ctx) TypeExpr(NewTypeRepr); + } + + // Fold 'P & Q' into a composition type + if (auto *binaryExpr = dyn_cast(E)) { + bool isComposition = false; + // look at the name of the operator, if it is a '&' we can create the + // composition TypeExpr + auto fn = binaryExpr->getFn(); + if (auto Overload = dyn_cast(fn)) { + for (auto Decl : Overload->getDecls()) + if (Decl->getBaseName() == "&") { + isComposition = true; + break; + } + } else if (auto *Decl = dyn_cast(fn)) { + if (Decl->getName().isSimpleName() && + Decl->getName().getBaseName() == "&") + isComposition = true; + } + + if (isComposition) { + // The protocols we are composing + SmallVector Types; + + auto lhsExpr = binaryExpr->getArg()->getElement(0); + if (auto *lhs = dyn_cast(lhsExpr)) { + Types.push_back(lhs->getTypeRepr()); + } else if (isa(lhsExpr)) { + // If the lhs is another binary expression, we have a multi element + // composition: 'A & B & C' is parsed as ((A & B) & C); we get + // the protocols from the lhs here + if (auto expr = simplifyTypeExpr(lhsExpr)) + if (auto *repr = dyn_cast(expr->getTypeRepr())) + // add the protocols to our list + for (auto proto : repr->getTypes()) + Types.push_back(proto); + else + return nullptr; + else + return nullptr; + } else + return nullptr; + + // Add the rhs which is just a TypeExpr + auto *rhs = dyn_cast(binaryExpr->getArg()->getElement(1)); + if (!rhs) return nullptr; + Types.push_back(rhs->getTypeRepr()); + + auto CompRepr = CompositionTypeRepr::create(getASTContext(), Types, + lhsExpr->getStartLoc(), + binaryExpr->getSourceRange()); + return new (getASTContext()) TypeExpr(CompRepr); + } + } + + return nullptr; +} + +void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) { + if (KPE->isObjC()) + return; + + if (!KPE->getComponents().empty()) + return; + + TypeRepr *rootType = nullptr; + SmallVector components; + auto &DE = getASTContext().Diags; + + // Pre-order visit of a sequence foo.bar[0]?.baz, which means that the + // components are pushed in reverse order. + auto traversePath = [&](Expr *expr, bool isInParsedPath, + bool emitErrors = true) { + Expr *outermostExpr = expr; + // We can end up in scenarios where the key path has contextual type, + // but is missing a leading dot. This can happen when we have an + // implicit TypeExpr or an implicit DeclRefExpr. + auto diagnoseMissingDot = [&]() { + DE.diagnose(expr->getLoc(), + diag::expr_swift_keypath_not_starting_with_dot) + .fixItInsert(expr->getStartLoc(), "."); + }; + while (1) { + // Base cases: we've reached the top. + if (auto TE = dyn_cast(expr)) { + assert(!isInParsedPath); + rootType = TE->getTypeRepr(); + if (TE->isImplicit() && !KPE->expectsContextualRoot()) + diagnoseMissingDot(); + return; + } else if (isa(expr)) { + assert(isInParsedPath); + // Nothing here: the type is either the root, or is inferred. + return; + } else if (!KPE->expectsContextualRoot() && expr->isImplicit() && + isa(expr)) { + assert(!isInParsedPath); + diagnoseMissingDot(); + return; + } + + // Recurring cases: + if (auto SE = dyn_cast(expr)) { + // .self, the identity component. + components.push_back(KeyPathExpr::Component::forIdentity( + SE->getSelfLoc())); + expr = SE->getSubExpr(); + } else if (auto UDE = dyn_cast(expr)) { + // .foo + components.push_back(KeyPathExpr::Component::forUnresolvedProperty( + UDE->getName(), UDE->getLoc())); + + expr = UDE->getBase(); + } else if (auto SE = dyn_cast(expr)) { + // .[0] or just plain [0] + components.push_back( + KeyPathExpr::Component::forUnresolvedSubscriptWithPrebuiltIndexExpr( + getASTContext(), SE->getIndex(), SE->getArgumentLabels(), + SE->getLoc())); + + expr = SE->getBase(); + } else if (auto BOE = dyn_cast(expr)) { + // .? or ? + components.push_back(KeyPathExpr::Component::forUnresolvedOptionalChain( + BOE->getQuestionLoc())); + + expr = BOE->getSubExpr(); + } else if (auto FVE = dyn_cast(expr)) { + // .! or ! + components.push_back(KeyPathExpr::Component::forUnresolvedOptionalForce( + FVE->getExclaimLoc())); + + expr = FVE->getSubExpr(); + } else if (auto OEE = dyn_cast(expr)) { + // Do nothing: this is implied to exist as the last expression, by the + // BindOptionalExprs, but is irrelevant to the components. + (void)outermostExpr; + assert(OEE == outermostExpr); + expr = OEE->getSubExpr(); + } else { + if (emitErrors) { + // \() may be an attempt to write a string interpolation outside + // of a string literal; diagnose this case specially. + if (isa(expr) || isa(expr)) { + DE.diagnose(expr->getLoc(), + diag::expr_string_interpolation_outside_string); + } else { + DE.diagnose(expr->getLoc(), + diag::expr_swift_keypath_invalid_component); + } + } + components.push_back(KeyPathExpr::Component()); + return; + } + } + }; + + auto root = KPE->getParsedRoot(); + auto path = KPE->getParsedPath(); + + if (path) { + traversePath(path, /*isInParsedPath=*/true); + + // This path looks like \Foo.Bar.[0].baz, which means Foo.Bar has to be a + // type. + if (root) { + if (auto TE = dyn_cast(root)) { + rootType = TE->getTypeRepr(); + } else { + // FIXME: Probably better to catch this case earlier and force-eval as + // TypeExpr. + DE.diagnose(root->getLoc(), + diag::expr_swift_keypath_not_starting_with_type); + + // Traverse this path for recovery purposes: it may be a typo like + // \Foo.property.[0]. + traversePath(root, /*isInParsedPath=*/false, + /*emitErrors=*/false); + } + } + } else { + traversePath(root, /*isInParsedPath=*/false); + } + + // Key paths must be spelled with at least one component. + if (components.empty()) { + // Passes further down the pipeline expect keypaths to always have at least + // one component, so stuff an invalid component in the AST for recovery. + components.push_back(KeyPathExpr::Component()); + } + + std::reverse(components.begin(), components.end()); + + KPE->setRootType(rootType); + KPE->resolveComponents(getASTContext(), components); +} + +Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) { + // If constructor call is expected to produce an optional let's not attempt + // this optimization because literal initializers aren't failable. + if (!getASTContext().LangOpts.isSwiftVersionAtLeast(5)) { + if (!ExprStack.empty()) { + auto *parent = ExprStack.back(); + if (isa(parent) || isa(parent)) + return nullptr; + } + } + + auto *call = dyn_cast(E); + if (!call || call->getNumArguments() != 1) + return nullptr; + + auto *typeExpr = dyn_cast(call->getFn()); + if (!typeExpr) + return nullptr; + + auto *argExpr = call->getArg()->getSemanticsProvidingExpr(); + auto *literal = dyn_cast(argExpr); + if (!literal) + return nullptr; + + auto *protocol = TypeChecker::getLiteralProtocol(getASTContext(), literal); + if (!protocol) + return nullptr; + + Type castTy; + if (auto precheckedTy = typeExpr->getInstanceType()) { + castTy = precheckedTy; + } else { + const auto options = + TypeResolutionOptions(TypeResolverContext::InExpression) | + TypeResolutionFlags::SilenceErrors; + + const auto resolution = + TypeResolution::forContextual(DC, options, [](auto unboundTy) { + // FIXME: Don't let unbound generic types escape type resolution. + // For now, just return the unbound generic type. + return unboundTy; + }); + const auto result = resolution.resolveType(typeExpr->getTypeRepr()); + if (result->hasError()) + return nullptr; + castTy = result; + } + + if (!castTy || !castTy->getAnyNominal()) + return nullptr; + + // Don't bother to convert deprecated selector syntax. + if (auto selectorTy = getASTContext().getSelectorType()) { + if (castTy->isEqual(selectorTy)) + return nullptr; + } + + SmallVector conformances; + return castTy->getAnyNominal()->lookupConformance(DC->getParentModule(), + protocol, conformances) + ? CoerceExpr::forLiteralInit(getASTContext(), argExpr, + call->getSourceRange(), + typeExpr->getTypeRepr()) + : nullptr; +} + +/// Pre-check the expression, validating any types that occur in the +/// expression and folding sequence expressions. +bool ConstraintSystem::preCheckExpression(Expr *&expr, DeclContext *dc, + bool replaceInvalidRefsWithErrors) { + PreCheckExpression preCheck(dc, expr, replaceInvalidRefsWithErrors); + // Perform the pre-check. + if (auto result = expr->walk(preCheck)) { + expr = result; + return false; + } + return true; +} \ No newline at end of file diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index ef179ec4acd1f..71fb7c5d2a691 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -20,30 +20,17 @@ #include "MiscDiagnostics.h" #include "SolutionResult.h" #include "TypeChecker.h" -#include "TypeCheckType.h" -#include "TypoCorrection.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" -#include "swift/AST/DiagnosticsParse.h" #include "swift/AST/DiagnosticSuppression.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Initializer.h" -#include "swift/AST/NameLookup.h" -#include "swift/AST/NameLookupRequests.h" -#include "swift/AST/ParameterList.h" #include "swift/AST/PrettyStackTrace.h" -#include "swift/AST/PropertyWrappers.h" -#include "swift/AST/ProtocolConformance.h" #include "swift/AST/SubstitutionMap.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Statistic.h" -#include "swift/Parse/Confusables.h" -#include "swift/Parse/Lexer.h" #include "swift/Sema/CodeCompletionTypeChecking.h" -#include "llvm/ADT/APInt.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/MapVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" @@ -224,1850 +211,6 @@ Expr *ConstraintLocatorBuilder::trySimplifyToExpr() const { return (path.empty() ? getAsExpr(anchor) : nullptr); } -//===----------------------------------------------------------------------===// -// High-level entry points. -//===----------------------------------------------------------------------===// - -static unsigned getNumArgs(ValueDecl *value) { - if (auto *func = dyn_cast(value)) - return func->getParameters()->size(); - return ~0U; -} - -static bool matchesDeclRefKind(ValueDecl *value, DeclRefKind refKind) { - switch (refKind) { - // An ordinary reference doesn't ignore anything. - case DeclRefKind::Ordinary: - return true; - - // A binary-operator reference only honors FuncDecls with a certain type. - case DeclRefKind::BinaryOperator: - return (getNumArgs(value) == 2); - - case DeclRefKind::PrefixOperator: - return (!value->getAttrs().hasAttribute() && - getNumArgs(value) == 1); - - case DeclRefKind::PostfixOperator: - return (value->getAttrs().hasAttribute() && - getNumArgs(value) == 1); - } - llvm_unreachable("bad declaration reference kind"); -} - -static bool containsDeclRefKind(LookupResult &lookupResult, - DeclRefKind refKind) { - for (auto candidate : lookupResult) { - ValueDecl *D = candidate.getValueDecl(); - if (!D) - continue; - if (matchesDeclRefKind(D, refKind)) - return true; - } - return false; -} - -/// Emit a diagnostic with a fixit hint for an invalid binary operator, showing -/// how to split it according to splitCandidate. -static void diagnoseBinOpSplit(ASTContext &Context, UnresolvedDeclRefExpr *UDRE, - std::pair splitCandidate, - Diag diagID) { - - unsigned splitLoc = splitCandidate.first; - bool isBinOpFirst = splitCandidate.second; - StringRef nameStr = UDRE->getName().getBaseIdentifier().str(); - auto startStr = nameStr.substr(0, splitLoc); - auto endStr = nameStr.drop_front(splitLoc); - - // One valid split found, it is almost certainly the right answer. - auto diag = Context.Diags.diagnose( - UDRE->getLoc(), diagID, Context.getIdentifier(startStr), - Context.getIdentifier(endStr), isBinOpFirst); - // Highlight the whole operator. - diag.highlight(UDRE->getLoc()); - // Insert whitespace on the left if the binop is at the start, or to the - // right if it is end. - if (isBinOpFirst) - diag.fixItInsert(UDRE->getLoc(), " "); - else - diag.fixItInsertAfter(UDRE->getLoc(), " "); - - // Insert a space between the operators. - diag.fixItInsert(UDRE->getLoc().getAdvancedLoc(splitLoc), " "); -} - -/// If we failed lookup of a binary operator, check to see it to see if -/// it is a binary operator juxtaposed with a unary operator (x*-4) that -/// needs whitespace. If so, emit specific diagnostics for it and return true, -/// otherwise return false. -static bool diagnoseOperatorJuxtaposition(UnresolvedDeclRefExpr *UDRE, - DeclContext *DC) { - Identifier name = UDRE->getName().getBaseIdentifier(); - StringRef nameStr = name.str(); - if (!name.isOperator() || nameStr.size() < 2) - return false; - - bool isBinOp = UDRE->getRefKind() == DeclRefKind::BinaryOperator; - - // If this is a binary operator, relex the token, to decide whether it has - // whitespace around it or not. If it does "x +++ y", then it isn't likely to - // be a case where a space was forgotten. - auto &Context = DC->getASTContext(); - if (isBinOp) { - auto tok = Lexer::getTokenAtLocation(Context.SourceMgr, UDRE->getLoc()); - if (tok.getKind() != tok::oper_binary_unspaced) - return false; - } - - // Okay, we have a failed lookup of a multicharacter operator. Check to see if - // lookup succeeds if part is split off, and record the matches found. - // - // In the case of a binary operator, the bool indicated is false if the - // first half of the split is the unary operator (x!*4) or true if it is the - // binary operator (x*+4). - std::vector> WorkableSplits; - - // Check all the potential splits. - for (unsigned splitLoc = 1, e = nameStr.size(); splitLoc != e; ++splitLoc) { - // For it to be a valid split, the start and end section must be valid - // operators, splitting a unicode code point isn't kosher. - auto startStr = nameStr.substr(0, splitLoc); - auto endStr = nameStr.drop_front(splitLoc); - if (!Lexer::isOperator(startStr) || !Lexer::isOperator(endStr)) - continue; - - DeclNameRef startName(Context.getIdentifier(startStr)); - DeclNameRef endName(Context.getIdentifier(endStr)); - - // Perform name lookup for the first and second pieces. If either fail to - // be found, then it isn't a valid split. - auto startLookup = TypeChecker::lookupUnqualified( - DC, startName, UDRE->getLoc(), defaultUnqualifiedLookupOptions); - if (!startLookup) continue; - auto endLookup = TypeChecker::lookupUnqualified(DC, endName, UDRE->getLoc(), - defaultUnqualifiedLookupOptions); - if (!endLookup) continue; - - // If the overall operator is a binary one, then we're looking at - // juxtaposed binary and unary operators. - if (isBinOp) { - // Look to see if the candidates found could possibly match. - if (containsDeclRefKind(startLookup, DeclRefKind::PostfixOperator) && - containsDeclRefKind(endLookup, DeclRefKind::BinaryOperator)) - WorkableSplits.push_back({ splitLoc, false }); - - if (containsDeclRefKind(startLookup, DeclRefKind::BinaryOperator) && - containsDeclRefKind(endLookup, DeclRefKind::PrefixOperator)) - WorkableSplits.push_back({ splitLoc, true }); - } else { - // Otherwise, it is two of the same kind, e.g. "!!x" or "!~x". - if (containsDeclRefKind(startLookup, UDRE->getRefKind()) && - containsDeclRefKind(endLookup, UDRE->getRefKind())) - WorkableSplits.push_back({ splitLoc, false }); - } - } - - switch (WorkableSplits.size()) { - case 0: - // No splits found, can't produce this diagnostic. - return false; - case 1: - // One candidate: produce an error with a fixit on it. - if (isBinOp) - diagnoseBinOpSplit(Context, UDRE, WorkableSplits[0], - diag::unspaced_binary_operator_fixit); - else - Context.Diags.diagnose( - UDRE->getLoc().getAdvancedLoc(WorkableSplits[0].first), - diag::unspaced_unary_operator); - return true; - - default: - // Otherwise, we have to produce a series of notes listing the various - // options. - Context.Diags - .diagnose(UDRE->getLoc(), isBinOp ? diag::unspaced_binary_operator - : diag::unspaced_unary_operator) - .highlight(UDRE->getLoc()); - - if (isBinOp) { - for (auto candidateSplit : WorkableSplits) - diagnoseBinOpSplit(Context, UDRE, candidateSplit, - diag::unspaced_binary_operators_candidate); - } - return true; - } -} - -static bool diagnoseRangeOperatorMisspell(DiagnosticEngine &Diags, - UnresolvedDeclRefExpr *UDRE) { - auto name = UDRE->getName().getBaseIdentifier(); - if (!name.isOperator()) - return false; - - auto corrected = StringRef(); - if (name.str() == ".." || name.str() == "...." || - name.str() == ".…" || name.str() == "…" || name.str() == "….") - corrected = "..."; - else if (name.str() == "...<" || name.str() == "....<" || - name.str() == "…<") - corrected = "..<"; - - if (!corrected.empty()) { - Diags - .diagnose(UDRE->getLoc(), diag::cannot_find_in_scope_corrected, - UDRE->getName(), true, corrected) - .highlight(UDRE->getSourceRange()) - .fixItReplace(UDRE->getSourceRange(), corrected); - - return true; - } - return false; -} - -static bool diagnoseIncDecOperator(DiagnosticEngine &Diags, - UnresolvedDeclRefExpr *UDRE) { - auto name = UDRE->getName().getBaseIdentifier(); - if (!name.isOperator()) - return false; - - auto corrected = StringRef(); - if (name.str() == "++") - corrected = "+= 1"; - else if (name.str() == "--") - corrected = "-= 1"; - - if (!corrected.empty()) { - Diags - .diagnose(UDRE->getLoc(), diag::cannot_find_in_scope_corrected, - UDRE->getName(), true, corrected) - .highlight(UDRE->getSourceRange()); - - return true; - } - return false; -} - -static bool findNonMembers(ArrayRef lookupResults, - DeclRefKind refKind, bool breakOnMember, - SmallVectorImpl &ResultValues, - llvm::function_ref isValid) { - bool AllDeclRefs = true; - for (auto Result : lookupResults) { - // If we find a member, then all of the results aren't non-members. - bool IsMember = - (Result.getBaseDecl() && !isa(Result.getBaseDecl())); - if (IsMember) { - AllDeclRefs = false; - if (breakOnMember) - break; - continue; - } - - ValueDecl *D = Result.getValueDecl(); - if (!isValid(D)) - return false; - - if (matchesDeclRefKind(D, refKind)) - ResultValues.push_back(D); - } - - return AllDeclRefs; -} - -/// Find the next element in a chain of members. If this expression is (or -/// could be) the base of such a chain, this will return \c nullptr. -static Expr *getMemberChainSubExpr(Expr *expr) { - assert(expr && "getMemberChainSubExpr called with null expr!"); - if (auto *UDE = dyn_cast(expr)) { - return UDE->getBase(); - } else if (auto *CE = dyn_cast(expr)) { - return CE->getFn(); - } else if (auto *BOE = dyn_cast(expr)) { - return BOE->getSubExpr(); - } else if (auto *FVE = dyn_cast(expr)) { - return FVE->getSubExpr(); - } else if (auto *SE = dyn_cast(expr)) { - return SE->getBase(); - } else if (auto *CCE = dyn_cast(expr)) { - return CCE->getBase(); - } else { - return nullptr; - } -} - -UnresolvedMemberExpr *TypeChecker::getUnresolvedMemberChainBase(Expr *expr) { - if (auto *subExpr = getMemberChainSubExpr(expr)) - return getUnresolvedMemberChainBase(subExpr); - else - return dyn_cast(expr); -} - -/// Whether this expression is a member of a member chain. -static bool isMemberChainMember(Expr *expr) { - return getMemberChainSubExpr(expr) != nullptr; -} -/// Whether this expression sits at the end of a chain of member accesses. -static bool isMemberChainTail(Expr *expr, Expr *parent) { - assert(expr && "isMemberChainTail called with null expr!"); - // If this expression's parent is not itself part of a chain (or, this expr - // has no parent expr), this must be the tail of the chain. - return parent == nullptr || !isMemberChainMember(parent); -} - -/// Bind an UnresolvedDeclRefExpr by performing name lookup and -/// returning the resultant expression. Context is the DeclContext used -/// for the lookup. -Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, - DeclContext *DC, - bool replaceInvalidRefsWithErrors) { - // Process UnresolvedDeclRefExpr by doing an unqualified lookup. - DeclNameRef Name = UDRE->getName(); - SourceLoc Loc = UDRE->getLoc(); - - auto errorResult = [&]() -> Expr * { - if (replaceInvalidRefsWithErrors) - return new (DC->getASTContext()) ErrorExpr(UDRE->getSourceRange()); - return UDRE; - }; - - // Perform standard value name lookup. - NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; - // TODO: Include all of the possible members to give a solver a - // chance to diagnose name shadowing which requires explicit - // name/module qualifier to access top-level name. - lookupOptions |= NameLookupFlags::IncludeOuterResults; - - if (Loc.isInvalid()) - DC = DC->getModuleScopeContext(); - - auto Lookup = TypeChecker::lookupUnqualified(DC, Name, Loc, lookupOptions); - - auto &Context = DC->getASTContext(); - if (!Lookup) { - // If we failed lookup of an operator, check to see if this is a range - // operator misspelling. Otherwise try to diagnose a juxtaposition - // e.g. (x*-4) that needs whitespace. - if (diagnoseRangeOperatorMisspell(Context.Diags, UDRE) || - diagnoseIncDecOperator(Context.Diags, UDRE) || - diagnoseOperatorJuxtaposition(UDRE, DC)) { - return errorResult(); - } - - // Try ignoring access control. - NameLookupOptions relookupOptions = lookupOptions; - relookupOptions |= NameLookupFlags::IgnoreAccessControl; - auto inaccessibleResults = - TypeChecker::lookupUnqualified(DC, Name, Loc, relookupOptions); - if (inaccessibleResults) { - // FIXME: What if the unviable candidates have different levels of access? - const ValueDecl *first = inaccessibleResults.front().getValueDecl(); - Context.Diags.diagnose( - Loc, diag::candidate_inaccessible, first->getBaseName(), - first->getFormalAccessScope().accessLevelForDiagnostics()); - - // FIXME: If any of the candidates (usually just one) are in the same - // module we could offer a fix-it. - for (auto lookupResult : inaccessibleResults) { - auto *VD = lookupResult.getValueDecl(); - VD->diagnose(diag::decl_declared_here, VD->getName()); - } - - // Don't try to recover here; we'll get more access-related diagnostics - // downstream if the type of the inaccessible decl is also inaccessible. - return errorResult(); - } - - // TODO: Name will be a compound name if it was written explicitly as - // one, but we should also try to propagate labels into this. - DeclNameLoc nameLoc = UDRE->getNameLoc(); - - Identifier simpleName = Name.getBaseIdentifier(); - const char *buffer = simpleName.get(); - llvm::SmallString<64> expectedIdentifier; - bool isConfused = false; - uint32_t codepoint; - uint32_t firstConfusableCodepoint = 0; - int totalCodepoints = 0; - int offset = 0; - while ((codepoint = validateUTF8CharacterAndAdvance(buffer, - buffer + - strlen(buffer))) - != ~0U) { - int length = (buffer - simpleName.get()) - offset; - if (auto expectedCodepoint = - confusable::tryConvertConfusableCharacterToASCII(codepoint)) { - if (firstConfusableCodepoint == 0) { - firstConfusableCodepoint = codepoint; - } - isConfused = true; - expectedIdentifier += expectedCodepoint; - } else { - expectedIdentifier += (char)codepoint; - } - - totalCodepoints++; - - offset += length; - } - - auto emitBasicError = [&] { - Context.Diags - .diagnose(Loc, diag::cannot_find_in_scope, Name, - Name.isOperator()) - .highlight(UDRE->getSourceRange()); - }; - - if (!isConfused) { - if (Name.isSimpleName(Context.Id_Self)) { - if (DeclContext *typeContext = DC->getInnermostTypeContext()){ - Type SelfType = typeContext->getSelfInterfaceType(); - - if (typeContext->getSelfClassDecl()) - SelfType = DynamicSelfType::get(SelfType, Context); - SelfType = DC->mapTypeIntoContext(SelfType); - return new (Context) - TypeExpr(new (Context) FixedTypeRepr(SelfType, Loc)); - } - } - - TypoCorrectionResults corrections(Name, nameLoc); - TypeChecker::performTypoCorrection(DC, UDRE->getRefKind(), Type(), - lookupOptions, corrections); - - if (auto typo = corrections.claimUniqueCorrection()) { - auto diag = Context.Diags.diagnose( - Loc, diag::cannot_find_in_scope_corrected, Name, - Name.isOperator(), typo->CorrectedName.getBaseIdentifier().str()); - diag.highlight(UDRE->getSourceRange()); - typo->addFixits(diag); - } else { - emitBasicError(); - } - - corrections.noteAllCandidates(); - } else { - emitBasicError(); - - if (totalCodepoints == 1) { - auto charNames = confusable::getConfusableAndBaseCodepointNames( - firstConfusableCodepoint); - Context.Diags - .diagnose(Loc, diag::single_confusable_character, - UDRE->getName().isOperator(), simpleName.str(), - charNames.first, expectedIdentifier, charNames.second) - .fixItReplace(Loc, expectedIdentifier); - } else { - Context.Diags - .diagnose(Loc, diag::confusable_character, - UDRE->getName().isOperator(), simpleName.str(), - expectedIdentifier) - .fixItReplace(Loc, expectedIdentifier); - } - } - - // TODO: consider recovering from here. We may want some way to suppress - // downstream diagnostics, though. - - return errorResult(); - } - - // FIXME: Need to refactor the way we build an AST node from a lookup result! - - SmallVector ResultValues; - ValueDecl *localDeclAfterUse = nullptr; - auto isValid = [&](ValueDecl *D) { - // FIXME: The source-location checks won't make sense once - // EnableASTScopeLookup is the default. - // - // Note that we allow forward references to types, because they cannot - // capture. - if (Loc.isValid() && D->getLoc().isValid() && - D->getDeclContext()->isLocalContext() && - D->getDeclContext() == DC && - Context.SourceMgr.isBeforeInBuffer(Loc, D->getLoc()) && - !isa(D)) { - localDeclAfterUse = D; - return false; - } - return true; - }; - bool AllDeclRefs = - findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), - /*breakOnMember=*/true, ResultValues, isValid); - - // If local declaration after use is found, check outer results for - // better matching candidates. - if (localDeclAfterUse) { - auto innerDecl = localDeclAfterUse; - while (localDeclAfterUse) { - if (Lookup.outerResults().empty()) { - Context.Diags.diagnose(Loc, diag::use_local_before_declaration, Name); - Context.Diags.diagnose(innerDecl, diag::decl_declared_here, - localDeclAfterUse->getName()); - Expr *error = new (Context) ErrorExpr(UDRE->getSourceRange()); - return error; - } - - Lookup.shiftDownResults(); - ResultValues.clear(); - localDeclAfterUse = nullptr; - AllDeclRefs = - findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), - /*breakOnMember=*/true, ResultValues, isValid); - } - } - - // If we have an unambiguous reference to a type decl, form a TypeExpr. - if (Lookup.size() == 1 && UDRE->getRefKind() == DeclRefKind::Ordinary && - isa(Lookup[0].getValueDecl())) { - auto *D = cast(Lookup[0].getValueDecl()); - // FIXME: This is odd. - if (isa(D)) { - return new (Context) DeclRefExpr(D, UDRE->getNameLoc(), - /*Implicit=*/false, - AccessSemantics::Ordinary, - D->getInterfaceType()); - } - - auto *LookupDC = Lookup[0].getDeclContext(); - if (UDRE->isImplicit()) { - return TypeExpr::createImplicitForDecl( - UDRE->getNameLoc(), D, LookupDC, - LookupDC->mapTypeIntoContext(D->getInterfaceType())); - } else { - return TypeExpr::createForDecl(UDRE->getNameLoc(), D, LookupDC); - } - } - - if (AllDeclRefs) { - // Diagnose uses of operators that found no matching candidates. - if (ResultValues.empty()) { - assert(UDRE->getRefKind() != DeclRefKind::Ordinary); - Context.Diags.diagnose( - Loc, diag::use_nonmatching_operator, Name, - UDRE->getRefKind() == DeclRefKind::BinaryOperator - ? 0 - : UDRE->getRefKind() == DeclRefKind::PrefixOperator ? 1 : 2); - return new (Context) ErrorExpr(UDRE->getSourceRange()); - } - - // For operators, sort the results so that non-generic operations come - // first. - // Note: this is part of a performance hack to prefer non-generic operators - // to generic operators, because the former is far more efficient to check. - if (UDRE->getRefKind() != DeclRefKind::Ordinary) { - std::stable_sort(ResultValues.begin(), ResultValues.end(), - [&](ValueDecl *x, ValueDecl *y) -> bool { - auto xGeneric = x->getInterfaceType()->getAs(); - auto yGeneric = y->getInterfaceType()->getAs(); - if (static_cast(xGeneric) != static_cast(yGeneric)) { - return xGeneric? false : true; - } - - if (!xGeneric) - return false; - - unsigned xDepth = xGeneric->getGenericParams().back()->getDepth(); - unsigned yDepth = yGeneric->getGenericParams().back()->getDepth(); - return xDepth < yDepth; - }); - } - - return buildRefExpr(ResultValues, DC, UDRE->getNameLoc(), - UDRE->isImplicit(), UDRE->getFunctionRefKind()); - } - - ResultValues.clear(); - bool AllMemberRefs = true; - ValueDecl *Base = nullptr; - DeclContext *BaseDC = nullptr; - for (auto Result : Lookup) { - auto ThisBase = Result.getBaseDecl(); - - // Track the base for member declarations. - if (ThisBase && !isa(ThisBase)) { - auto Value = Result.getValueDecl(); - ResultValues.push_back(Value); - if (Base && ThisBase != Base) { - AllMemberRefs = false; - break; - } - - Base = ThisBase; - BaseDC = Result.getDeclContext(); - continue; - } - - AllMemberRefs = false; - break; - } - - if (AllMemberRefs) { - Expr *BaseExpr; - if (auto PD = dyn_cast(Base)) { - auto selfParam = PD->getGenericParams()->getParams().front(); - BaseExpr = TypeExpr::createImplicitForDecl( - UDRE->getNameLoc(), selfParam, - /*DC*/ nullptr, - DC->mapTypeIntoContext(selfParam->getInterfaceType())); - } else if (auto NTD = dyn_cast(Base)) { - BaseExpr = TypeExpr::createImplicitForDecl( - UDRE->getNameLoc(), NTD, BaseDC, - DC->mapTypeIntoContext(NTD->getInterfaceType())); - } else { - BaseExpr = new (Context) DeclRefExpr(Base, UDRE->getNameLoc(), - /*Implicit=*/true); - } - - llvm::SmallVector outerAlternatives; - (void)findNonMembers(Lookup.outerResults(), UDRE->getRefKind(), - /*breakOnMember=*/false, outerAlternatives, - /*isValid=*/[](ValueDecl *choice) -> bool { - return !choice->isInvalid(); - }); - - // Otherwise, form an UnresolvedDotExpr and sema will resolve it based on - // type information. - return new (Context) UnresolvedDotExpr( - BaseExpr, SourceLoc(), Name, UDRE->getNameLoc(), UDRE->isImplicit(), - Context.AllocateCopy(outerAlternatives)); - } - - // FIXME: If we reach this point, the program we're being handed is likely - // very broken, but it's still conceivable that this may happen due to - // invalid shadowed declarations. - // - // Make sure we emit a diagnostic, since returning an ErrorExpr without - // producing one will break things downstream. - Context.Diags.diagnose(Loc, diag::ambiguous_decl_ref, Name); - for (auto Result : Lookup) { - auto *Decl = Result.getValueDecl(); - Context.Diags.diagnose(Decl, diag::decl_declared_here, Decl->getName()); - } - return new (Context) ErrorExpr(UDRE->getSourceRange()); -} - -/// If an expression references 'self.init' or 'super.init' in an -/// initializer context, returns the implicit 'self' decl of the constructor. -/// Otherwise, return nil. -VarDecl * -TypeChecker::getSelfForInitDelegationInConstructor(DeclContext *DC, - UnresolvedDotExpr *ctorRef) { - // If the reference isn't to a constructor, we're done. - if (ctorRef->getName().getBaseName() != DeclBaseName::createConstructor()) - return nullptr; - - if (auto ctorContext = - dyn_cast_or_null(DC->getInnermostMethodContext())) { - auto nestedArg = ctorRef->getBase(); - if (auto inout = dyn_cast(nestedArg)) - nestedArg = inout->getSubExpr(); - if (nestedArg->isSuperExpr()) - return ctorContext->getImplicitSelfDecl(); - if (auto declRef = dyn_cast(nestedArg)) - if (declRef->getDecl()->getName() == DC->getASTContext().Id_self) - return ctorContext->getImplicitSelfDecl(); - } - return nullptr; -} - -namespace { - /// Update the function reference kind based on adding a direct call to a - /// callee with this kind. - FunctionRefKind addingDirectCall(FunctionRefKind kind) { - switch (kind) { - case FunctionRefKind::Unapplied: - return FunctionRefKind::SingleApply; - - case FunctionRefKind::SingleApply: - case FunctionRefKind::DoubleApply: - return FunctionRefKind::DoubleApply; - - case FunctionRefKind::Compound: - return FunctionRefKind::Compound; - } - - llvm_unreachable("Unhandled FunctionRefKind in switch."); - } - - /// Update a direct callee expression node that has a function reference kind - /// based on seeing a call to this callee. - templategetFunctionRefKind())> - void tryUpdateDirectCalleeImpl(E *callee, int) { - callee->setFunctionRefKind(addingDirectCall(callee->getFunctionRefKind())); - } - - /// Version of tryUpdateDirectCalleeImpl for when the callee - /// expression type doesn't carry a reference. - template - void tryUpdateDirectCalleeImpl(E *callee, ...) { } - - /// The given expression is the direct callee of a call expression; mark it to - /// indicate that it has been called. - void markDirectCallee(Expr *callee) { - while (true) { - // Look through identity expressions. - if (auto identity = dyn_cast(callee)) { - callee = identity->getSubExpr(); - continue; - } - - // Look through unresolved 'specialize' expressions. - if (auto specialize = dyn_cast(callee)) { - callee = specialize->getSubExpr(); - continue; - } - - // Look through optional binding. - if (auto bindOptional = dyn_cast(callee)) { - callee = bindOptional->getSubExpr(); - continue; - } - - // Look through forced binding. - if (auto force = dyn_cast(callee)) { - callee = force->getSubExpr(); - continue; - } - - // Calls compose. - if (auto call = dyn_cast(callee)) { - callee = call->getFn(); - continue; - } - - // We're done. - break; - } - - // Cast the callee to its most-specific class, then try to perform an - // update. If the expression node has a declaration reference in it, the - // update will succeed. Otherwise, we're done propagating. - switch (callee->getKind()) { -#define EXPR(Id, Parent) \ - case ExprKind::Id: \ - tryUpdateDirectCalleeImpl(cast(callee), 0); \ - break; -#include "swift/AST/ExprNodes.def" - } - } - - class PreCheckExpression : public ASTWalker { - ASTContext &Ctx; - DeclContext *DC; - - Expr *ParentExpr; - - /// Indicates whether pre-check is allowed to insert - /// implicit `ErrorExpr` in place of invalid references. - bool UseErrorExprs; - - /// A stack of expressions being walked, used to determine where to - /// insert RebindSelfInConstructorExpr nodes. - llvm::SmallVector ExprStack; - - /// The 'self' variable to use when rebinding 'self' in a constructor. - VarDecl *UnresolvedCtorSelf = nullptr; - - /// The expression that will be wrapped by a RebindSelfInConstructorExpr - /// node when visited. - Expr *UnresolvedCtorRebindTarget = nullptr; - - /// The expressions that are direct arguments of call expressions. - llvm::SmallPtrSet CallArgs; - - /// Simplify expressions which are type sugar productions that got parsed - /// as expressions due to the parser not knowing which identifiers are - /// type names. - TypeExpr *simplifyTypeExpr(Expr *E); - - /// Simplify unresolved dot expressions which are nested type productions. - TypeExpr *simplifyNestedTypeExpr(UnresolvedDotExpr *UDE); - - TypeExpr *simplifyUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *USE); - - /// Simplify a key path expression into a canonical form. - void resolveKeyPathExpr(KeyPathExpr *KPE); - - /// Simplify constructs like `UInt32(1)` into `1 as UInt32` if - /// the type conforms to the expected literal protocol. - Expr *simplifyTypeConstructionWithLiteralArg(Expr *E); - - /// In Swift < 5, diagnose and correct invalid multi-argument or - /// argument-labeled interpolations. - void correctInterpolationIfStrange(InterpolatedStringLiteralExpr *ISLE) { - // These expressions are valid in Swift 5+. - if (getASTContext().isSwiftVersionAtLeast(5)) - return; - - /// Diagnoses appendInterpolation(...) calls with multiple - /// arguments or argument labels and corrects them. - class StrangeInterpolationRewriter : public ASTWalker { - ASTContext &Context; - - public: - StrangeInterpolationRewriter(ASTContext &Ctx) : Context(Ctx) {} - - virtual bool walkToDeclPre(Decl *D) override { - // We don't want to look inside decls. - return false; - } - - virtual std::pair walkToExprPre(Expr *E) override { - // One InterpolatedStringLiteralExpr should never be nested inside - // another except as a child of a CallExpr, and we don't recurse into - // the children of CallExprs. - assert(!isa(E) && - "StrangeInterpolationRewriter found nested interpolation?"); - - // We only care about CallExprs. - if (!isa(E)) - return { true, E }; - - auto call = cast(E); - if (auto callee = dyn_cast(call->getFn())) { - if (callee->getName().getBaseName() == - Context.Id_appendInterpolation) { - Expr *newArg = nullptr; - SourceLoc lParen, rParen; - - if (call->getNumArguments() > 1) { - auto *args = cast(call->getArg()); - - lParen = args->getLParenLoc(); - rParen = args->getRParenLoc(); - Expr *secondArg = args->getElement(1); - - Context.Diags - .diagnose(secondArg->getLoc(), - diag::string_interpolation_list_changing) - .highlightChars(secondArg->getLoc(), rParen); - Context.Diags - .diagnose(secondArg->getLoc(), - diag::string_interpolation_list_insert_parens) - .fixItInsertAfter(lParen, "(") - .fixItInsert(rParen, ")"); - - newArg = args; - } - else if(call->getNumArguments() == 1 && - call->getArgumentLabels().front() != Identifier()) { - auto *args = cast(call->getArg()); - newArg = args->getElement(0); - - lParen = args->getLParenLoc(); - rParen = args->getRParenLoc(); - - SourceLoc argLabelLoc = call->getArgumentLabelLoc(0), - argLoc = newArg->getStartLoc(); - - Context.Diags - .diagnose(argLabelLoc, - diag::string_interpolation_label_changing) - .highlightChars(argLabelLoc, argLoc); - Context.Diags - .diagnose(argLabelLoc, - diag::string_interpolation_remove_label, - call->getArgumentLabels().front()) - .fixItRemoveChars(argLabelLoc, argLoc); - } - - // If newArg is no longer null, we need to build a new - // appendInterpolation(_:) call that takes it to replace the bad - // appendInterpolation(...) call. - if (newArg) { - auto newCallee = new (Context) UnresolvedDotExpr( - callee->getBase(), /*dotloc=*/SourceLoc(), - DeclNameRef(Context.Id_appendInterpolation), - /*nameloc=*/DeclNameLoc(), /*Implicit=*/true); - - E = CallExpr::create(Context, newCallee, lParen, {newArg}, - {Identifier()}, {SourceLoc()}, rParen, - /*trailingClosures=*/{}, - /*implicit=*/false); - } - } - } - - // There is never a CallExpr between an InterpolatedStringLiteralExpr - // and an un-typechecked appendInterpolation(...) call, so whether we - // changed E or not, we don't need to recurse any deeper. - return { false, E }; - } - }; - - ISLE->getAppendingExpr()->walk( - StrangeInterpolationRewriter(getASTContext())); - } - - public: - PreCheckExpression(DeclContext *dc, Expr *parent, - bool replaceInvalidRefsWithErrors) - : Ctx(dc->getASTContext()), DC(dc), ParentExpr(parent), - UseErrorExprs(replaceInvalidRefsWithErrors) {} - - ASTContext &getASTContext() const { return Ctx; } - - bool walkToClosureExprPre(ClosureExpr *expr); - - bool shouldWalkCaptureInitializerExpressions() override { return true; } - - VarDecl *getImplicitSelfDeclForSuperContext(SourceLoc Loc) { - auto *methodContext = DC->getInnermostMethodContext(); - if (!methodContext) { - Ctx.Diags.diagnose(Loc, diag::super_not_in_class_method); - return nullptr; - } - - // Do an actual lookup for 'self' in case it shows up in a capture list. - auto *methodSelf = methodContext->getImplicitSelfDecl(); - auto *lookupSelf = ASTScope::lookupSingleLocalDecl(DC->getParentSourceFile(), - Ctx.Id_self, Loc); - if (lookupSelf && lookupSelf != methodSelf) { - // FIXME: This is the wrong diagnostic for if someone manually declares a - // variable named 'self' using backticks. - Ctx.Diags.diagnose(Loc, diag::super_in_closure_with_capture); - Ctx.Diags.diagnose(lookupSelf->getLoc(), - diag::super_in_closure_with_capture_here); - return nullptr; - } - - return methodSelf; - } - - std::pair walkToExprPre(Expr *expr) override { - // If this is a call, record the argument expression. - if (auto call = dyn_cast(expr)) { - if (!isa(expr)) { - CallArgs.insert(call->getArg()); - } - } - - // FIXME(diagnostics): `InOutType` could appear here as a result - // of successful re-typecheck of the one of the sub-expressions e.g. - // `let _: Int = { (s: inout S) in s.bar() }`. On the first - // attempt to type-check whole expression `s.bar()` - is going - // to have a base which points directly to declaration of `S`. - // But when diagnostics attempts to type-check `s.bar()` standalone - // its base would be tranformed into `InOutExpr -> DeclRefExr`, - // and `InOutType` is going to be recorded in constraint system. - // One possible way to fix this (if diagnostics still use typecheck) - // might be to make it so self is not wrapped into `InOutExpr` - // but instead used as @lvalue type in some case of mutable members. - if (!expr->isImplicit()) { - if (isa(expr) || isa(expr)) { - auto *LE = cast(expr); - if (auto *IOE = dyn_cast(LE->getBase())) - LE->setBase(IOE->getSubExpr()); - } - - if (auto *DSCE = dyn_cast(expr)) { - if (auto *IOE = dyn_cast(DSCE->getBase())) - DSCE->setBase(IOE->getSubExpr()); - } - } - - // Local function used to finish up processing before returning. Every - // return site should call through here. - auto finish = [&](bool recursive, Expr *expr) { - // If we're going to recurse, record this expression on the stack. - if (recursive) - ExprStack.push_back(expr); - - return std::make_pair(recursive, expr); - }; - - // Resolve 'super' references. - if (auto *superRef = dyn_cast(expr)) { - auto loc = superRef->getLoc(); - auto *selfDecl = getImplicitSelfDeclForSuperContext(loc); - if (selfDecl == nullptr) - return finish(true, new (Ctx) ErrorExpr(loc)); - - superRef->setSelf(selfDecl); - return finish(true, superRef); - } - - // For closures, type-check the patterns and result type as written, - // but do not walk into the body. That will be type-checked after - // we've determine the complete function type. - if (auto closure = dyn_cast(expr)) - return finish(walkToClosureExprPre(closure), expr); - - if (auto unresolved = dyn_cast(expr)) { - TypeChecker::checkForForbiddenPrefix( - getASTContext(), unresolved->getName().getBaseName()); - return finish(true, TypeChecker::resolveDeclRefExpr(unresolved, DC, - UseErrorExprs)); - } - - // Let's try to figure out if `InOutExpr` is out of place early - // otherwise there is a risk of producing solutions which can't - // be later applied to AST and would result in the crash in some - // cases. Such expressions are only allowed in argument positions - // of function/operator calls. - if (isa(expr)) { - // If this is an implicit `inout` expression we assume that - // compiler knowns what it's doing. - if (expr->isImplicit()) - return finish(true, expr); - - auto parents = ParentExpr->getParentMap(); - - auto result = parents.find(expr); - if (result != parents.end()) { - auto *parent = result->getSecond(); - - if (isa(parent)) - return finish(true, expr); - - if (isa(parent) || isa(parent)) { - auto call = parents.find(parent); - if (call != parents.end()) { - if (isa(call->getSecond()) || - isa(call->getSecond())) - return finish(true, expr); - - if (isa(call->getSecond())) { - getASTContext().Diags.diagnose( - expr->getStartLoc(), - diag::cannot_pass_inout_arg_to_subscript); - return finish(false, nullptr); - } - } - } - } - - getASTContext().Diags.diagnose(expr->getStartLoc(), - diag::extraneous_address_of); - return finish(false, nullptr); - } - - if (auto *ISLE = dyn_cast(expr)) - correctInterpolationIfStrange(ISLE); - - return finish(true, expr); - } - - Expr *walkToExprPost(Expr *expr) override { - // Remove this expression from the stack. - assert(ExprStack.back() == expr); - ExprStack.pop_back(); - - // Mark the direct callee as being a callee. - if (auto *call = dyn_cast(expr)) - markDirectCallee(call->getFn()); - - // Fold sequence expressions. - if (auto *seqExpr = dyn_cast(expr)) { - auto result = TypeChecker::foldSequence(seqExpr, DC); - return result->walk(*this); - } - - // Type check the type parameters in an UnresolvedSpecializeExpr. - if (auto *us = dyn_cast(expr)) { - if (auto *typeExpr = simplifyUnresolvedSpecializeExpr(us)) - return typeExpr; - } - - // If we're about to step out of a ClosureExpr, restore the DeclContext. - if (auto *ce = dyn_cast(expr)) { - assert(DC == ce && "DeclContext imbalance"); - DC = ce->getParent(); - } - - // A 'self.init' or 'super.init' application inside a constructor will - // evaluate to void, with the initializer's result implicitly rebound - // to 'self'. Recognize the unresolved constructor expression and - // determine where to place the RebindSelfInConstructorExpr node. - // When updating this logic, also update - // RebindSelfInConstructorExpr::getCalledConstructor. - auto &ctx = getASTContext(); - if (auto unresolvedDot = dyn_cast(expr)) { - if (auto self = TypeChecker::getSelfForInitDelegationInConstructor( - DC, unresolvedDot)) { - // Walk our ancestor expressions looking for the appropriate place - // to insert the RebindSelfInConstructorExpr. - Expr *target = nullptr; - bool foundApply = false; - bool foundRebind = false; - for (auto ancestor : llvm::reverse(ExprStack)) { - if (isa(ancestor)) { - // If we already have a rebind, then we're re-typechecking an - // expression and are done. - foundRebind = true; - break; - } - - // Recognize applications. - if (auto apply = dyn_cast(ancestor)) { - // If we already saw an application, we're done. - if (foundApply) - break; - - // If the function being called is not our unresolved initializer - // reference, we're done. - if (apply->getSemanticFn() != unresolvedDot) - break; - - foundApply = true; - target = ancestor; - continue; - } - - // Look through identity, force-value, and 'try' expressions. - if (isa(ancestor) || - isa(ancestor) || - isa(ancestor)) { - if (!CallArgs.count(ancestor)) { - if (target) - target = ancestor; - continue; - } - } - - // No other expression kinds are permitted. - break; - } - - // If we found a rebind target, note the insertion point. - if (target && !foundRebind) { - UnresolvedCtorRebindTarget = target; - UnresolvedCtorSelf = self; - } - } - } - - // If the expression we've found is the intended target of an - // RebindSelfInConstructorExpr, wrap it in the - // RebindSelfInConstructorExpr. - if (expr == UnresolvedCtorRebindTarget) { - expr = new (ctx) - RebindSelfInConstructorExpr(expr, UnresolvedCtorSelf); - UnresolvedCtorRebindTarget = nullptr; - return expr; - } - - // Double check if there are any BindOptionalExpr remaining in the - // tree (see comment below for more details), if there are no BOE - // expressions remaining remove OptionalEvaluationExpr from the tree. - if (auto OEE = dyn_cast(expr)) { - bool hasBindOptional = false; - OEE->forEachChildExpr([&](Expr *expr) -> Expr * { - if (isa(expr)) - hasBindOptional = true; - // If at least a single BOE was found, no reason - // to walk any further in the tree. - return hasBindOptional ? nullptr : expr; - }); - - return hasBindOptional ? OEE : OEE->getSubExpr(); - } - - // Check if there are any BindOptionalExpr in the tree which - // wrap DiscardAssignmentExpr, such situation corresponds to syntax - // like - `_? = `, since it doesn't really make - // sense to have optional assignment to discarded LValue which can - // never be optional, we can remove BOE from the tree and avoid - // generating any of the unnecessary constraints. - if (auto BOE = dyn_cast(expr)) { - if (auto DAE = dyn_cast(BOE->getSubExpr())) - return DAE; - } - - // If this is a sugared type that needs to be folded into a single - // TypeExpr, do it. - if (auto *simplified = simplifyTypeExpr(expr)) - return simplified; - - if (auto KPE = dyn_cast(expr)) { - resolveKeyPathExpr(KPE); - return KPE; - } - - if (auto *simplified = simplifyTypeConstructionWithLiteralArg(expr)) - return simplified; - - // If we find an unresolved member chain, wrap it in an - // UnresolvedMemberChainResultExpr (unless this has already been done). - auto *parent = Parent.getAsExpr(); - if (isMemberChainTail(expr, parent)) - if (auto *UME = TypeChecker::getUnresolvedMemberChainBase(expr)) - if (!parent || !isa(parent)) - return new (ctx) UnresolvedMemberChainResultExpr(expr, UME); - - return expr; - } - - std::pair walkToStmtPre(Stmt *stmt) override { - return { true, stmt }; - } - }; -} // end anonymous namespace - -/// Perform prechecking of a ClosureExpr before we dive into it. This returns -/// true when we want the body to be considered part of this larger expression. -bool PreCheckExpression::walkToClosureExprPre(ClosureExpr *closure) { - auto *PL = closure->getParameters(); - - // Validate the parameters. - bool hadParameterError = false; - - // If we encounter an error validating the parameter list, don't bail. - // Instead, go on to validate any potential result type, and bail - // afterwards. This allows for better diagnostics, and keeps the - // closure expression type well-formed. - for (auto param : *PL) { - hadParameterError |= param->isInvalid(); - } - - if (hadParameterError) - return false; - - // If we won't be checking the body of the closure, don't walk into it here. - if (!shouldTypeCheckInEnclosingExpression(closure)) - return false; - - // Update the current DeclContext to be the closure we're about to - // recurse into. - assert((closure->getParent() == DC || - closure->getParent()->isChildContextOf(DC)) && - "Decl context isn't correct"); - DC = closure; - return true; -} - -TypeExpr *PreCheckExpression::simplifyNestedTypeExpr(UnresolvedDotExpr *UDE) { - if (!UDE->getName().isSimpleName() || - UDE->getName().isSpecial()) - return nullptr; - - auto Name = UDE->getName(); - auto NameLoc = UDE->getNameLoc().getBaseNameLoc(); - - // Qualified type lookup with a module base is represented as a DeclRefExpr - // and not a TypeExpr. - if (auto *DRE = dyn_cast(UDE->getBase())) { - if (auto *TD = dyn_cast(DRE->getDecl())) { - // See if the type has a member type with this name. - auto Result = TypeChecker::lookupMemberType( - DC, TD->getDeclaredInterfaceType(), Name, - defaultMemberLookupOptions); - - // If there is no nested type with this name, we have a lookup of - // a non-type member, so leave the expression as-is. - if (Result.size() == 1) { - return TypeExpr::createForMemberDecl( - DRE->getNameLoc(), TD, UDE->getNameLoc(), Result.front().Member); - } - } - - return nullptr; - } - - auto *TyExpr = dyn_cast(UDE->getBase()); - if (!TyExpr) - return nullptr; - - auto *InnerTypeRepr = TyExpr->getTypeRepr(); - if (!InnerTypeRepr) - return nullptr; - - // Fold 'T.Protocol' into a protocol metatype. - if (Name.isSimpleName(getASTContext().Id_Protocol)) { - auto *NewTypeRepr = - new (getASTContext()) ProtocolTypeRepr(InnerTypeRepr, NameLoc); - return new (getASTContext()) TypeExpr(NewTypeRepr); - } - - // Fold 'T.Type' into an existential metatype if 'T' is a protocol, - // or an ordinary metatype otherwise. - if (Name.isSimpleName(getASTContext().Id_Type)) { - auto *NewTypeRepr = - new (getASTContext()) MetatypeTypeRepr(InnerTypeRepr, NameLoc); - return new (getASTContext()) TypeExpr(NewTypeRepr); - } - - // Fold 'T.U' into a nested type. - if (auto *ITR = dyn_cast(InnerTypeRepr)) { - // Resolve the TypeRepr to get the base type for the lookup. - // Disable availability diagnostics here, because the final - // TypeRepr will be resolved again when generating constraints. - const auto options = - TypeResolutionOptions(TypeResolverContext::InExpression) | - TypeResolutionFlags::AllowUnavailable; - const auto resolution = - TypeResolution::forContextual(DC, options, [](auto unboundTy) { - // FIXME: Don't let unbound generic types escape type resolution. - // For now, just return the unbound generic type. - return unboundTy; - }); - const auto BaseTy = resolution.resolveType(InnerTypeRepr); - - if (BaseTy->mayHaveMembers()) { - // See if there is a member type with this name. - auto Result = - TypeChecker::lookupMemberType(DC, BaseTy, Name, - defaultMemberLookupOptions); - - // If there is no nested type with this name, we have a lookup of - // a non-type member, so leave the expression as-is. - if (Result.size() == 1) { - return TypeExpr::createForMemberDecl(ITR, UDE->getNameLoc(), - Result.front().Member); - } - } - } - - return nullptr; -} - -TypeExpr *PreCheckExpression::simplifyUnresolvedSpecializeExpr( - UnresolvedSpecializeExpr *us) { - // If this is a reference type a specialized type, form a TypeExpr. - // The base should be a TypeExpr that we already resolved. - if (auto *te = dyn_cast(us->getSubExpr())) { - if (auto *ITR = dyn_cast_or_null(te->getTypeRepr())) { - return TypeExpr::createForSpecializedDecl(ITR, - us->getUnresolvedParams(), - SourceRange(us->getLAngleLoc(), - us->getRAngleLoc()), - getASTContext()); - } - } - - return nullptr; -} - -/// Simplify expressions which are type sugar productions that got parsed -/// as expressions due to the parser not knowing which identifiers are -/// type names. -TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { - // Don't try simplifying a call argument, because we don't want to - // simplify away the required ParenExpr/TupleExpr. - if (CallArgs.count(E) > 0) return nullptr; - - // Fold member types. - if (auto *UDE = dyn_cast(E)) { - return simplifyNestedTypeExpr(UDE); - } - - // Fold T? into an optional type when T is a TypeExpr. - if (isa(E) || isa(E)) { - TypeExpr *TyExpr; - SourceLoc QuestionLoc; - if (auto *OOE = dyn_cast(E)) { - TyExpr = dyn_cast(OOE->getSubExpr()); - QuestionLoc = OOE->getLoc(); - } else { - TyExpr = dyn_cast(cast(E)->getSubExpr()); - QuestionLoc = cast(E)->getQuestionLoc(); - } - if (!TyExpr) return nullptr; - - auto *InnerTypeRepr = TyExpr->getTypeRepr(); - assert(!TyExpr->isImplicit() && InnerTypeRepr && - "This doesn't work on implicit TypeExpr's, " - "the TypeExpr should have been built correctly in the first place"); - - // The optional evaluation is passed through. - if (isa(E)) - return TyExpr; - - auto *NewTypeRepr = - new (getASTContext()) OptionalTypeRepr(InnerTypeRepr, QuestionLoc); - return new (getASTContext()) TypeExpr(NewTypeRepr); - } - - // Fold T! into an IUO type when T is a TypeExpr. - if (auto *FVE = dyn_cast(E)) { - auto *TyExpr = dyn_cast(FVE->getSubExpr()); - if (!TyExpr) return nullptr; - - auto *InnerTypeRepr = TyExpr->getTypeRepr(); - assert(!TyExpr->isImplicit() && InnerTypeRepr && - "This doesn't work on implicit TypeExpr's, " - "the TypeExpr should have been built correctly in the first place"); - - auto *NewTypeRepr = new (getASTContext()) - ImplicitlyUnwrappedOptionalTypeRepr(InnerTypeRepr, - FVE->getExclaimLoc()); - return new (getASTContext()) TypeExpr(NewTypeRepr); - } - - // Fold (T) into a type T with parens around it. - if (auto *PE = dyn_cast(E)) { - auto *TyExpr = dyn_cast(PE->getSubExpr()); - if (!TyExpr) return nullptr; - - TupleTypeReprElement InnerTypeRepr[] = { TyExpr->getTypeRepr() }; - assert(!TyExpr->isImplicit() && InnerTypeRepr[0].Type && - "SubscriptExpr doesn't work on implicit TypeExpr's, " - "the TypeExpr should have been built correctly in the first place"); - - auto *NewTypeRepr = TupleTypeRepr::create(getASTContext(), InnerTypeRepr, - PE->getSourceRange()); - return new (getASTContext()) TypeExpr(NewTypeRepr); - } - - // Fold a tuple expr like (T1,T2) into a tuple type (T1,T2). - if (auto *TE = dyn_cast(E)) { - if (TE->hasTrailingClosure() || - // FIXME: Decide what to do about (). It could be a type or an expr. - TE->getNumElements() == 0) - return nullptr; - - SmallVector Elts; - unsigned EltNo = 0; - for (auto Elt : TE->getElements()) { - auto *eltTE = dyn_cast(Elt); - if (!eltTE) return nullptr; - TupleTypeReprElement elt; - assert(eltTE->getTypeRepr() && !eltTE->isImplicit() && - "This doesn't work on implicit TypeExpr's, the " - "TypeExpr should have been built correctly in the first place"); - - // If the tuple element has a label, propagate it. - elt.Type = eltTE->getTypeRepr(); - Identifier name = TE->getElementName(EltNo); - if (!name.empty()) { - elt.Name = name; - elt.NameLoc = TE->getElementNameLoc(EltNo); - } - - Elts.push_back(elt); - ++EltNo; - } - auto *NewTypeRepr = TupleTypeRepr::create( - getASTContext(), Elts, TE->getSourceRange(), SourceLoc(), Elts.size()); - return new (getASTContext()) TypeExpr(NewTypeRepr); - } - - - // Fold [T] into an array type. - if (auto *AE = dyn_cast(E)) { - if (AE->getElements().size() != 1) - return nullptr; - - auto *TyExpr = dyn_cast(AE->getElement(0)); - if (!TyExpr) - return nullptr; - - auto *NewTypeRepr = new (getASTContext()) - ArrayTypeRepr(TyExpr->getTypeRepr(), - SourceRange(AE->getLBracketLoc(), AE->getRBracketLoc())); - return new (getASTContext()) TypeExpr(NewTypeRepr); - } - - // Fold [K : V] into a dictionary type. - if (auto *DE = dyn_cast(E)) { - if (DE->getElements().size() != 1) - return nullptr; - - TypeRepr *keyTypeRepr, *valueTypeRepr; - - if (auto EltTuple = dyn_cast(DE->getElement(0))) { - auto *KeyTyExpr = dyn_cast(EltTuple->getElement(0)); - if (!KeyTyExpr) - return nullptr; - - auto *ValueTyExpr = dyn_cast(EltTuple->getElement(1)); - if (!ValueTyExpr) - return nullptr; - - keyTypeRepr = KeyTyExpr->getTypeRepr(); - valueTypeRepr = ValueTyExpr->getTypeRepr(); - } else { - auto *TE = dyn_cast(DE->getElement(0)); - if (!TE) return nullptr; - - auto *TRE = dyn_cast_or_null(TE->getTypeRepr()); - if (!TRE || TRE->getEllipsisLoc().isValid()) return nullptr; - while (TRE->isParenType()) { - TRE = dyn_cast_or_null(TRE->getElementType(0)); - if (!TRE || TRE->getEllipsisLoc().isValid()) return nullptr; - } - - assert(TRE->getElements().size() == 2); - keyTypeRepr = TRE->getElementType(0); - valueTypeRepr = TRE->getElementType(1); - } - - auto *NewTypeRepr = new (getASTContext()) DictionaryTypeRepr( - keyTypeRepr, valueTypeRepr, - /*FIXME:colonLoc=*/SourceLoc(), - SourceRange(DE->getLBracketLoc(), DE->getRBracketLoc())); - return new (getASTContext()) TypeExpr(NewTypeRepr); - } - - // Reinterpret arrow expr T1 -> T2 as function type. - // FIXME: support 'inout', etc. - if (auto *AE = dyn_cast(E)) { - if (!AE->isFolded()) return nullptr; - - auto diagnoseMissingParens = [](ASTContext &ctx, TypeRepr *tyR) { - bool isVoid = false; - if (const auto Void = dyn_cast(tyR)) { - if (Void->getNameRef().isSimpleName(ctx.Id_Void)) { - isVoid = true; - } - } - - if (isVoid) { - ctx.Diags.diagnose(tyR->getStartLoc(), diag::function_type_no_parens) - .fixItReplace(tyR->getStartLoc(), "()"); - } else { - ctx.Diags.diagnose(tyR->getStartLoc(), diag::function_type_no_parens) - .highlight(tyR->getSourceRange()) - .fixItInsert(tyR->getStartLoc(), "(") - .fixItInsertAfter(tyR->getEndLoc(), ")"); - } - }; - - auto &ctx = getASTContext(); - auto extractInputTypeRepr = [&](Expr *E) -> TupleTypeRepr * { - if (!E) - return nullptr; - if (auto *TyE = dyn_cast(E)) { - auto ArgRepr = TyE->getTypeRepr(); - if (auto *TTyRepr = dyn_cast(ArgRepr)) - return TTyRepr; - diagnoseMissingParens(ctx, ArgRepr); - return TupleTypeRepr::create(ctx, {ArgRepr}, ArgRepr->getSourceRange()); - } - if (auto *TE = dyn_cast(E)) - if (TE->getNumElements() == 0) - return TupleTypeRepr::createEmpty(getASTContext(), - TE->getSourceRange()); - - // When simplifying a type expr like "(P1 & P2) -> (P3 & P4) -> Int", - // it may have been folded at the same time; recursively simplify it. - if (auto ArgsTypeExpr = simplifyTypeExpr(E)) { - auto ArgRepr = ArgsTypeExpr->getTypeRepr(); - if (auto *TTyRepr = dyn_cast(ArgRepr)) - return TTyRepr; - diagnoseMissingParens(ctx, ArgRepr); - return TupleTypeRepr::create(ctx, {ArgRepr}, ArgRepr->getSourceRange()); - } - return nullptr; - }; - - auto extractTypeRepr = [&](Expr *E) -> TypeRepr * { - if (!E) - return nullptr; - if (auto *TyE = dyn_cast(E)) - return TyE->getTypeRepr(); - if (auto *TE = dyn_cast(E)) - if (TE->getNumElements() == 0) - return TupleTypeRepr::createEmpty(ctx, TE->getSourceRange()); - - // When simplifying a type expr like "P1 & P2 -> P3 & P4 -> Int", - // it may have been folded at the same time; recursively simplify it. - if (auto ArgsTypeExpr = simplifyTypeExpr(E)) - return ArgsTypeExpr->getTypeRepr(); - return nullptr; - }; - - TupleTypeRepr *ArgsTypeRepr = extractInputTypeRepr(AE->getArgsExpr()); - if (!ArgsTypeRepr) { - ctx.Diags.diagnose(AE->getArgsExpr()->getLoc(), - diag::expected_type_before_arrow); - auto ArgRange = AE->getArgsExpr()->getSourceRange(); - auto ErrRepr = new (ctx) ErrorTypeRepr(ArgRange); - ArgsTypeRepr = - TupleTypeRepr::create(ctx, {ErrRepr}, ArgRange); - } - - TypeRepr *ResultTypeRepr = extractTypeRepr(AE->getResultExpr()); - if (!ResultTypeRepr) { - ctx.Diags.diagnose(AE->getResultExpr()->getLoc(), - diag::expected_type_after_arrow); - ResultTypeRepr = new (ctx) - ErrorTypeRepr(AE->getResultExpr()->getSourceRange()); - } - - auto NewTypeRepr = new (ctx) - FunctionTypeRepr(nullptr, ArgsTypeRepr, AE->getAsyncLoc(), - AE->getThrowsLoc(), AE->getArrowLoc(), ResultTypeRepr); - return new (ctx) TypeExpr(NewTypeRepr); - } - - // Fold 'P & Q' into a composition type - if (auto *binaryExpr = dyn_cast(E)) { - bool isComposition = false; - // look at the name of the operator, if it is a '&' we can create the - // composition TypeExpr - auto fn = binaryExpr->getFn(); - if (auto Overload = dyn_cast(fn)) { - for (auto Decl : Overload->getDecls()) - if (Decl->getBaseName() == "&") { - isComposition = true; - break; - } - } else if (auto *Decl = dyn_cast(fn)) { - if (Decl->getName().isSimpleName() && - Decl->getName().getBaseName() == "&") - isComposition = true; - } - - if (isComposition) { - // The protocols we are composing - SmallVector Types; - - auto lhsExpr = binaryExpr->getArg()->getElement(0); - if (auto *lhs = dyn_cast(lhsExpr)) { - Types.push_back(lhs->getTypeRepr()); - } else if (isa(lhsExpr)) { - // If the lhs is another binary expression, we have a multi element - // composition: 'A & B & C' is parsed as ((A & B) & C); we get - // the protocols from the lhs here - if (auto expr = simplifyTypeExpr(lhsExpr)) - if (auto *repr = dyn_cast(expr->getTypeRepr())) - // add the protocols to our list - for (auto proto : repr->getTypes()) - Types.push_back(proto); - else - return nullptr; - else - return nullptr; - } else - return nullptr; - - // Add the rhs which is just a TypeExpr - auto *rhs = dyn_cast(binaryExpr->getArg()->getElement(1)); - if (!rhs) return nullptr; - Types.push_back(rhs->getTypeRepr()); - - auto CompRepr = CompositionTypeRepr::create(getASTContext(), Types, - lhsExpr->getStartLoc(), - binaryExpr->getSourceRange()); - return new (getASTContext()) TypeExpr(CompRepr); - } - } - - return nullptr; -} - -void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) { - if (KPE->isObjC()) - return; - - if (!KPE->getComponents().empty()) - return; - - TypeRepr *rootType = nullptr; - SmallVector components; - auto &DE = getASTContext().Diags; - - // Pre-order visit of a sequence foo.bar[0]?.baz, which means that the - // components are pushed in reverse order. - auto traversePath = [&](Expr *expr, bool isInParsedPath, - bool emitErrors = true) { - Expr *outermostExpr = expr; - // We can end up in scenarios where the key path has contextual type, - // but is missing a leading dot. This can happen when we have an - // implicit TypeExpr or an implicit DeclRefExpr. - auto diagnoseMissingDot = [&]() { - DE.diagnose(expr->getLoc(), - diag::expr_swift_keypath_not_starting_with_dot) - .fixItInsert(expr->getStartLoc(), "."); - }; - while (1) { - // Base cases: we've reached the top. - if (auto TE = dyn_cast(expr)) { - assert(!isInParsedPath); - rootType = TE->getTypeRepr(); - if (TE->isImplicit() && !KPE->expectsContextualRoot()) - diagnoseMissingDot(); - return; - } else if (isa(expr)) { - assert(isInParsedPath); - // Nothing here: the type is either the root, or is inferred. - return; - } else if (!KPE->expectsContextualRoot() && expr->isImplicit() && - isa(expr)) { - assert(!isInParsedPath); - diagnoseMissingDot(); - return; - } - - // Recurring cases: - if (auto SE = dyn_cast(expr)) { - // .self, the identity component. - components.push_back(KeyPathExpr::Component::forIdentity( - SE->getSelfLoc())); - expr = SE->getSubExpr(); - } else if (auto UDE = dyn_cast(expr)) { - // .foo - components.push_back(KeyPathExpr::Component::forUnresolvedProperty( - UDE->getName(), UDE->getLoc())); - - expr = UDE->getBase(); - } else if (auto SE = dyn_cast(expr)) { - // .[0] or just plain [0] - components.push_back( - KeyPathExpr::Component::forUnresolvedSubscriptWithPrebuiltIndexExpr( - getASTContext(), SE->getIndex(), SE->getArgumentLabels(), - SE->getLoc())); - - expr = SE->getBase(); - } else if (auto BOE = dyn_cast(expr)) { - // .? or ? - components.push_back(KeyPathExpr::Component::forUnresolvedOptionalChain( - BOE->getQuestionLoc())); - - expr = BOE->getSubExpr(); - } else if (auto FVE = dyn_cast(expr)) { - // .! or ! - components.push_back(KeyPathExpr::Component::forUnresolvedOptionalForce( - FVE->getExclaimLoc())); - - expr = FVE->getSubExpr(); - } else if (auto OEE = dyn_cast(expr)) { - // Do nothing: this is implied to exist as the last expression, by the - // BindOptionalExprs, but is irrelevant to the components. - (void)outermostExpr; - assert(OEE == outermostExpr); - expr = OEE->getSubExpr(); - } else { - if (emitErrors) { - // \() may be an attempt to write a string interpolation outside - // of a string literal; diagnose this case specially. - if (isa(expr) || isa(expr)) { - DE.diagnose(expr->getLoc(), - diag::expr_string_interpolation_outside_string); - } else { - DE.diagnose(expr->getLoc(), - diag::expr_swift_keypath_invalid_component); - } - } - components.push_back(KeyPathExpr::Component()); - return; - } - } - }; - - auto root = KPE->getParsedRoot(); - auto path = KPE->getParsedPath(); - - if (path) { - traversePath(path, /*isInParsedPath=*/true); - - // This path looks like \Foo.Bar.[0].baz, which means Foo.Bar has to be a - // type. - if (root) { - if (auto TE = dyn_cast(root)) { - rootType = TE->getTypeRepr(); - } else { - // FIXME: Probably better to catch this case earlier and force-eval as - // TypeExpr. - DE.diagnose(root->getLoc(), - diag::expr_swift_keypath_not_starting_with_type); - - // Traverse this path for recovery purposes: it may be a typo like - // \Foo.property.[0]. - traversePath(root, /*isInParsedPath=*/false, - /*emitErrors=*/false); - } - } - } else { - traversePath(root, /*isInParsedPath=*/false); - } - - // Key paths must be spelled with at least one component. - if (components.empty()) { - // Passes further down the pipeline expect keypaths to always have at least - // one component, so stuff an invalid component in the AST for recovery. - components.push_back(KeyPathExpr::Component()); - } - - std::reverse(components.begin(), components.end()); - - KPE->setRootType(rootType); - KPE->resolveComponents(getASTContext(), components); -} - -Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) { - // If constructor call is expected to produce an optional let's not attempt - // this optimization because literal initializers aren't failable. - if (!getASTContext().LangOpts.isSwiftVersionAtLeast(5)) { - if (!ExprStack.empty()) { - auto *parent = ExprStack.back(); - if (isa(parent) || isa(parent)) - return nullptr; - } - } - - auto *call = dyn_cast(E); - if (!call || call->getNumArguments() != 1) - return nullptr; - - auto *typeExpr = dyn_cast(call->getFn()); - if (!typeExpr) - return nullptr; - - auto *argExpr = call->getArg()->getSemanticsProvidingExpr(); - auto *literal = dyn_cast(argExpr); - if (!literal) - return nullptr; - - auto *protocol = TypeChecker::getLiteralProtocol(getASTContext(), literal); - if (!protocol) - return nullptr; - - Type castTy; - if (auto precheckedTy = typeExpr->getInstanceType()) { - castTy = precheckedTy; - } else { - const auto options = - TypeResolutionOptions(TypeResolverContext::InExpression) | - TypeResolutionFlags::SilenceErrors; - - const auto resolution = - TypeResolution::forContextual(DC, options, [](auto unboundTy) { - // FIXME: Don't let unbound generic types escape type resolution. - // For now, just return the unbound generic type. - return unboundTy; - }); - const auto result = resolution.resolveType(typeExpr->getTypeRepr()); - if (result->hasError()) - return nullptr; - castTy = result; - } - - if (!castTy || !castTy->getAnyNominal()) - return nullptr; - - // Don't bother to convert deprecated selector syntax. - if (auto selectorTy = getASTContext().getSelectorType()) { - if (castTy->isEqual(selectorTy)) - return nullptr; - } - - SmallVector conformances; - return castTy->getAnyNominal()->lookupConformance(DC->getParentModule(), - protocol, conformances) - ? CoerceExpr::forLiteralInit(getASTContext(), argExpr, - call->getSourceRange(), - typeExpr->getTypeRepr()) - : nullptr; -} - -/// Pre-check the expression, validating any types that occur in the -/// expression and folding sequence expressions. -bool ConstraintSystem::preCheckExpression(Expr *&expr, DeclContext *dc, - bool replaceInvalidRefsWithErrors) { - PreCheckExpression preCheck(dc, expr, replaceInvalidRefsWithErrors); - // Perform the pre-check. - if (auto result = expr->walk(preCheck)) { - expr = result; - return false; - } - return true; -} - void ParentConditionalConformance::diagnoseConformanceStack( DiagnosticEngine &diags, SourceLoc loc, ArrayRef conformances) { From 964f640636d848318d35fc48e25df624a55fef91 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 1 Oct 2020 13:09:58 -0700 Subject: [PATCH 162/745] Drop "Private Deps" Flag In order for type body fingerprints to work, these declarations must always be included. Drop the ability to turn this off. --- .../AST/AbstractSourceFileDepGraphFactory.h | 7 +- include/swift/AST/Decl.h | 3 - include/swift/Basic/LangOptions.h | 5 - include/swift/Driver/Compilation.h | 8 - include/swift/Option/Options.td | 5 - lib/AST/AbstractSourceFileDepGraphFactory.cpp | 5 +- lib/AST/Decl.cpp | 23 --- lib/AST/FrontendSourceFileDepGraphFactory.cpp | 154 ++++-------------- lib/AST/FrontendSourceFileDepGraphFactory.h | 2 - lib/Driver/Compilation.cpp | 3 - lib/Driver/Driver.cpp | 3 - lib/Driver/ToolChains.cpp | 2 - lib/Frontend/CompilerInvocation.cpp | 3 - .../reference-dependencies-fine.swift | 5 +- .../reference-dependencies-members-fine.swift | 5 +- .../MockingFineGrainedDependencyGraphs.cpp | 12 +- .../MockingFineGrainedDependencyGraphs.h | 8 +- .../UnitTestSourceFileDepGraphFactory.cpp | 8 +- .../UnitTestSourceFileDepGraphFactory.h | 4 +- 19 files changed, 50 insertions(+), 215 deletions(-) diff --git a/include/swift/AST/AbstractSourceFileDepGraphFactory.h b/include/swift/AST/AbstractSourceFileDepGraphFactory.h index 80ed4122ec1b2..a0991efd8ad93 100644 --- a/include/swift/AST/AbstractSourceFileDepGraphFactory.h +++ b/include/swift/AST/AbstractSourceFileDepGraphFactory.h @@ -24,10 +24,6 @@ namespace fine_grained_dependencies { /// \c SourceFile or a unit test class AbstractSourceFileDepGraphFactory { protected: - /// To match the existing system, set this to false. - /// To include even private entities and get intra-file info, set to true. - const bool includePrivateDeps; - /// If there was an error, cannot get accurate info. const bool hadCompilationError; @@ -48,8 +44,7 @@ class AbstractSourceFileDepGraphFactory { public: /// Expose this layer to enable faking up a constructor for testing. /// See the instance variable comments for explanation. - AbstractSourceFileDepGraphFactory(bool includePrivateDeps, - bool hadCompilationError, + AbstractSourceFileDepGraphFactory(bool hadCompilationError, StringRef swiftDeps, StringRef fileFingerprint, bool emitDotFileAfterConstruction, diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 588ef350f497d..fe17f0cae96c8 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -948,9 +948,6 @@ class alignas(1 << DeclAlignInBits) Decl { /// If this returns true, the decl can be safely casted to ValueDecl. bool isPotentiallyOverridable() const; - /// Returns true if this Decl cannot be seen by any other source file - bool isPrivateToEnclosingFile() const; - /// If an alternative module name is specified for this decl, e.g. using /// @_originalDefinedIn attribute, this function returns this module name. StringRef getAlternateModuleName() const; diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index bd704cb29c4d9..5ee6f3e74e2fb 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -334,11 +334,6 @@ namespace swift { /// file. bool EmitFineGrainedDependencySourcefileDotFiles = false; - /// To mimic existing system, set to false. - /// To experiment with including file-private and private dependency info, - /// set to true. - bool FineGrainedDependenciesIncludeIntrafileOnes = false; - /// Whether to enable experimental differentiable programming features: /// `@differentiable` declaration attribute, etc. bool EnableExperimentalDifferentiableProgramming = false; diff --git a/include/swift/Driver/Compilation.h b/include/swift/Driver/Compilation.h index 5e6e1972f9dfd..6c75516c71e90 100644 --- a/include/swift/Driver/Compilation.h +++ b/include/swift/Driver/Compilation.h @@ -272,9 +272,6 @@ class Compilation { /// needed. const bool EmitFineGrainedDependencyDotFileAfterEveryImport; - /// Experiment with intrafile dependencies - const bool FineGrainedDependenciesIncludeIntrafileOnes; - /// Experiment with source-range-based dependencies const bool EnableSourceRangeDependencies; @@ -318,7 +315,6 @@ class Compilation { bool OnlyOneDependencyFile = false, bool VerifyFineGrainedDependencyGraphAfterEveryImport = false, bool EmitFineGrainedDependencyDotFileAfterEveryImport = false, - bool FineGrainedDependenciesIncludeIntrafileOnes = false, bool EnableSourceRangeDependencies = false, bool CompareIncrementalSchemes = false, StringRef CompareIncrementalSchemesPath = "", @@ -389,10 +385,6 @@ class Compilation { return EmitFineGrainedDependencyDotFileAfterEveryImport; } - bool getFineGrainedDependenciesIncludeIntrafileOnes() const { - return FineGrainedDependenciesIncludeIntrafileOnes; - } - bool getEnableSourceRangeDependencies() const { return EnableSourceRangeDependencies; } diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index d60baf2396495..7c80c7c03e68e 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -182,11 +182,6 @@ Flag<["-"], "driver-emit-fine-grained-dependency-dot-file-after-every-import">, InternalDebugOpt, HelpText<"Emit dot files every time driver imports an fine-grained swiftdeps file.">; -def fine_grained_dependency_include_intrafile : -Flag<["-"], "fine-grained-dependency-include-intrafile">, -Flags<[FrontendOption, HelpHidden]>, -HelpText<"Include within-file dependencies.">; - def emit_fine_grained_dependency_sourcefile_dot_files : Flag<["-"], "emit-fine-grained-dependency-sourcefile-dot-files">, Flags<[FrontendOption, HelpHidden]>, diff --git a/lib/AST/AbstractSourceFileDepGraphFactory.cpp b/lib/AST/AbstractSourceFileDepGraphFactory.cpp index 6a4ccfdf78ab6..b70ea103a4040 100644 --- a/lib/AST/AbstractSourceFileDepGraphFactory.cpp +++ b/lib/AST/AbstractSourceFileDepGraphFactory.cpp @@ -33,11 +33,10 @@ using namespace fine_grained_dependencies; //============================================================================== AbstractSourceFileDepGraphFactory::AbstractSourceFileDepGraphFactory( - bool includePrivateDeps, bool hadCompilationError, StringRef swiftDeps, + bool hadCompilationError, StringRef swiftDeps, StringRef fileFingerprint, bool emitDotFileAfterConstruction, DiagnosticEngine &diags) - : includePrivateDeps(includePrivateDeps), - hadCompilationError(hadCompilationError), swiftDeps(swiftDeps.str()), + : hadCompilationError(hadCompilationError), swiftDeps(swiftDeps.str()), fileFingerprint(fileFingerprint.str()), emitDotFileAfterConstruction(emitDotFileAfterConstruction), diags(diags) { } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a330b63faedcb..2aa5a8f9700b6 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -8098,26 +8098,3 @@ void swift::simple_display(llvm::raw_ostream &out, AnyFunctionRef fn) { else out << "closure"; } - -bool Decl::isPrivateToEnclosingFile() const { - if (auto *VD = dyn_cast(this)) - return VD->getFormalAccess() <= AccessLevel::FilePrivate; - switch (getKind()) { - case DeclKind::Import: - case DeclKind::PatternBinding: - case DeclKind::EnumCase: - case DeclKind::TopLevelCode: - case DeclKind::IfConfig: - case DeclKind::PoundDiagnostic: - return true; - - case DeclKind::Extension: - case DeclKind::InfixOperator: - case DeclKind::PrefixOperator: - case DeclKind::PostfixOperator: - return false; - - default: - llvm_unreachable("everything else is a ValueDecl"); - } -} diff --git a/lib/AST/FrontendSourceFileDepGraphFactory.cpp b/lib/AST/FrontendSourceFileDepGraphFactory.cpp index c89e1e0318e98..5ca65c70fb870 100644 --- a/lib/AST/FrontendSourceFileDepGraphFactory.cpp +++ b/lib/AST/FrontendSourceFileDepGraphFactory.cpp @@ -65,50 +65,6 @@ static std::string mangleTypeAsContext(const NominalTypeDecl *NTD) { return !NTD ? "" : Mangler.mangleTypeAsContextUSR(NTD); } -//============================================================================== -// MARK: Privacy queries -//============================================================================== - -/// Return true if \ref ED does not contain a member that can affect other -/// files. -static bool allMembersArePrivate(const ExtensionDecl *ED) { - return std::all_of( - ED->getMembers().begin(), ED->getMembers().end(), - [](const Decl *d) { return d->isPrivateToEnclosingFile(); }); -} - -/// \ref inheritedType, an inherited protocol, return true if this inheritance -/// cannot affect other files. -static bool extendedTypeIsPrivate(TypeLoc inheritedType) { - auto type = inheritedType.getType(); - if (!type) - return true; - - if (!type->isExistentialType()) { - // Be conservative. We don't know how to deal with other extended types. - return false; - } - - auto layout = type->getExistentialLayout(); - assert(!layout.explicitSuperclass && - "Should not have a subclass existential " - "in the inheritance clause of an extension"); - for (auto protoTy : layout.getProtocols()) { - if (!protoTy->getDecl()->isPrivateToEnclosingFile()) - return false; - } - - return true; -} - -/// Return true if \ref ED does not inherit a protocol that can affect other -/// files. Was called "justMembers" in ReferenceDependencies.cpp -/// \ref ED might be null. -static bool allInheritedProtocolsArePrivate(const ExtensionDecl *ED) { - return std::all_of(ED->getInherited().begin(), ED->getInherited().end(), - extendedTypeIsPrivate); -} - //============================================================================== // MARK: DependencyKey - creation for Decls //============================================================================== @@ -262,24 +218,11 @@ FrontendSourceFileDepGraphFactory::FrontendSourceFileDepGraphFactory( SourceFile *SF, StringRef outputPath, const DependencyTracker &depTracker, const bool alsoEmitDotFile) : AbstractSourceFileDepGraphFactory( - computeIncludePrivateDeps(SF), SF->getASTContext().hadError(), + SF->getASTContext().hadError(), outputPath, getInterfaceHash(SF), alsoEmitDotFile, SF->getASTContext().Diags), SF(SF), depTracker(depTracker) {} -bool FrontendSourceFileDepGraphFactory::computeIncludePrivateDeps( - SourceFile *SF) { - // Since, when fingerprints are enabled, - // the parser diverts token hashing into per-body fingerprints - // before it can know if a difference is in a private type, - // in order to be able to test the changed fingerprints - // we force the inclusion of private declarations when fingerprints - // are enabled. - return SF->getASTContext() - .LangOpts.FineGrainedDependenciesIncludeIntrafileOnes || - SF->getASTContext().LangOpts.EnableTypeFingerprints; -} - /// Centralize the invariant that the fingerprint of the whole file is the /// interface hash std::string FrontendSourceFileDepGraphFactory::getFingerprint(SourceFile *SF) { @@ -297,10 +240,6 @@ namespace { /// Takes all the Decls in a SourceFile, and collects them into buckets by /// groups of DeclKinds. Also casts them to more specific types struct DeclFinder { - /// Existing system excludes private decls in some cases. - /// In the future, we might not want to do this, so use bool to decide. - const bool includePrivateDecls; - // The extracted Decls: ConstPtrVec extensions; ConstPtrVec operators; @@ -319,19 +258,17 @@ struct DeclFinder { /// Construct me and separates the Decls. // clang-format off DeclFinder(ArrayRef topLevelDecls, - const bool includePrivateDecls, - LookupClassMember lookupClassMember) - : includePrivateDecls(includePrivateDecls) { + LookupClassMember lookupClassMember) { for (const Decl *const D : topLevelDecls) { - select(D, extensions, false) || + select(D, extensions) || select(D, operators, false) || + DeclKind::PostfixOperator>(D, operators) || select( - D, precedenceGroups, false) || + D, precedenceGroups) || select(D, topNominals, true) || + DeclKind::Class, DeclKind::Protocol>(D, topNominals) || select(D, topValues, true); + DeclKind::Accessor>(D, topValues); } // clang-format on // The order is important because some of these use instance variables @@ -360,18 +297,7 @@ struct DeclFinder { /// (indirectly recursive) void findNominalsAndOperatorsIn(const NominalTypeDecl *const NTD, const ExtensionDecl *ED = nullptr) { - if (excludeIfPrivate(NTD)) - return; - const bool exposedProtocolIsExtended = - ED && !allInheritedProtocolsArePrivate(ED); - if (ED && !includePrivateDecls && !exposedProtocolIsExtended && - std::all_of( - ED->getMembers().begin(), ED->getMembers().end(), - [&](const Decl *D) { return D->isPrivateToEnclosingFile(); })) { - return; - } - if (includePrivateDecls || !ED || exposedProtocolIsExtended) - allNominals.push_back(NTD); + allNominals.push_back(NTD); potentialMemberHolders.push_back(NTD); findNominalsAndOperatorsInMembers(ED ? ED->getMembers() : NTD->getMembers()); @@ -383,7 +309,7 @@ struct DeclFinder { void findNominalsAndOperatorsInMembers(const DeclRange members) { for (const Decl *const D : members) { auto *VD = dyn_cast(D); - if (!VD || excludeIfPrivate(VD)) + if (!VD) continue; if (VD->getName().isOperator()) memberOperatorDecls.push_back(cast(D)); @@ -396,19 +322,20 @@ struct DeclFinder { void findValuesInExtensions() { for (const auto *ED : extensions) { const auto *const NTD = ED->getExtendedNominal(); - if (!NTD || excludeIfPrivate(NTD)) + if (!NTD) { continue; - if (!includePrivateDecls && - (!allInheritedProtocolsArePrivate(ED) || allMembersArePrivate(ED))) - continue; - for (const auto *member : ED->getMembers()) - if (const auto *VD = dyn_cast(member)) - if (VD->hasName() && - (includePrivateDecls || !VD->isPrivateToEnclosingFile())) { - const auto *const NTD = ED->getExtendedNominal(); - if (NTD) - valuesInExtensions.push_back(std::make_pair(NTD, VD)); - } + } + + for (const auto *member : ED->getMembers()) { + const auto *VD = dyn_cast(member); + if (!VD || !VD->hasName()) { + continue; + } + + if (const auto *const NTD = ED->getExtendedNominal()) { + valuesInExtensions.push_back(std::make_pair(NTD, VD)); + } + } } } @@ -431,30 +358,19 @@ struct DeclFinder { /// \returns true if successful. template - bool select(const Decl *const D, ConstPtrVec &foundDecls, - const bool canExcludePrivateDecls) { + bool select(const Decl *const D, ConstPtrVec &foundDecls) { if (D->getKind() == firstKind) { - auto *dd = cast(D); - const bool exclude = canExcludePrivateDecls && excludeIfPrivate(dd); - if (!exclude) - foundDecls.push_back(cast(D)); + foundDecls.push_back(cast(D)); return true; } - return select(D, foundDecls, - canExcludePrivateDecls); + return select(D, foundDecls); } /// Terminate the template recursion. template - bool select(const Decl *const D, ConstPtrVec &foundDecls, - bool) { + bool select(const Decl *const D, ConstPtrVec &foundDecls) { return false; } - - /// Return true if \param D should be excluded on privacy grounds. - bool excludeIfPrivate(const Decl *const D) { - return !includePrivateDecls && D->isPrivateToEnclosingFile(); - } }; } // namespace @@ -464,7 +380,7 @@ void FrontendSourceFileDepGraphFactory::addAllDefinedDecls() { // Many kinds of Decls become top-level depends. - DeclFinder declFinder(SF->getTopLevelDecls(), includePrivateDeps, + DeclFinder declFinder(SF->getTopLevelDecls(), [this](VisibleDeclConsumer &consumer) { SF->lookupClassMembers({}, consumer); }); @@ -513,14 +429,11 @@ class UsedDeclEnumerator { const DependencyKey sourceFileInterface; const DependencyKey sourceFileImplementation; - const bool includeIntrafileDeps; - function_ref createDefUse; public: UsedDeclEnumerator( SourceFile *SF, const DependencyTracker &depTracker, StringRef swiftDeps, - bool includeIntrafileDeps, function_ref createDefUse) : SF(SF), depTracker(depTracker), swiftDeps(swiftDeps), @@ -528,7 +441,7 @@ class UsedDeclEnumerator { DeclAspect::interface, swiftDeps)), sourceFileImplementation(DependencyKey::createKeyForWholeSourceFile( DeclAspect::implementation, swiftDeps)), - includeIntrafileDeps(includeIntrafileDeps), createDefUse(createDefUse) { + createDefUse(createDefUse) { } public: @@ -581,11 +494,6 @@ class UsedDeclEnumerator { return; } - bool isPrivate = subject->isPrivateToEnclosingFile(); - if (isPrivate && !includeIntrafileDeps) { - return; - } - std::string context = DependencyKey::computeContextForProvidedEntity( subject); @@ -604,7 +512,7 @@ class UsedDeclEnumerator { } // end namespace void FrontendSourceFileDepGraphFactory::addAllUsedDecls() { - UsedDeclEnumerator(SF, depTracker, swiftDeps, includePrivateDeps, + UsedDeclEnumerator(SF, depTracker, swiftDeps, [&](const DependencyKey &def, const DependencyKey &use) { addAUsedDecl(def, use); }) @@ -644,7 +552,7 @@ FrontendSourceFileDepGraphFactory::getFingerprintIfAny(const Decl *d) { ModuleDepGraphFactory::ModuleDepGraphFactory(ModuleDecl *Mod, bool emitDot) : AbstractSourceFileDepGraphFactory( - /*include private*/ true, Mod->getASTContext().hadError(), + Mod->getASTContext().hadError(), Mod->getNameStr(), "0xBADBEEF", emitDot, Mod->getASTContext().Diags), Mod(Mod) {} @@ -656,7 +564,7 @@ void ModuleDepGraphFactory::addAllDefinedDecls() { SmallVector TopLevelDecls; Mod->getTopLevelDecls(TopLevelDecls); - DeclFinder declFinder(TopLevelDecls, includePrivateDeps, + DeclFinder declFinder(TopLevelDecls, [this](VisibleDeclConsumer &consumer) { return Mod->lookupClassMembers({}, consumer); }); diff --git a/lib/AST/FrontendSourceFileDepGraphFactory.h b/lib/AST/FrontendSourceFileDepGraphFactory.h index 898357f72117f..6b2740f54baf2 100644 --- a/lib/AST/FrontendSourceFileDepGraphFactory.h +++ b/lib/AST/FrontendSourceFileDepGraphFactory.h @@ -35,8 +35,6 @@ class FrontendSourceFileDepGraphFactory private: static std::string getFingerprint(SourceFile *SF); - - static bool computeIncludePrivateDeps(SourceFile *SF); static std::string getInterfaceHash(SourceFile *SF); void addAllDefinedDecls() override; diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index 31f3d0ce4f1dd..11332f958b731 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -123,7 +123,6 @@ Compilation::Compilation(DiagnosticEngine &Diags, bool OnlyOneDependencyFile, bool VerifyFineGrainedDependencyGraphAfterEveryImport, bool EmitFineGrainedDependencyDotFileAfterEveryImport, - bool FineGrainedDependenciesIncludeIntrafileOnes, bool EnableSourceRangeDependencies, bool CompareIncrementalSchemes, StringRef CompareIncrementalSchemesPath, @@ -152,8 +151,6 @@ Compilation::Compilation(DiagnosticEngine &Diags, VerifyFineGrainedDependencyGraphAfterEveryImport), EmitFineGrainedDependencyDotFileAfterEveryImport( EmitFineGrainedDependencyDotFileAfterEveryImport), - FineGrainedDependenciesIncludeIntrafileOnes( - FineGrainedDependenciesIncludeIntrafileOnes), EnableSourceRangeDependencies(EnableSourceRangeDependencies), EnableCrossModuleIncrementalBuild(EnableCrossModuleIncrementalBuild) { diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 783d297595fb6..ed09cf7bb028e 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1020,8 +1020,6 @@ Driver::buildCompilation(const ToolChain &TC, const bool EmitFineGrainedDependencyDotFileAfterEveryImport = ArgList->hasArg( options:: OPT_driver_emit_fine_grained_dependency_dot_file_after_every_import); - const bool FineGrainedDependenciesIncludeIntrafileOnes = - ArgList->hasArg(options::OPT_fine_grained_dependency_include_intrafile); const bool EnableCrossModuleDependencies = ArgList->hasArg( options::OPT_enable_experimental_cross_module_incremental_build); @@ -1047,7 +1045,6 @@ Driver::buildCompilation(const ToolChain &TC, OnlyOneDependencyFile, VerifyFineGrainedDependencyGraphAfterEveryImport, EmitFineGrainedDependencyDotFileAfterEveryImport, - FineGrainedDependenciesIncludeIntrafileOnes, EnableSourceRangeDependencies, CompareIncrementalSchemes, CompareIncrementalSchemesPath, diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index a03ea4fe52e9f..2ff01dd73b4fa 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -259,8 +259,6 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_O_Group); inputArgs.AddLastArg(arguments, options::OPT_RemoveRuntimeAsserts); inputArgs.AddLastArg(arguments, options::OPT_AssumeSingleThreaded); - inputArgs.AddLastArg(arguments, - options::OPT_fine_grained_dependency_include_intrafile); inputArgs.AddLastArg(arguments, options::OPT_emit_fine_grained_dependency_sourcefile_dot_files); inputArgs.AddLastArg(arguments, options::OPT_package_description_version); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 2c8f39248235c..3d0f6a6180bb1 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -441,9 +441,6 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, if (Args.hasArg(OPT_emit_fine_grained_dependency_sourcefile_dot_files)) Opts.EmitFineGrainedDependencySourcefileDotFiles = true; - if (Args.hasArg(OPT_fine_grained_dependency_include_intrafile)) - Opts.FineGrainedDependenciesIncludeIntrafileOnes = true; - if (Args.hasArg(OPT_enable_experimental_additive_arithmetic_derivation)) Opts.EnableExperimentalAdditiveArithmeticDerivedConformances = true; diff --git a/test/Incremental/Dependencies/reference-dependencies-fine.swift b/test/Incremental/Dependencies/reference-dependencies-fine.swift index 3c76ee51fa7a1..e2fba6628bf13 100644 --- a/test/Incremental/Dependencies/reference-dependencies-fine.swift +++ b/test/Incremental/Dependencies/reference-dependencies-fine.swift @@ -5,10 +5,9 @@ // RUN: %empty-directory(%t) // RUN: cp %s %t/main.swift -// Need -fine-grained-dependency-include-intrafile to be invarient wrt type-body-fingerprints enabled/disabled -// RUN: %target-swift-frontend -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/../Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps // Check that the output is deterministic. -// RUN: %target-swift-frontend -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/../Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps +// RUN: %target-swift-frontend -typecheck -primary-file %t/main.swift %S/../Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps +// RUN: %target-swift-frontend -typecheck -primary-file %t/main.swift %S/../Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps // Merge each entry onto one line and sort to overcome order differences // RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh %swift-dependency-tool %t.swiftdeps %t-processed.swiftdeps diff --git a/test/Incremental/Dependencies/reference-dependencies-members-fine.swift b/test/Incremental/Dependencies/reference-dependencies-members-fine.swift index 01e06c2dc6cb7..0fec187e1d91d 100644 --- a/test/Incremental/Dependencies/reference-dependencies-members-fine.swift +++ b/test/Incremental/Dependencies/reference-dependencies-members-fine.swift @@ -5,10 +5,9 @@ // RUN: %empty-directory(%t) // RUN: cp %s %t/main.swift -// Need -fine-grained-dependency-include-intrafile to be invarient wrt type-body-fingerprints enabled/disabled -// RUN: %target-swift-frontend -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/../Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps +// RUN: %target-swift-frontend -typecheck -primary-file %t/main.swift %S/../Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps -// RUN: %target-swift-frontend -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/../Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps +// RUN: %target-swift-frontend -typecheck -primary-file %t/main.swift %S/../Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps // RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh %swift-dependency-tool %t.swiftdeps %t-processed.swiftdeps // RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh %swift-dependency-tool %t-2.swiftdeps %t-2-processed.swiftdeps diff --git a/unittests/Driver/MockingFineGrainedDependencyGraphs.cpp b/unittests/Driver/MockingFineGrainedDependencyGraphs.cpp index 6ac82c50d45f2..549e3cb4af234 100644 --- a/unittests/Driver/MockingFineGrainedDependencyGraphs.cpp +++ b/unittests/Driver/MockingFineGrainedDependencyGraphs.cpp @@ -26,11 +26,11 @@ using namespace mocking_fine_grained_dependency_graphs; void mocking_fine_grained_dependency_graphs::simulateLoad( ModuleDepGraph &g, const driver::Job *cmd, const DependencyDescriptions &dependencyDescriptions, - StringRef interfaceHashIfNonEmpty, const bool includePrivateDeps, + StringRef interfaceHashIfNonEmpty, const bool hadCompilationError) { const auto changes = getChangesForSimulatedLoad( g, cmd, dependencyDescriptions, interfaceHashIfNonEmpty, - includePrivateDeps, hadCompilationError); + hadCompilationError); assert(changes && "simulated load should always succeed"); } @@ -38,7 +38,7 @@ ModuleDepGraph::Changes mocking_fine_grained_dependency_graphs::getChangesForSimulatedLoad( ModuleDepGraph &g, const driver::Job *cmd, const DependencyDescriptions &dependencyDescriptions, - StringRef interfaceHashIfNonEmpty, const bool includePrivateDeps, + StringRef interfaceHashIfNonEmpty, const bool hadCompilationError) { StringRef swiftDeps = cmd->getOutput().getAdditionalOutputForType(file_types::TY_SwiftDeps); @@ -51,7 +51,7 @@ mocking_fine_grained_dependency_graphs::getChangesForSimulatedLoad( auto sfdg = UnitTestSourceFileDepGraphFactory( - includePrivateDeps, hadCompilationError, swiftDeps, interfaceHash, + hadCompilationError, swiftDeps, interfaceHash, g.emitFineGrainedDependencyDotFileAfterEveryImport, dependencyDescriptions, diags) .construct(); @@ -63,11 +63,11 @@ std::vector mocking_fine_grained_dependency_graphs::simulateReload( ModuleDepGraph &g, const driver::Job *cmd, const DependencyDescriptions &dependencyDescriptions, - StringRef interfaceHashIfNonEmpty, const bool includePrivateDeps, + StringRef interfaceHashIfNonEmpty, const bool hadCompilationError) { const auto changedNodes = getChangesForSimulatedLoad( g, cmd, dependencyDescriptions, interfaceHashIfNonEmpty, - includePrivateDeps, hadCompilationError); + hadCompilationError); if (!changedNodes) return g.getAllJobs(); return g.findJobsToRecompileWhenNodesChange(changedNodes.getValue()); diff --git a/unittests/Driver/MockingFineGrainedDependencyGraphs.h b/unittests/Driver/MockingFineGrainedDependencyGraphs.h index 0bb5e298355b9..2bd042006bce1 100644 --- a/unittests/Driver/MockingFineGrainedDependencyGraphs.h +++ b/unittests/Driver/MockingFineGrainedDependencyGraphs.h @@ -35,9 +35,8 @@ namespace mocking_fine_grained_dependency_graphs { /// \param cmd The \c Job whose dependency info will be loaded. /// \param dependencyDescriptions Dependency info, see below /// \param interfaceHashIfNonEmpty If non-empty, overrides the default simulated -/// interface hash \param includePrivateDeps Include file-private declarations -/// in the dependency information. \param hadCompilationError Simulate a -/// compilation error. +/// in the dependency information. +/// \param hadCompilationError Simulate a compilation error. /// /// Fails an assertion if the information is not valid (for instance a /// fingerprint where it should not be). @@ -68,7 +67,6 @@ namespace mocking_fine_grained_dependency_graphs { void simulateLoad(ModuleDepGraph &g, const driver::Job *cmd, const DependencyDescriptions &dependencyDescriptions, StringRef interfaceHashIfNonEmpty = StringRef(), - const bool includePrivateDeps = true, const bool hadCompilationError = false); /// Same as \ref simulateLoad, but returns the specifically changed nodes or @@ -78,7 +76,6 @@ ModuleDepGraph::Changes getChangesForSimulatedLoad(ModuleDepGraph &g, const driver::Job *cmd, const DependencyDescriptions &dependencyDescriptions, StringRef interfaceHashIfNonEmpty = StringRef(), - const bool includePrivateDeps = true, const bool hadCompilationError = false); /// Simulates the driver reloading a swiftdeps file after a job has run. @@ -91,7 +88,6 @@ std::vector simulateReload(ModuleDepGraph &g, const driver::Job *cmd, const DependencyDescriptions &dependencyDescriptions, StringRef interfaceHashIfNonEmpty = StringRef(), - const bool includePrivateDeps = true, const bool hadCompilationError = false); std::vector diff --git a/unittests/Driver/UnitTestSourceFileDepGraphFactory.cpp b/unittests/Driver/UnitTestSourceFileDepGraphFactory.cpp index 34047395b1411..50cce1c47a95c 100644 --- a/unittests/Driver/UnitTestSourceFileDepGraphFactory.cpp +++ b/unittests/Driver/UnitTestSourceFileDepGraphFactory.cpp @@ -104,9 +104,7 @@ Optional UnitTestSourceFileDepGraphFactory::parseADefinedDecl( StringRef s, const NodeKind kind, const DeclAspect aspect) { static const char *privatePrefix = "#"; - const bool isPrivate = s.consume_front(privatePrefix); - if (isPrivate && !includePrivateDeps) - return None; + s.consume_front(privatePrefix); const std::string context = parseContext(s.split(fingerprintSeparator).first, kind); std::string name = parseName(s.split(fingerprintSeparator).first, kind); @@ -125,9 +123,7 @@ UnitTestSourceFileDepGraphFactory::parseAUsedDecl(const StringRef argString, // Someday, we might differentiate. const DeclAspect aspectOfDefUsed = DeclAspect::interface; - const bool isHolderPrivate = s.consume_front(privateHolderPrefix); - if (!includePrivateDeps && isHolderPrivate) - return None; + s.consume_front(privateHolderPrefix); const auto defUseStrings = s.split(defUseSeparator); const auto context = parseContext(defUseStrings.first, kind); const auto name = parseName(defUseStrings.first, kind); diff --git a/unittests/Driver/UnitTestSourceFileDepGraphFactory.h b/unittests/Driver/UnitTestSourceFileDepGraphFactory.h index 16b29321a2037..9298177aa39e7 100644 --- a/unittests/Driver/UnitTestSourceFileDepGraphFactory.h +++ b/unittests/Driver/UnitTestSourceFileDepGraphFactory.h @@ -27,12 +27,12 @@ class UnitTestSourceFileDepGraphFactory public: UnitTestSourceFileDepGraphFactory( - bool includePrivateDeps, bool hadCompilationError, StringRef swiftDeps, + bool hadCompilationError, StringRef swiftDeps, StringRef fileFingerprint, bool emitDotFileAfterConstruction, const DependencyDescriptions &dependencyDescriptions, DiagnosticEngine &diags) : AbstractSourceFileDepGraphFactory( - includePrivateDeps, hadCompilationError, swiftDeps, fileFingerprint, + hadCompilationError, swiftDeps, fileFingerprint, emitDotFileAfterConstruction, diags), dependencyDescriptions(dependencyDescriptions) {} From da764ebe39fae5a23f32cb852953eaa24e5c20f6 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 1 Oct 2020 14:47:54 -0700 Subject: [PATCH 163/745] Update README.md Remove the GPU builder status for TensorFlow which is currently not running. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 67163d29fdba8..8c58de108142a 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,6 @@ |---|:---:|:---:| |**[Ubuntu 18.04](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/x86_64_ubuntu_18_04_tensorflow.json)** | x86_64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-18.04-tensorflow/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-18.04-tensorflow)| |**[macOS 10.13](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/x86_64_macos_high_sierra_tensorflow.json)** | x86_64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-macOS-tensorflow/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-macOS-tensorflow)| -|**[Ubuntu 18.04 (GPU)](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/x86_64_ubuntu_18_04_tensorflow_gpu.json)** | x86_64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-18.04-tensorflow-gpu/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-18.04-tensorflow-gpu)| ## Welcome to Swift From 28f3e442135d7ae60c1522e8da41322350f84171 Mon Sep 17 00:00:00 2001 From: Fero Date: Fri, 2 Oct 2020 01:16:22 +0200 Subject: [PATCH 164/745] Fix typo --- lib/IRGen/GenPointerAuth.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/IRGen/GenPointerAuth.cpp b/lib/IRGen/GenPointerAuth.cpp index 6f2a7d2c17fc7..aeae73999d0d8 100644 --- a/lib/IRGen/GenPointerAuth.cpp +++ b/lib/IRGen/GenPointerAuth.cpp @@ -401,7 +401,7 @@ PointerAuthEntity::getDeclDiscriminator(IRGenModule &IGM) const { "discriminator for foreign declaration not supported yet!"); // Special case: methods that are witnesses to Actor.enqueue(partialTask:) - // have their own descriminator, which is shared across all actor classes. + // have their own discriminator, which is shared across all actor classes. if (constant.hasFuncDecl()) { auto func = dyn_cast(constant.getFuncDecl()); if (func->isActorEnqueuePartialTaskWitness()) { From b0b546873ca8c47b99cdae99110f94d2cbaa5283 Mon Sep 17 00:00:00 2001 From: Fero Date: Fri, 2 Oct 2020 01:26:33 +0200 Subject: [PATCH 165/745] Fix typo --- lib/Serialization/Serialization.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index e577c12090c06..df2c0ca859e2d 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2489,11 +2489,11 @@ class Serializer::DeclSerializer : public DeclVisitor { auto *storage = dyn_cast(value); auto access = value->getFormalAccess(); - // Emit the private descriminator for private decls. + // Emit the private discriminator for private decls. // FIXME: We shouldn't need to encode this for /all/ private decls. // In theory we can follow the same rules as mangling and only include // the outermost private context. - bool shouldEmitPrivateDescriminator = + bool shouldEmitPrivateDiscriminator = access <= swift::AccessLevel::FilePrivate && !value->getDeclContext()->isLocalContext(); @@ -2507,10 +2507,10 @@ class Serializer::DeclSerializer : public DeclVisitor { storage->getFormalAccess() >= swift::AccessLevel::Internal && storage->hasPrivateAccessor())); - if (shouldEmitFilenameForPrivate || shouldEmitPrivateDescriminator) { + if (shouldEmitFilenameForPrivate || shouldEmitPrivateDiscriminator) { auto topLevelContext = value->getDeclContext()->getModuleScopeContext(); if (auto *enclosingFile = dyn_cast(topLevelContext)) { - if (shouldEmitPrivateDescriminator) { + if (shouldEmitPrivateDiscriminator) { Identifier discriminator = enclosingFile->getDiscriminatorForPrivateValue(value); unsigned abbrCode = From af71c5dfbdfe16ec694aabf72f83f16bc68b935b Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 1 Oct 2020 19:34:43 -0700 Subject: [PATCH 166/745] [Property Wrappers] Ban non-member property wrappers with observers, just like we do for regular non-member properties. --- lib/Sema/TypeCheckStorage.cpp | 2 ++ test/decl/var/property_wrappers.swift | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 3a4ef5a4c61d2..9c8f67aef23a7 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -2737,6 +2737,8 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, initializer); pbd->setInit(0, initializer); pbd->setInitializerChecked(0); + } else if (var->hasObservers() && !dc->isTypeContext()) { + var->diagnose(diag::observingprop_requires_initializer); } if (var->getOpaqueResultTypeDecl()) { diff --git a/test/decl/var/property_wrappers.swift b/test/decl/var/property_wrappers.swift index 1a3ef8a2fff9c..8f712128d3c5b 100644 --- a/test/decl/var/property_wrappers.swift +++ b/test/decl/var/property_wrappers.swift @@ -167,6 +167,11 @@ func testLocalContext() { @WrapperWithStorageRef var hasProjection = 10 let _: Wrapper = $hasProjection + + @WrapperWithInitialValue + var uninitialized: Int { // expected-error {{non-member observing properties require an initializer}} + didSet {} + } } // --------------------------------------------------------------------------- From 763bcf900529152ac35f566cb5e887a4c867d9f7 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 1 Oct 2020 20:11:40 -0700 Subject: [PATCH 167/745] [SILGen] Don't use assign_by_wrapper for local property wrappers if there is an initial wrapped value. --- lib/SILGen/SILGenLValue.cpp | 6 ++++++ test/SILGen/property_wrapper_local.swift | 14 +++++++++++++ .../di_property_wrappers_errors.swift | 20 +++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 6bcef2767ebb2..443433584ca2f 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1319,6 +1319,12 @@ namespace { if (!(isAssignmentToSelfParamInInit || VD->getDeclContext()->isLocalContext())) return false; + // If this var isn't in a type context, assignment will always use the setter + // if there is an initial value. + if (!VD->getDeclContext()->isTypeContext() && + wrapperInfo.wrappedValuePlaceholder->getOriginalWrappedValue()) + return false; + // If we have a nonmutating setter on a value type, the call // captures all of 'self' and we cannot rewrite an assignment // into an initialization. diff --git a/test/SILGen/property_wrapper_local.swift b/test/SILGen/property_wrapper_local.swift index a20d83a0291c0..dd29b6d55467e 100644 --- a/test/SILGen/property_wrapper_local.swift +++ b/test/SILGen/property_wrapper_local.swift @@ -51,6 +51,20 @@ func testLocalWrapper() { // CHECK-LABEL: sil private [ossa] @$s22property_wrapper_local16testLocalWrapperyyF5valueL_Sivs : $@convention(thin) (Int, @guaranteed { var Wrapper }) -> () { } +func testInitialValue() { + // CHECK-LABEL: sil hidden [ossa] @$s22property_wrapper_local16testInitialValueyyF : $@convention(thin) () -> () { + + @Wrapper var value: Int = 10 + // CHECK: function_ref @$s22property_wrapper_local16testInitialValueyyF5valueL_SivpfP : $@convention(thin) (Int) -> Wrapper + + value = 15 + // CHECK: function_ref @$s22property_wrapper_local16testInitialValueyyF5valueL_Sivs : $@convention(thin) (Int, @guaranteed { var Wrapper }) -> () + // CHECK-NOT: assign_by_wrapper + // CHECK: return + + // CHECK-LABEL: sil private [ossa] @$s22property_wrapper_local16testInitialValueyyF5valueL_SivpfP : $@convention(thin) (Int) -> Wrapper { +} + @propertyWrapper enum Lazy { case uninitialized(() -> Value) diff --git a/test/SILOptimizer/di_property_wrappers_errors.swift b/test/SILOptimizer/di_property_wrappers_errors.swift index d83948c0a78d8..7c63e45a47f1c 100644 --- a/test/SILOptimizer/di_property_wrappers_errors.swift +++ b/test/SILOptimizer/di_property_wrappers_errors.swift @@ -99,3 +99,23 @@ struct UseWrapperWithAutoclosure { } // expected-error{{return from initializer without initializing all stored properties}} // expected-note@-1{{'self.wrapped' not initialized}} } + +@propertyWrapper +struct Wrapper { + var wrappedValue: T +} + +func local() { + var anotherVar: String // expected-note {{variable defined here}} + + @Wrapper var value = 10 { + didSet { + anotherVar = "hello!" + } + } + + value = 15 // expected-error {{variable 'anotherVar' used by function definition before being initialized}} + + anotherVar = "hello!" + _ = anotherVar +} From 60427845781ba4c247e932338fde85dd46ec0bcf Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 30 Sep 2020 18:41:21 -0400 Subject: [PATCH 168/745] Sema: Tweak re-declaration checking behavior to match old logic The parser's own re-declaration checking for local declarations has slightly different behavior than Sema's re-declaration check. Whereas Sema rejects this: class C { let x = 123 func x() {} } The parser accepts this: func f() { let x = 123 func x() {} } With Sema's check now handling both cases, fudge the behavior to match the parser in the local case. --- lib/Sema/TypeCheckDeclPrimary.cpp | 5 +++++ test/Sema/redeclaration-checking.swift | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 7c882a9c232a5..99becbbdc005a 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -572,6 +572,11 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const { if (currentDC->isTypeContext() != other->getDeclContext()->isTypeContext()) continue; + // In local context, only consider exact name matches. + if (currentDC->isLocalContext() && + current->getName() != other->getName()) + continue; + // Check whether the overload signatures conflict (ignoring the type for // now). auto otherSig = other->getOverloadSignature(); diff --git a/test/Sema/redeclaration-checking.swift b/test/Sema/redeclaration-checking.swift index 9445d612f94b1..558c23faac717 100644 --- a/test/Sema/redeclaration-checking.swift +++ b/test/Sema/redeclaration-checking.swift @@ -93,4 +93,9 @@ func stmtTest() { // expected-error@-2 {{invalid redeclaration of 'x'}} // expected-warning@-3 2{{never used}} // expected-warning@-4 {{unreachable}} +} + +func fullNameTest() { + let x = 123 // expected-warning {{never used}} + func x() {} } \ No newline at end of file From 28388384f2f6d65d78ce23838ef296bfc1fe66d2 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 30 Sep 2020 01:34:33 -0400 Subject: [PATCH 169/745] AST: UnqualifiedLookup only finds forward references to outer local bindings when IncludeOuterResults is set The old behavior was that ASTScope would introduce all VarDecls defined in a BraceStmt at the beginning of the BraceStmt. I recently enabled the use of PatternEntryDeclScopes, which introduce the binding at its actual source location instead of at the beginning of the parent statement. This patch now makes use of the new information by having UnqualifiedLookupFlags::IncludeOuterResults toggle between the two behaviors. When searching for outer results, we also consider all VarDecls in a BraceStmt, not just those in scope. This is implemented by giving AbstractASTScopeDeclConsumer a new entry point, consumePossiblyNotInScope(). When looking up into a BraceStmt, all VarDecls are passed in to this entry point. The default implementation does nothing, which means that ASTScope::lookupSingleLocalDecl() now respects source locations when searching for bindings, just like parse-time lookup. However, Sema's preCheckExpression() pass, which sets Flags::IgnoreOuterResults, will continue to find forward-referenced VarDecls, just as it did with the old context-based DeclContext lookup. --- include/swift/AST/ASTScope.h | 16 ++++++++++++-- include/swift/AST/NameLookup.h | 8 +++++++ lib/AST/ASTScopeCreation.cpp | 22 +++++++++++++++++++- lib/AST/ASTScopeLookup.cpp | 18 ++++------------ lib/AST/UnqualifiedLookup.cpp | 38 +++++++++++++++++++++++++++++++--- 5 files changed, 82 insertions(+), 20 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 80cc61ac08be3..fd05c8e453f8b 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -1658,10 +1658,22 @@ class CaseStmtBodyScope final : public ASTScopeImpl { }; class BraceStmtScope final : public AbstractStmtScope { + BraceStmt *const stmt; + + /// Declarations which are in scope from the beginning of the statement. + SmallVector localFuncsAndTypes; + + /// Declarations that are normally in scope only after their + /// definition. + SmallVector localVars; public: - BraceStmt *const stmt; - BraceStmtScope(BraceStmt *e) : stmt(e) {} + BraceStmtScope(BraceStmt *e, + SmallVector localFuncsAndTypes, + SmallVector localVars) + : stmt(e), + localFuncsAndTypes(localFuncsAndTypes), + localVars(localVars) {} virtual ~BraceStmtScope() {} protected: diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index d57cd07ea8b4c..748a6d27fdf28 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -613,6 +613,14 @@ class AbstractASTScopeDeclConsumer { lookInMembers(DeclContext *const scopeDC, NominalTypeDecl *const nominal) = 0; + /// Called for local VarDecls that might not yet be in scope. + /// + /// Note that the set of VarDecls visited here are going to be a + /// superset of those visited in consume(). + virtual bool consumePossiblyNotInScope(ArrayRef values) { + return false; + } + /// Called right before looking at the parent scope of a BraceStmt. /// /// \return true if the lookup should be stopped at this point. diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 0c6d22b4bca5c..3dbe233239604 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -667,10 +667,30 @@ class NodeAdder NullablePtr visitBraceStmt(BraceStmt *bs, ASTScopeImpl *p, ScopeCreator &scopeCreator) { + SmallVector localFuncsAndTypes; + SmallVector localVars; + + // All types and functions are visible anywhere within a brace statement + // scope. When ordering matters (i.e. var decl) we will have split the brace + // statement into nested scopes. + for (auto braceElement : bs->getElements()) { + if (auto localBinding = braceElement.dyn_cast()) { + if (auto *vd = dyn_cast(localBinding)) { + if (isa(vd) || isa(vd)) { + localFuncsAndTypes.push_back(vd); + } else if (auto *var = dyn_cast(localBinding)) { + localVars.push_back(var); + } + } + } + } + auto maybeBraceScope = - scopeCreator.ifUniqueConstructExpandAndInsert(p, bs); + scopeCreator.ifUniqueConstructExpandAndInsert( + p, bs, std::move(localFuncsAndTypes), std::move(localVars)); if (auto *s = scopeCreator.getASTContext().Stats) ++s->getFrontendCounters().NumBraceStmtASTScopes; + return maybeBraceScope.getPtrOr(p); } diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index 47c103e83e608..0821ce8c7fbe7 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -371,20 +371,10 @@ bool DifferentiableAttributeScope::lookupLocalsOrMembers( } bool BraceStmtScope::lookupLocalsOrMembers(DeclConsumer consumer) const { - // All types and functions are visible anywhere within a brace statement - // scope. When ordering matters (i.e. var decl) we will have split the brace - // statement into nested scopes. - // - // Don't stop at the first one, there may be local funcs with same base name - // and want them all. - SmallVector localBindings; - for (auto braceElement : stmt->getElements()) { - if (auto localBinding = braceElement.dyn_cast()) { - if (auto *vd = dyn_cast(localBinding)) - localBindings.push_back(vd); - } - } - if (consumer.consume(localBindings, DeclVisibilityKind::LocalVariable)) + if (consumer.consume(localFuncsAndTypes, DeclVisibilityKind::LocalVariable)) + return true; + + if (consumer.consumePossiblyNotInScope(localVars)) return true; if (consumer.finishLookupInBraceStmt(stmt)) diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 20b5aad9311c1..3693f11ae5d8c 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -133,7 +133,12 @@ namespace { /// Can lookup stop searching for results, assuming hasn't looked for outer /// results yet? bool isFirstResultEnough() const; - + + /// Do we want precise scoping of VarDecls? If IncludeOuterResults is on, + /// this is true, which allows us to resolve forward references to + /// local VarDecls from inside local function and closure bodies. + bool hasPreciseScopingOfVarDecls() const; + /// Every time lookup finishes searching a scope, call me /// to record the dividing line between results from first fruitful scope and /// the result. @@ -209,6 +214,8 @@ class ASTScopeDeclConsumerForUnqualifiedLookup bool consume(ArrayRef values, DeclVisibilityKind vis, NullablePtr baseDC = nullptr) override; + bool consumePossiblyNotInScope(ArrayRef vars) override; + /// returns true if finished bool lookInMembers(DeclContext *const scopeDC, NominalTypeDecl *const nominal) override; @@ -462,6 +469,10 @@ bool UnqualifiedLookupFactory::isFirstResultEnough() const { return !Results.empty() && !options.contains(Flags::IncludeOuterResults); } +bool UnqualifiedLookupFactory::hasPreciseScopingOfVarDecls() const { + return !options.contains(Flags::IncludeOuterResults); +} + void UnqualifiedLookupFactory::recordCompletionOfAScope() { // OK to call (NOOP) if there are more inner results and Results is empty if (IndexOfFirstOuterResult == 0) @@ -556,12 +567,18 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::consume( if (factory.isOriginallyTypeLookup && !isa(value)) continue; - // Try to resolve the base for unqualified instance member - // references. This is used by lookInMembers(). if (auto *var = dyn_cast(value)) { + // Try to resolve the base for unqualified instance member + // references. This is used by lookInMembers(). if (var->getName() == factory.Ctx.Id_self) { maybeUpdateSelfDC(var); } + + // Local VarDecls with a pattern binding are visited as part of their + // BraceStmt when hasPreciseScopingOfVarDecls() is off. + if (var->getParentPatternBinding() && + !factory.hasPreciseScopingOfVarDecls()) + continue; } if (!value->getName().matchesRef(factory.Name.getFullName())) @@ -587,6 +604,21 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::consume( return factory.isFirstResultEnough(); } +bool ASTScopeDeclConsumerForUnqualifiedLookup::consumePossiblyNotInScope( + ArrayRef vars) { + if (factory.hasPreciseScopingOfVarDecls()) + return false; + + for (auto *var : vars) { + if (!factory.Name.getFullName().isSimpleName(var->getName())) + continue; + + factory.Results.push_back(LookupResultEntry(var)); + } + + return false; +} + bool ASTScopeDeclGatherer::consume(ArrayRef valuesArg, DeclVisibilityKind, NullablePtr) { From 06fc9c5a5ed64128f15e89b54070f458a6960014 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 30 Sep 2020 01:38:07 -0400 Subject: [PATCH 170/745] Sema: Simulate old name lookup behavior when parser lookup is off Before performing an UnqualifiedLookup with Flags::IncludeOuterResults turned on, call ASTScope::lookupSingleLocalDecl() to find local bindings that precede the current source location. If this fails, we perform an unqualified lookup to try to find forward references to captures, type members, and top-level declarations. --- lib/Sema/PreCheckExpr.cpp | 115 ++++++++++++++++++------------- test/NameLookup/edge-cases.swift | 21 ++++++ 2 files changed, 89 insertions(+), 47 deletions(-) create mode 100644 test/NameLookup/edge-cases.swift diff --git a/lib/Sema/PreCheckExpr.cpp b/lib/Sema/PreCheckExpr.cpp index 7cf0c1a0b0a86..0da6f8379f65f 100644 --- a/lib/Sema/PreCheckExpr.cpp +++ b/lib/Sema/PreCheckExpr.cpp @@ -351,12 +351,77 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, // name/module qualifier to access top-level name. lookupOptions |= NameLookupFlags::IncludeOuterResults; - if (Loc.isInvalid()) - DC = DC->getModuleScopeContext(); + LookupResult Lookup; - auto Lookup = TypeChecker::lookupUnqualified(DC, Name, Loc, lookupOptions); + bool AllDeclRefs = true; + SmallVector ResultValues; auto &Context = DC->getASTContext(); + if (Context.LangOpts.DisableParserLookup) { + // First, look for a local binding in scope. + if (Loc.isValid() && !Name.isOperator()) { + SmallVector localDecls; + ASTScope::lookupLocalDecls(DC->getParentSourceFile(), + Name.getFullName(), Loc, + /*stopAfterInnermostBraceStmt=*/false, + ResultValues); + for (auto *localDecl : ResultValues) { + Lookup.add(LookupResultEntry(localDecl), /*isOuter=*/false); + } + } + } + + if (!Lookup) { + // Now, look for all local bindings, even forward references, as well + // as type members and top-level declarations. + if (Loc.isInvalid()) + DC = DC->getModuleScopeContext(); + + Lookup = TypeChecker::lookupUnqualified(DC, Name, Loc, lookupOptions); + + ValueDecl *localDeclAfterUse = nullptr; + auto isValid = [&](ValueDecl *D) { + // If we find something in the current context, it must be a forward + // reference, because otherwise if it was in scope, it would have + // been returned by the call to ASTScope::lookupLocalDecls() above. + if (D->getDeclContext()->isLocalContext() && + D->getDeclContext() == DC && + (Context.LangOpts.DisableParserLookup || + (Loc.isValid() && D->getLoc().isValid() && + Context.SourceMgr.isBeforeInBuffer(Loc, D->getLoc()) && + !isa(D)))) { + localDeclAfterUse = D; + return false; + } + return true; + }; + AllDeclRefs = + findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), + /*breakOnMember=*/true, ResultValues, isValid); + + // If local declaration after use is found, check outer results for + // better matching candidates. + if (ResultValues.empty() && localDeclAfterUse) { + auto innerDecl = localDeclAfterUse; + while (localDeclAfterUse) { + if (Lookup.outerResults().empty()) { + Context.Diags.diagnose(Loc, diag::use_local_before_declaration, Name); + Context.Diags.diagnose(innerDecl, diag::decl_declared_here, + localDeclAfterUse->getName()); + Expr *error = new (Context) ErrorExpr(UDRE->getSourceRange()); + return error; + } + + Lookup.shiftDownResults(); + ResultValues.clear(); + localDeclAfterUse = nullptr; + AllDeclRefs = + findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), + /*breakOnMember=*/true, ResultValues, isValid); + } + } + } + if (!Lookup) { // If we failed lookup of an operator, check to see if this is a range // operator misspelling. Otherwise try to diagnose a juxtaposition @@ -487,50 +552,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, // FIXME: Need to refactor the way we build an AST node from a lookup result! - SmallVector ResultValues; - ValueDecl *localDeclAfterUse = nullptr; - auto isValid = [&](ValueDecl *D) { - // FIXME: The source-location checks won't make sense once - // EnableASTScopeLookup is the default. - // - // Note that we allow forward references to types, because they cannot - // capture. - if (Loc.isValid() && D->getLoc().isValid() && - D->getDeclContext()->isLocalContext() && - D->getDeclContext() == DC && - Context.SourceMgr.isBeforeInBuffer(Loc, D->getLoc()) && - !isa(D)) { - localDeclAfterUse = D; - return false; - } - return true; - }; - bool AllDeclRefs = - findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), - /*breakOnMember=*/true, ResultValues, isValid); - - // If local declaration after use is found, check outer results for - // better matching candidates. - if (localDeclAfterUse) { - auto innerDecl = localDeclAfterUse; - while (localDeclAfterUse) { - if (Lookup.outerResults().empty()) { - Context.Diags.diagnose(Loc, diag::use_local_before_declaration, Name); - Context.Diags.diagnose(innerDecl, diag::decl_declared_here, - localDeclAfterUse->getName()); - Expr *error = new (Context) ErrorExpr(UDRE->getSourceRange()); - return error; - } - - Lookup.shiftDownResults(); - ResultValues.clear(); - localDeclAfterUse = nullptr; - AllDeclRefs = - findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), - /*breakOnMember=*/true, ResultValues, isValid); - } - } - // If we have an unambiguous reference to a type decl, form a TypeExpr. if (Lookup.size() == 1 && UDRE->getRefKind() == DeclRefKind::Ordinary && isa(Lookup[0].getValueDecl())) { diff --git a/test/NameLookup/edge-cases.swift b/test/NameLookup/edge-cases.swift new file mode 100644 index 0000000000000..cc0798480b43c --- /dev/null +++ b/test/NameLookup/edge-cases.swift @@ -0,0 +1,21 @@ +// RUN: %target-typecheck-verify-swift -disable-parser-lookup + +struct A {} +struct B {} + +func other() -> A {} + +func takesB(_: B) {} + +func multipleLocalResults1() { + func other() -> B {} + let result = other() + takesB(result) +} + +func multipleLocalResults2() { + func other() -> B {} + let result = other() + takesB(result) + let other: Int = 123 // expected-warning {{never used}} +} From 8168dda2bc4d15aee8d6ba729230d58791556f76 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 30 Sep 2020 01:26:05 -0400 Subject: [PATCH 171/745] AST: Remove dead code from ASTScopeDeclConsumerForUnqualifiedLookup::consume() --- lib/AST/UnqualifiedLookup.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 3693f11ae5d8c..16af6d0ba6145 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -584,17 +584,6 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::consume( if (!value->getName().matchesRef(factory.Name.getFullName())) continue; - // In order to preserve the behavior of the existing context-based lookup, - // which finds all results for non-local variables at the top level instead - // of stopping at the first one, ignore results at the top level that are - // not local variables. The caller \c lookInASTScopes will - // then do the appropriate work when the scope lookup fails. In - // FindLocalVal::visitBraceStmt, it sees PatternBindingDecls, not VarDecls, - // so a VarDecl at top level would not be found by the context-based lookup. - if (isa(value->getDeclContext()) && - (vis != DeclVisibilityKind::LocalVariable || isa(value))) - return false; - factory.Results.push_back(LookupResultEntry(value)); #ifndef NDEBUG factory.stopForDebuggingIfAddingTargetLookupResult(factory.Results.back()); From f738a57040ab1e42f46c8a73c1f6cc0b8a34bdae Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 30 Sep 2020 01:29:39 -0400 Subject: [PATCH 172/745] ASTScope: Remove DeclVisibilityKind parameter from AbstractASTScopeDeclConsumer::consume() It wasn't used for anything, and it was always set based on whether the declaration in question was a GenericTypeParamDecl, a ParamDecl, or something else. --- include/swift/AST/ASTScope.h | 1 - include/swift/AST/NameLookup.h | 4 ++-- lib/AST/ASTScopeLookup.cpp | 42 ++++++++++++++-------------------- lib/AST/UnqualifiedLookup.cpp | 8 +++---- 4 files changed, 22 insertions(+), 33 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index fd05c8e453f8b..aadfbff462ad4 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -444,7 +444,6 @@ class ASTScopeImpl { // It is not an instance variable or inherited type. static bool lookupLocalBindingsInPattern(const Pattern *p, - DeclVisibilityKind vis, DeclConsumer consumer); /// When lookup must stop before the outermost scope, return the scope to stop diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 748a6d27fdf28..f9a6df52039a5 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -603,7 +603,7 @@ class AbstractASTScopeDeclConsumer { /// of type -vs- instance lookup results. /// /// \return true if the lookup should be stopped at this point. - virtual bool consume(ArrayRef values, DeclVisibilityKind vis, + virtual bool consume(ArrayRef values, NullablePtr baseDC = nullptr) = 0; /// Look for members of a nominal type or extension scope. @@ -644,7 +644,7 @@ class ASTScopeDeclGatherer : public AbstractASTScopeDeclConsumer { public: virtual ~ASTScopeDeclGatherer() = default; - bool consume(ArrayRef values, DeclVisibilityKind vis, + bool consume(ArrayRef values, NullablePtr baseDC = nullptr) override; /// Eventually this functionality should move into ASTScopeLookup diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index 0821ce8c7fbe7..2c70497c03c7c 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -222,7 +222,7 @@ bool ASTScopeImpl::lookInGenericParametersOf( SmallVector bindings; for (auto *param : paramList.get()->getParams()) bindings.push_back(param); - if (consumer.consume(bindings, DeclVisibilityKind::GenericParameter)) + if (consumer.consume(bindings)) return true; return false; } @@ -289,28 +289,26 @@ PatternEntryInitializerScope::getLookupParent() const { bool GenericParamScope::lookupLocalsOrMembers(DeclConsumer consumer) const { auto *param = paramList->getParams()[index]; - return consumer.consume({param}, DeclVisibilityKind::GenericParameter); + return consumer.consume({param}); } bool PatternEntryDeclScope::lookupLocalsOrMembers(DeclConsumer consumer) const { if (vis != DeclVisibilityKind::LocalVariable) return false; // look in self type will find this later - return lookupLocalBindingsInPattern(getPattern(), vis, consumer); + return lookupLocalBindingsInPattern(getPattern(), consumer); } bool ForEachPatternScope::lookupLocalsOrMembers(DeclConsumer consumer) const { - return lookupLocalBindingsInPattern( - stmt->getPattern(), DeclVisibilityKind::LocalVariable, consumer); + return lookupLocalBindingsInPattern(stmt->getPattern(), consumer); } bool CaseLabelItemScope::lookupLocalsOrMembers(DeclConsumer consumer) const { - return lookupLocalBindingsInPattern( - item.getPattern(), DeclVisibilityKind::LocalVariable, consumer); + return lookupLocalBindingsInPattern(item.getPattern(), consumer); } bool CaseStmtBodyScope::lookupLocalsOrMembers(DeclConsumer consumer) const { for (auto *var : stmt->getCaseBodyVariablesOrEmptyArray()) - if (consumer.consume({var}, DeclVisibilityKind::LocalVariable)) + if (consumer.consume({var})) return true; return false; @@ -320,13 +318,12 @@ bool FunctionBodyScope::lookupLocalsOrMembers( DeclConsumer consumer) const { if (auto *paramList = decl->getParameters()) { for (auto *paramDecl : *paramList) - if (consumer.consume({paramDecl}, DeclVisibilityKind::FunctionParameter)) + if (consumer.consume({paramDecl})) return true; } if (decl->getDeclContext()->isTypeContext()) { - return consumer.consume({decl->getImplicitSelfDecl()}, - DeclVisibilityKind::FunctionParameter); + return consumer.consume({decl->getImplicitSelfDecl()}); } // Consider \c var t: T { (did/will/)get/set { ... t }} @@ -335,7 +332,7 @@ bool FunctionBodyScope::lookupLocalsOrMembers( // then t needs to be found as a local binding: if (auto *accessor = dyn_cast(decl)) { if (auto *storage = accessor->getStorage()) - if (consumer.consume({storage}, DeclVisibilityKind::LocalVariable)) + if (consumer.consume({storage})) return true; } @@ -346,7 +343,7 @@ bool SpecializeAttributeScope::lookupLocalsOrMembers( DeclConsumer consumer) const { if (auto *params = whatWasSpecialized->getGenericParams()) for (auto *param : params->getParams()) - if (consumer.consume({param}, DeclVisibilityKind::GenericParameter)) + if (consumer.consume({param})) return true; return false; } @@ -356,7 +353,7 @@ bool DifferentiableAttributeScope::lookupLocalsOrMembers( auto visitAbstractFunctionDecl = [&](AbstractFunctionDecl *afd) { if (auto *params = afd->getGenericParams()) for (auto *param : params->getParams()) - if (consumer.consume({param}, DeclVisibilityKind::GenericParameter)) + if (consumer.consume({param})) return true; return false; }; @@ -371,7 +368,7 @@ bool DifferentiableAttributeScope::lookupLocalsOrMembers( } bool BraceStmtScope::lookupLocalsOrMembers(DeclConsumer consumer) const { - if (consumer.consume(localFuncsAndTypes, DeclVisibilityKind::LocalVariable)) + if (consumer.consume(localFuncsAndTypes)) return true; if (consumer.consumePossiblyNotInScope(localVars)) @@ -390,8 +387,7 @@ bool PatternEntryInitializerScope::lookupLocalsOrMembers( decl->getInitContext(0)); if (initContext) { if (auto *selfParam = initContext->getImplicitSelfDecl()) { - return consumer.consume({selfParam}, - DeclVisibilityKind::FunctionParameter); + return consumer.consume({selfParam}); } } return false; @@ -399,9 +395,7 @@ bool PatternEntryInitializerScope::lookupLocalsOrMembers( bool CaptureListScope::lookupLocalsOrMembers(DeclConsumer consumer) const { for (auto &e : expr->getCaptureList()) { - if (consumer.consume( - {e.Var}, - DeclVisibilityKind::LocalVariable)) // or FunctionParameter?? + if (consumer.consume({e.Var})) return true; } return false; @@ -410,26 +404,24 @@ bool CaptureListScope::lookupLocalsOrMembers(DeclConsumer consumer) const { bool ClosureParametersScope::lookupLocalsOrMembers( DeclConsumer consumer) const { for (auto param : *closureExpr->getParameters()) - if (consumer.consume({param}, DeclVisibilityKind::FunctionParameter)) + if (consumer.consume({param})) return true; return false; } bool ConditionalClausePatternUseScope::lookupLocalsOrMembers( DeclConsumer consumer) const { - return lookupLocalBindingsInPattern( - pattern, DeclVisibilityKind::LocalVariable, consumer); + return lookupLocalBindingsInPattern(pattern, consumer); } bool ASTScopeImpl::lookupLocalBindingsInPattern(const Pattern *p, - DeclVisibilityKind vis, DeclConsumer consumer) { if (!p) return false; bool isDone = false; p->forEachVariable([&](VarDecl *var) { if (!isDone) - isDone = consumer.consume({var}, vis); + isDone = consumer.consume({var}); }); return isDone; } diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 16af6d0ba6145..91f17451b1f49 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -211,7 +211,7 @@ class ASTScopeDeclConsumerForUnqualifiedLookup void maybeUpdateSelfDC(VarDecl *var); - bool consume(ArrayRef values, DeclVisibilityKind vis, + bool consume(ArrayRef values, NullablePtr baseDC = nullptr) override; bool consumePossiblyNotInScope(ArrayRef vars) override; @@ -561,8 +561,7 @@ void ASTScopeDeclConsumerForUnqualifiedLookup::maybeUpdateSelfDC( } bool ASTScopeDeclConsumerForUnqualifiedLookup::consume( - ArrayRef values, DeclVisibilityKind vis, - NullablePtr baseDC) { + ArrayRef values, NullablePtr baseDC) { for (auto *value: values) { if (factory.isOriginallyTypeLookup && !isa(value)) continue; @@ -609,7 +608,6 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::consumePossiblyNotInScope( } bool ASTScopeDeclGatherer::consume(ArrayRef valuesArg, - DeclVisibilityKind, NullablePtr) { for (auto *v: valuesArg) values.push_back(v); @@ -768,7 +766,7 @@ class ASTScopeDeclConsumerForLocalLookup : name(name), stopAfterInnermostBraceStmt(stopAfterInnermostBraceStmt), results(results) {} - bool consume(ArrayRef values, DeclVisibilityKind vis, + bool consume(ArrayRef values, NullablePtr baseDC) override { for (auto *value: values) { if (!value->getName().matchesRef(name)) From 3ec4ced57db438f54b54d88f2207e762120e8eaa Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 30 Sep 2020 22:05:27 -0400 Subject: [PATCH 173/745] ASTScope: Don't handle top-level bindings in a SourceFile for now --- include/swift/AST/ASTScope.h | 14 +++++------ lib/AST/ASTScopeCreation.cpp | 29 ++++++++++++----------- lib/AST/ASTScopeLookup.cpp | 4 ++-- lib/AST/Decl.cpp | 3 ++- test/NameLookup/scope_map_top_level.swift | 10 ++++---- 5 files changed, 31 insertions(+), 29 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index aadfbff462ad4..f4247df1468c3 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -29,7 +29,7 @@ #define SWIFT_AST_AST_SCOPE_H #include "swift/AST/ASTNode.h" -#include "swift/AST/NameLookup.h" // for DeclVisibilityKind +#include "swift/AST/NameLookup.h" #include "swift/AST/SimpleRequest.h" #include "swift/Basic/Compiler.h" #include "swift/Basic/Debug.h" @@ -1023,10 +1023,10 @@ class AbstractPatternEntryScope : public ASTScopeImpl { public: PatternBindingDecl *const decl; const unsigned patternEntryIndex; - const DeclVisibilityKind vis; + const bool isLocalBinding; AbstractPatternEntryScope(PatternBindingDecl *, unsigned entryIndex, - DeclVisibilityKind); + bool); virtual ~AbstractPatternEntryScope() {} const PatternBindingEntry &getPatternEntry() const; @@ -1043,8 +1043,8 @@ class AbstractPatternEntryScope : public ASTScopeImpl { class PatternEntryDeclScope final : public AbstractPatternEntryScope { public: PatternEntryDeclScope(PatternBindingDecl *pbDecl, unsigned entryIndex, - DeclVisibilityKind vis) - : AbstractPatternEntryScope(pbDecl, entryIndex, vis) {} + bool isLocalBinding) + : AbstractPatternEntryScope(pbDecl, entryIndex, isLocalBinding) {} virtual ~PatternEntryDeclScope() {} protected: @@ -1071,8 +1071,8 @@ class PatternEntryInitializerScope final : public AbstractPatternEntryScope { public: PatternEntryInitializerScope(PatternBindingDecl *pbDecl, unsigned entryIndex, - DeclVisibilityKind vis) - : AbstractPatternEntryScope(pbDecl, entryIndex, vis), + bool isLocalBinding) + : AbstractPatternEntryScope(pbDecl, entryIndex, isLocalBinding), initAsWrittenWhenCreated(pbDecl->getOriginalInit(entryIndex)) {} virtual ~PatternEntryInitializerScope() {} diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 3dbe233239604..aa82c8014afc2 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -701,23 +701,23 @@ class NodeAdder if (auto *var = patternBinding->getSingleVar()) scopeCreator.addChildrenForKnownAttributes(var, parentScope); - const bool isLocalBinding = patternBinding->getDeclContext()->isLocalContext(); - - const DeclVisibilityKind vis = - isLocalBinding ? DeclVisibilityKind::LocalVariable - : DeclVisibilityKind::MemberOfCurrentNominal; auto *insertionPoint = parentScope; for (auto i : range(patternBinding->getNumPatternEntries())) { + bool isLocalBinding = false; + if (auto *varDecl = patternBinding->getAnchoringVarDecl(i)) { + isLocalBinding = varDecl->getDeclContext()->isLocalContext(); + } + insertionPoint = scopeCreator .ifUniqueConstructExpandAndInsert( - insertionPoint, patternBinding, i, vis) + insertionPoint, patternBinding, i, isLocalBinding) .getPtrOr(insertionPoint); - } - ASTScopeAssert(isLocalBinding || insertionPoint == parentScope, - "Bindings at the top-level or members of types should " - "not change the insertion point"); + ASTScopeAssert(isLocalBinding || insertionPoint == parentScope, + "Bindings at the top-level or members of types should " + "not change the insertion point"); + } return insertionPoint; } @@ -991,7 +991,7 @@ PatternEntryDeclScope::expandAScopeThatCreatesANewInsertionPoint( "Original inits are always after the '='"); scopeCreator .constructExpandAndInsertUncheckable( - this, decl, patternEntryIndex, vis); + this, decl, patternEntryIndex, isLocalBinding); } // Add accessors for the variables in this pattern. @@ -1002,7 +1002,7 @@ PatternEntryDeclScope::expandAScopeThatCreatesANewInsertionPoint( // In local context, the PatternEntryDeclScope becomes the insertion point, so // that all any bindings introduecd by the pattern are in scope for subsequent // lookups. - if (vis == DeclVisibilityKind::LocalVariable) + if (isLocalBinding) return {this, "All code that follows is inside this scope"}; return {getParent().get(), "Global and type members do not introduce scopes"}; @@ -1378,8 +1378,9 @@ ASTScopeImpl *LabeledConditionalStmtScope::createNestedConditionalClauseScopes( AbstractPatternEntryScope::AbstractPatternEntryScope( PatternBindingDecl *declBeingScoped, unsigned entryIndex, - DeclVisibilityKind vis) - : decl(declBeingScoped), patternEntryIndex(entryIndex), vis(vis) { + bool isLocalBinding) + : decl(declBeingScoped), patternEntryIndex(entryIndex), + isLocalBinding(isLocalBinding) { ASTScopeAssert(entryIndex < declBeingScoped->getPatternList().size(), "out of bounds"); } diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index 2c70497c03c7c..c49099bce99d2 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -293,8 +293,8 @@ bool GenericParamScope::lookupLocalsOrMembers(DeclConsumer consumer) const { } bool PatternEntryDeclScope::lookupLocalsOrMembers(DeclConsumer consumer) const { - if (vis != DeclVisibilityKind::LocalVariable) - return false; // look in self type will find this later + if (!isLocalBinding) + return false; return lookupLocalBindingsInPattern(getPattern(), consumer); } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 75b354c0d29d9..cdc16e07a366d 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1447,7 +1447,8 @@ void PatternBindingEntry::setInit(Expr *E) { VarDecl *PatternBindingEntry::getAnchoringVarDecl() const { SmallVector variables; getPattern()->collectVariables(variables); - assert(!variables.empty()); + if (variables.empty()) + return nullptr; return variables[0]; } diff --git a/test/NameLookup/scope_map_top_level.swift b/test/NameLookup/scope_map_top_level.swift index eeee236dae731..014c920862e53 100644 --- a/test/NameLookup/scope_map_top_level.swift +++ b/test/NameLookup/scope_map_top_level.swift @@ -31,8 +31,8 @@ var i: Int = b.my_identity() // CHECK-EXPANDED-NEXT: `-NominalTypeBodyScope {{.*}}, [4:11 - 4:13] // CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [6:1 - 21:28] // CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [6:1 - 21:28] -// CHECK-EXPANDED-NEXT: `-PatternEntryDeclScope {{.*}}, [6:5 - 21:28] entry 0 'a' -// CHECK-EXPANDED-NEXT: |-PatternEntryInitializerScope {{.*}}, [6:15 - 6:15] entry 0 'a' +// CHECK-EXPANDED-NEXT: |-PatternEntryDeclScope {{.*}}, [6:5 - 6:15] entry 0 'a' +// CHECK-EXPANDED-NEXT: `-PatternEntryInitializerScope {{.*}}, [6:15 - 6:15] entry 0 'a' // CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [8:1 - 21:28] // CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [8:1 - 21:28] // CHECK-EXPANDED-NEXT: `-GuardStmtScope {{.*}}, [8:1 - 21:28] @@ -46,9 +46,9 @@ var i: Int = b.my_identity() // CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [11:18 - 11:19] // CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [13:1 - 21:28] // CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [13:1 - 21:28] -// CHECK-EXPANDED-NEXT: `-PatternEntryDeclScope {{.*}}, [13:5 - 21:28] entry 0 'c' -// CHECK-EXPANDED-NEXT: |-PatternEntryInitializerScope {{.*}}, [13:9 - 13:9] entry 0 'c' -// CHECK-EXPANDED-NEXT: |-TypeAliasDeclScope {{.*}}, [15:1 - 15:15] +// CHECK-EXPANDED-NEXT: |-PatternEntryDeclScope {{.*}}, [13:5 - 13:9] entry 0 'c' +// CHECK-EXPANDED-NEXT: `-PatternEntryInitializerScope {{.*}}, [13:9 - 13:9] entry 0 'c' +// CHECK-EXPANDED-NEXT: |-TypeAliasDeclScope {{.*}}, [15:1 - 15:15] // CHECK-EXPANDED-NEXT: |-ExtensionDeclScope {{.*}}, [17:14 - 19:1] // CHECK-EXPANDED-NEXT: `-ExtensionBodyScope {{.*}}, [17:15 - 19:1] // CHECK-EXPANDED-NEXT: `-AbstractFunctionDeclScope {{.*}}, [18:3 - 18:43] 'my_identity()' From e2a9bf2009ff126ccfbf4108cf1248fe9ed2e9ff Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Thu, 1 Oct 2020 20:57:40 -0700 Subject: [PATCH 174/745] Fix use-after-free in SILCombine (#34145) SILCombine maintains a worklist of instructions and deleting of instructions is valid only via callbacks that remove them from the worklist as well. It calls swift::tryDeleteDeadClosure which in turn calls SILBuilder apis like emitStrongRelease/emitReleaseValue/emitDestroyValue which can delete instructions via SILInstruction::eraseFromParent leaving behind a stale entry in SILCombine's worklist causing a crash. This PR adds swift::emitDestroyOperation which correctly calls the appropriate InstModCallbacks on added/removed instructions. This comes from swift::releasePartialApplyCapturedArg which was handling creation of destroys with callbacks correctly. --- .../swift/SILOptimizer/Utils/InstOptUtils.h | 6 ++ lib/SILOptimizer/Utils/InstOptUtils.cpp | 64 ++++++++++--------- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/include/swift/SILOptimizer/Utils/InstOptUtils.h b/include/swift/SILOptimizer/Utils/InstOptUtils.h index 86c1ab802541f..209c154534e8f 100644 --- a/include/swift/SILOptimizer/Utils/InstOptUtils.h +++ b/include/swift/SILOptimizer/Utils/InstOptUtils.h @@ -357,6 +357,11 @@ void getConsumedPartialApplyArgs(PartialApplyInst *pai, SmallVectorImpl &argOperands, bool includeTrivialAddrArgs); +/// Emit destroy operation for \p operand, and call appropriate functions from +/// \p callbacks for newly created instructions and deleted instructions. +void emitDestroyOperation(SILBuilder &builder, SILLocation loc, + SILValue operand, InstModCallbacks callbacks); + /// Collect all (transitive) users of \p inst which just copy or destroy \p /// inst. /// @@ -366,6 +371,7 @@ void getConsumedPartialApplyArgs(PartialApplyInst *pai, /// destroys, i.e. if \p inst can be considered as "dead". bool collectDestroys(SingleValueInstruction *inst, SmallVectorImpl &destroys); + /// If Closure is a partial_apply or thin_to_thick_function with only local /// ref count users and a set of post-dominating releases: /// diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index 7cfd7f4147715..91e75def71ffb 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -1187,28 +1187,16 @@ static bool shouldDestroyPartialApplyCapturedArg(SILValue arg, return true; } -// *HEY YOU, YES YOU, PLEASE READ*. Even though a textual partial apply is -// printed with the convention of the closed over function upon it, all -// non-inout arguments to a partial_apply are passed at +1. This includes -// arguments that will eventually be passed as guaranteed or in_guaranteed to -// the closed over function. This is because the partial apply is building up a -// boxed aggregate to send off to the closed over function. Of course when you -// call the function, the proper conventions will be used. -void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc, - SILValue arg, - SILParameterInfo paramInfo, - InstModCallbacks callbacks) { - if (!shouldDestroyPartialApplyCapturedArg(arg, paramInfo, - builder.getFunction())) - return; - - // Otherwise, we need to destroy the argument. If we have an address, we - // insert a destroy_addr and return. Any live range issues must have been - // dealt with by our caller. - if (arg->getType().isAddress()) { - // Then emit the destroy_addr for this arg - SILInstruction *newInst = builder.emitDestroyAddrAndFold(loc, arg); - callbacks.createdNewInst(newInst); +void swift::emitDestroyOperation(SILBuilder &builder, SILLocation loc, + SILValue operand, InstModCallbacks callbacks) { + // If we have an address, we insert a destroy_addr and return. Any live range + // issues must have been dealt with by our caller. + if (operand->getType().isAddress()) { + // Then emit the destroy_addr for this operand. This function does not + // delete any instructions + SILInstruction *newInst = builder.emitDestroyAddrAndFold(loc, operand); + if (newInst != nullptr) + callbacks.createdNewInst(newInst); return; } @@ -1217,12 +1205,12 @@ void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc, // If we have qualified ownership, we should just emit a destroy value. if (builder.getFunction().hasOwnership()) { - callbacks.createdNewInst(builder.createDestroyValue(loc, arg)); + callbacks.createdNewInst(builder.createDestroyValue(loc, operand)); return; } - if (arg->getType().hasReferenceSemantics()) { - auto u = builder.emitStrongRelease(loc, arg); + if (operand->getType().hasReferenceSemantics()) { + auto u = builder.emitStrongRelease(loc, operand); if (u.isNull()) return; @@ -1235,7 +1223,7 @@ void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc, return; } - auto u = builder.emitReleaseValue(loc, arg); + auto u = builder.emitReleaseValue(loc, operand); if (u.isNull()) return; @@ -1247,6 +1235,24 @@ void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc, callbacks.createdNewInst(u.get()); } +// *HEY YOU, YES YOU, PLEASE READ*. Even though a textual partial apply is +// printed with the convention of the closed over function upon it, all +// non-inout arguments to a partial_apply are passed at +1. This includes +// arguments that will eventually be passed as guaranteed or in_guaranteed to +// the closed over function. This is because the partial apply is building up a +// boxed aggregate to send off to the closed over function. Of course when you +// call the function, the proper conventions will be used. +void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc, + SILValue arg, + SILParameterInfo paramInfo, + InstModCallbacks callbacks) { + if (!shouldDestroyPartialApplyCapturedArg(arg, paramInfo, + builder.getFunction())) + return; + + emitDestroyOperation(builder, loc, arg, callbacks); +} + void swift::deallocPartialApplyCapturedArg(SILBuilder &builder, SILLocation loc, SILValue arg, SILParameterInfo paramInfo) { @@ -1415,11 +1421,7 @@ bool swift::tryDeleteDeadClosure(SingleValueInstruction *closure, for (Operand *argOp : argsToHandle) { SILValue arg = argOp->get(); SILBuilderWithScope builder(pai, builderCtxt); - if (arg->getType().isObject()) { - builder.emitDestroyValueOperation(pai->getLoc(), arg); - } else { - builder.emitDestroyAddr(pai->getLoc(), arg); - } + emitDestroyOperation(builder, pai->getLoc(), arg, callbacks); } } } From ac061482c032d7590b624f6fd9bd57ce6e5de98c Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 2 Oct 2020 06:46:52 +0100 Subject: [PATCH 175/745] [Docs] Update the documentation contents and index (#34157) * Update docs/README.md * Update docs/contents.rst --- docs/README.md | 2 +- docs/contents.rst | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index 56f4e92c00e86..ceeb6bb95f34b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -113,7 +113,7 @@ documentation, please create a thread on the Swift forums under the Describes how the "Omit Needless Words" algorithm works, making imported names more idiomatic. - Type-checking and inference: - - [TypeChecker.rst](/docs/TypeChecker.rst): + - [TypeChecker.md](/docs/TypeChecker.md): Provides an overview of how type-checking and inference work. - [RequestEvaluator.md](/docs/RequestEvaluator.md): Describes the request evaluator architecture, which is used for diff --git a/docs/contents.rst b/docs/contents.rst index 2487933e92ebb..e6d1180ec105b 100644 --- a/docs/contents.rst +++ b/docs/contents.rst @@ -12,7 +12,6 @@ Contents Generics StoredAndComputedVariables SIL - TypeChecker OptimizationTips ABI: TypeMetadata ABI: TypeLayout From 903786f5ab88056e2abc5b5db99b0107d6d06461 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 2 Oct 2020 17:18:19 +0200 Subject: [PATCH 176/745] tests: correct availability checks in Interpreter/runtime_name_local_class_opaque_type.swift The test checks a fix, which also affects the runtime. Therefore, the test requires a minimum OS version to run without a crash. --- ...runtime_name_local_class_opaque_type.swift | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/test/Interpreter/runtime_name_local_class_opaque_type.swift b/test/Interpreter/runtime_name_local_class_opaque_type.swift index 1b325fc10664b..e7a18e316780c 100644 --- a/test/Interpreter/runtime_name_local_class_opaque_type.swift +++ b/test/Interpreter/runtime_name_local_class_opaque_type.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -Xfrontend -disable-availability-checking %s -o %t/a.out +// RUN: %target-build-swift %s -o %t/a.out // RUN: %target-run %t/a.out | %FileCheck %s // REQUIRES: executable_test @@ -7,18 +7,27 @@ protocol MyProtocol {} +@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) func returnsClass1() -> some MyProtocol { class MyClass1: MyProtocol {} return MyClass1() } -var returnsClass2: some MyProtocol { - class MyClass2: MyProtocol {} - return MyClass2() +@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) +struct Outer { + static var returnsClass2: some MyProtocol { + class MyClass2: MyProtocol {} + return MyClass2() + } } -print(returnsClass1()) // CHECK: a.(unknown context at ${{[0-9a-z]+}}).(unknown context at ${{[0-9a-z]+}}).MyClass1 - -print(returnsClass2) -// CHECK: a.(unknown context at ${{[0-9a-z]+}}).(unknown context at ${{[0-9a-z]+}}).MyClass2 +// CHECK: a.Outer.(unknown context at ${{[0-9a-z]+}}).(unknown context at ${{[0-9a-z]+}}).MyClass2 +if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) { + print(returnsClass1()) + print(Outer.returnsClass2) +} else { + // Make FileCheck happy if this test runs on an older OS. + print("a.(unknown context at $0).(unknown context at $0).MyClass1") + print("a.Outer.(unknown context at $0).(unknown context at $0).MyClass2") +} From b5247d2c8f9c4133ba165370d92370600cf67ece Mon Sep 17 00:00:00 2001 From: Cassie Jones Date: Fri, 2 Oct 2020 12:43:42 -0400 Subject: [PATCH 177/745] Fix formatting issue --- utils/swift_build_support/tests/test_cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/swift_build_support/tests/test_cmake.py b/utils/swift_build_support/tests/test_cmake.py index 3640751bb598d..8b84dc06c230c 100644 --- a/utils/swift_build_support/tests/test_cmake.py +++ b/utils/swift_build_support/tests/test_cmake.py @@ -132,7 +132,7 @@ def test_common_options_tsan(self): self.assertEqual( list(cmake.common_options()), ["-G", "Ninja", - "-DLLVM_USE_SANITIZER=Thread", + "-DLLVM_USE_SANITIZER=Thread", "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", From 7271fd079e5dd103bca30d55bf408aed6e06642c Mon Sep 17 00:00:00 2001 From: Eric Miotto <1094986+edymtt@users.noreply.github.com> Date: Fri, 2 Oct 2020 10:06:44 -0700 Subject: [PATCH 178/745] [build] Run dsymutil on multiple files at a time (#34149) The change in #33654 had the effect of not leveraging dsymutil parallelism -- pass instead multiple file at once. Addresses rdar://69550787 --- utils/build-script-impl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index 0662f5961b754..2bb99c886a669 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -3049,7 +3049,7 @@ for host in "${ALL_HOSTS[@]}"; do grep -v '.py$' | \ grep -v '.a$' | \ grep -v 'swift-api-digester' | \ - xargs -n 1 -P 1 ${dsymutil_path}) + xargs -P 1 ${dsymutil_path}) # Strip executables, shared libraries and static libraries in # `host_install_destdir`. From d74cad69600eb498f3f875a8418769b98fa0365e Mon Sep 17 00:00:00 2001 From: Cassie Jones Date: Fri, 2 Oct 2020 13:37:35 -0400 Subject: [PATCH 179/745] [Localization] Install only *.db and *.yaml files (#34139) This prevents us from picking up the .gitkeep file and any other stray files that might end up in there. --- localization/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/localization/CMakeLists.txt b/localization/CMakeLists.txt index 53c7e05e2a757..dcaac27691e35 100644 --- a/localization/CMakeLists.txt +++ b/localization/CMakeLists.txt @@ -19,4 +19,7 @@ add_dependencies(diagnostic-database swift-def-to-yaml-converter) swift_install_in_component( DIRECTORY ${CMAKE_BINARY_DIR}/share/swift/diagnostics/ DESTINATION "share/swift/diagnostics" - COMPONENT compiler) + COMPONENT compiler + PATTERN "*.db" + PATTERN "*.yaml" +) From ae9715ac0311e1d65dc4fed957e067ee2654d8ea Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Thu, 1 Oct 2020 19:39:25 +0300 Subject: [PATCH 180/745] WinSDK: extract System submodule with winioctl.h Currently this header gets included into `WinSDK.WinSock2` via windows.h & winscard.h --- stdlib/public/Platform/winsdk.modulemap | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/stdlib/public/Platform/winsdk.modulemap b/stdlib/public/Platform/winsdk.modulemap index 8d7d464d27ffd..d11dd16400920 100644 --- a/stdlib/public/Platform/winsdk.modulemap +++ b/stdlib/public/Platform/winsdk.modulemap @@ -164,13 +164,6 @@ module WinSDK [system] { link "NetAPI32.Lib" } - module DbgHelp { - header "DbgHelp.h" - export * - - link "DbgHelp.Lib" - } - module DWM { header "dwmapi.h" export * @@ -244,6 +237,20 @@ module WinSDK [system] { link "shcore.lib" } + module System { + module DbgHelp { + header "DbgHelp.h" + export * + + link "DbgHelp.Lib" + } + + module IOCTL { + header "winioctl.h" + export * + } + } + module OLE32 { header "oaidl.h" export * From 554e0f97e60f3873dcdae3e5bfd03ce45345e8c1 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 2 Oct 2020 14:09:07 -0400 Subject: [PATCH 181/745] AST: Find local property wrappers in ASTScope::lookupLocalDecls() Two fixes here: - The ASTScopeDeclConsumerForLocalLookup needs to visit auxiliary variables - preCheckExpression() should call ASTScope::lookupLocalDecls() even when parser lookup is enabled, since otherwise we won't be able to find auxiliary decls in the current DeclContext. --- lib/AST/UnqualifiedLookup.cpp | 13 ++++++++++--- lib/Sema/PreCheckExpr.cpp | 21 ++++++++++----------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index f939ed3b71ac8..d1fc74b511786 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -784,10 +784,17 @@ class ASTScopeDeclConsumerForLocalLookup bool consume(ArrayRef values, NullablePtr baseDC) override { for (auto *value: values) { - if (!value->getName().matchesRef(name)) - continue; + if (auto *varDecl = dyn_cast(value)) { + // Check if the name matches any auxiliary decls not in the AST + varDecl->visitAuxiliaryDecls([&](VarDecl *auxiliaryVar) { + if (name.isSimpleName(auxiliaryVar->getName())) { + results.push_back(auxiliaryVar); + } + }); + } - results.push_back(value); + if (value->getName().matchesRef(name)) + results.push_back(value); } return (!stopAfterInnermostBraceStmt && !results.empty()); diff --git a/lib/Sema/PreCheckExpr.cpp b/lib/Sema/PreCheckExpr.cpp index 0da6f8379f65f..32f7eb2838109 100644 --- a/lib/Sema/PreCheckExpr.cpp +++ b/lib/Sema/PreCheckExpr.cpp @@ -357,17 +357,16 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, SmallVector ResultValues; auto &Context = DC->getASTContext(); - if (Context.LangOpts.DisableParserLookup) { - // First, look for a local binding in scope. - if (Loc.isValid() && !Name.isOperator()) { - SmallVector localDecls; - ASTScope::lookupLocalDecls(DC->getParentSourceFile(), - Name.getFullName(), Loc, - /*stopAfterInnermostBraceStmt=*/false, - ResultValues); - for (auto *localDecl : ResultValues) { - Lookup.add(LookupResultEntry(localDecl), /*isOuter=*/false); - } + + // First, look for a local binding in scope. + if (Loc.isValid() && !Name.isOperator()) { + SmallVector localDecls; + ASTScope::lookupLocalDecls(DC->getParentSourceFile(), + Name.getFullName(), Loc, + /*stopAfterInnermostBraceStmt=*/false, + ResultValues); + for (auto *localDecl : ResultValues) { + Lookup.add(LookupResultEntry(localDecl), /*isOuter=*/false); } } From 59bb1fc23592bcc4eb18bca32dcbff9de4b7e751 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 30 Sep 2020 18:55:43 -0700 Subject: [PATCH 182/745] [Concurrency] Pass "async" into SILFunctionTypes. This reverts commit 3a48396110d2639d7b90970bc0369a559adbc0c1. --- lib/SIL/IR/SILFunctionType.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 744eab450ac83..fc175f4460017 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -4198,6 +4198,8 @@ TypeConverter::getLoweredFormalTypes(SILDeclRef constant, // Build the uncurried function type. if (innerExtInfo.isThrowing()) extInfo = extInfo.withThrows(true); + if (innerExtInfo.isAsync()) + extInfo = extInfo.withAsync(true); bridgedParams.push_back(selfParam); From 11b2251ac18de4fa7e78a132afb9ff0e7457af63 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Thu, 1 Oct 2020 15:55:40 -0700 Subject: [PATCH 183/745] Driver: forwarding driver invocations to the new driver when SWIFT_USE_NEW_DRIVER is defined This allows us to experiment with the new swift-driver without changing build systems. --- include/swift/AST/DiagnosticsDriver.def | 2 ++ tools/driver/driver.cpp | 30 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/include/swift/AST/DiagnosticsDriver.def b/include/swift/AST/DiagnosticsDriver.def index 17b5b0d4c36ea..32e862b3751da 100644 --- a/include/swift/AST/DiagnosticsDriver.def +++ b/include/swift/AST/DiagnosticsDriver.def @@ -190,5 +190,7 @@ WARNING(warn_drv_darwin_sdk_invalid_settings, none, "SDK settings were ignored because 'SDKSettings.json' could not be parsed", ()) +REMARK(remark_forwarding_to_new_driver, none, "new Swift driver will be used", ()) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index 318fc77746dd7..0fb6062e12ec0 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/DiagnosticsDriver.h" #include "swift/Basic/LLVMInitialize.h" #include "swift/Basic/PrettyStackTrace.h" #include "swift/Basic/Program.h" @@ -159,6 +160,35 @@ static int run_driver(StringRef ExecName, DiagnosticEngine Diags(SM); Diags.addConsumer(PDC); + // Forwarding calls to the swift driver if the C++ driver is invoked as `swift` + // or `swiftc`, and an environment variable SWIFT_USE_NEW_DRIVER is defined. + if (llvm::sys::Process::GetEnv("SWIFT_USE_NEW_DRIVER") && + (ExecName == "swift" || ExecName == "swiftc")) { + SmallString<256> NewDriverPath(llvm::sys::path::parent_path(Path)); + llvm::sys::path::append(NewDriverPath, "swift-driver"); + SmallVector subCommandArgs; + // Rewrite the program argument. + subCommandArgs.push_back(NewDriverPath.c_str()); + if (ExecName == "swiftc") { + subCommandArgs.push_back("--driver-mode=swiftc"); + } else { + assert(ExecName == "swift"); + subCommandArgs.push_back("--driver-mode=swift"); + } + subCommandArgs.insert(subCommandArgs.end(), argv.begin() + 1, argv.end()); + + // Execute the subcommand. + subCommandArgs.push_back(nullptr); + Diags.diagnose(SourceLoc(), diag::remark_forwarding_to_new_driver); + ExecuteInPlace(NewDriverPath.c_str(), subCommandArgs.data()); + + // If we reach here then an error occurred (typically a missing path). + std::string ErrorString = llvm::sys::StrError(); + llvm::errs() << "error: unable to invoke subcommand: " << subCommandArgs[0] + << " (" << ErrorString << ")\n"; + return 2; + } + Driver TheDriver(Path, ExecName, argv, Diags); switch (TheDriver.getDriverKind()) { case Driver::DriverKind::AutolinkExtract: From 04c21c18c935bc9a4101c2e5d49609c5d360e4b8 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 22 Sep 2020 15:31:18 -0400 Subject: [PATCH 184/745] Parse: Disable circular definition check when parser lookup is off --- lib/Parse/ParseExpr.cpp | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 1c8019aaf9464..e05b08cd4ece8 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -2208,20 +2208,23 @@ Expr *Parser::parseExprIdentifier() { // lookups, so disable this check when parsing for SwiftSyntax. if (!InPoundIfEnvironment && !Context.LangOpts.ParseForSyntaxTreeOnly) { D = lookupInScope(name); - // FIXME: We want this to work: "var x = { x() }", but for now it's better - // to disallow it than to crash. - if (D) { - for (auto activeVar : DisabledVars) { - if (activeVar == D) { - diagnose(loc.getBaseNameLoc(), DisabledVarReason); - return new (Context) ErrorExpr(loc.getSourceRange()); + + if (!Context.LangOpts.DisableParserLookup) { + // FIXME: We want this to work: "var x = { x() }", but for now it's better + // to disallow it than to crash. + if (D) { + for (auto activeVar : DisabledVars) { + if (activeVar == D) { + diagnose(loc.getBaseNameLoc(), DisabledVarReason); + return new (Context) ErrorExpr(loc.getSourceRange()); + } } - } - } else { - for (auto activeVar : DisabledVars) { - if (activeVar->getName() == name.getFullName()) { - diagnose(loc.getBaseNameLoc(), DisabledVarReason); - return new (Context) ErrorExpr(loc.getSourceRange()); + } else { + for (auto activeVar : DisabledVars) { + if (activeVar->getName() == name.getFullName()) { + diagnose(loc.getBaseNameLoc(), DisabledVarReason); + return new (Context) ErrorExpr(loc.getSourceRange()); + } } } } From 4648587326532a5e1ee672014cce4b1381607d3a Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 1 Oct 2020 19:20:32 -0400 Subject: [PATCH 185/745] ASTScope: Move sortBySourceRange() and cull() calls out of addSiblingsToScopeTree() --- lib/AST/ASTScopeCreation.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index aa82c8014afc2..8d10d51974931 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -218,7 +218,7 @@ class ScopeCreator final { ASTScopeImpl *const organicInsertionPoint, ArrayRef nodesOrDeclsToAdd) { auto *ip = insertionPoint; - for (auto nd : sortBySourceRange(cull(nodesOrDeclsToAdd))) { + for (auto nd : nodesOrDeclsToAdd) { auto *const newIP = addToScopeTreeAndReturnInsertionPoint(nd, ip).getPtrOr(ip); ip = newIP; @@ -419,9 +419,6 @@ class ScopeCreator final { void addChildrenForKnownAttributes(ValueDecl *decl, ASTScopeImpl *parent); -public: - -private: /// Remove VarDecls because we'll find them when we expand the /// PatternBindingDecls. Remove EnunCases /// because they overlap EnumElements and AST includes the elements in the @@ -463,7 +460,6 @@ class ScopeCreator final { return -1 == signum; } -public: SWIFT_DEBUG_DUMP { print(llvm::errs()); } void print(raw_ostream &out) const { @@ -950,7 +946,9 @@ ASTSourceFileScope::expandAScopeThatCreatesANewInsertionPoint( // Assume that decls are only added at the end, in source order std::vector newNodes(decls.begin(), decls.end()); insertionPoint = - scopeCreator.addSiblingsToScopeTree(insertionPoint, this, newNodes); + scopeCreator.addSiblingsToScopeTree(insertionPoint, this, + scopeCreator.sortBySourceRange( + scopeCreator.cull(newNodes))); // Too slow to perform all the time: // ASTScopeAssert(scopeCreator->containsAllDeclContextsFromAST(), // "ASTScope tree missed some DeclContexts or made some up"); @@ -1068,7 +1066,10 @@ BraceStmtScope::expandAScopeThatCreatesANewInsertionPoint( // TODO: remove the sort after fixing parser to create brace statement // elements in source order auto *insertionPoint = - scopeCreator.addSiblingsToScopeTree(this, this, stmt->getElements()); + scopeCreator.addSiblingsToScopeTree(this, this, + scopeCreator.sortBySourceRange( + scopeCreator.cull( + stmt->getElements()))); if (auto *s = scopeCreator.getASTContext().Stats) ++s->getFrontendCounters().NumBraceStmtASTScopeExpansions; return { @@ -1414,6 +1415,7 @@ void GenericTypeOrExtensionScope::expandBody(ScopeCreator &) {} void IterableTypeScope::expandBody(ScopeCreator &scopeCreator) { auto nodes = asNodeVector(getIterableDeclContext().get()->getMembers()); + nodes = scopeCreator.sortBySourceRange(scopeCreator.cull(nodes)); scopeCreator.addSiblingsToScopeTree(this, this, nodes); if (auto *s = scopeCreator.getASTContext().Stats) ++s->getFrontendCounters().NumIterableTypeBodyASTScopeExpansions; From 8b2cf55b705e5e6cf5d316ed8af75990017f8571 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 1 Oct 2020 20:24:56 -0400 Subject: [PATCH 186/745] ASTScope: Push isLocalBinding down from AbstractPatternEntryScope to PatternEntryDeclScope --- include/swift/AST/ASTScope.h | 16 ++++++++-------- lib/AST/ASTScopeCreation.cpp | 8 +++----- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index f4247df1468c3..aa373dcd6e750 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -1023,10 +1023,8 @@ class AbstractPatternEntryScope : public ASTScopeImpl { public: PatternBindingDecl *const decl; const unsigned patternEntryIndex; - const bool isLocalBinding; - AbstractPatternEntryScope(PatternBindingDecl *, unsigned entryIndex, - bool); + AbstractPatternEntryScope(PatternBindingDecl *, unsigned entryIndex); virtual ~AbstractPatternEntryScope() {} const PatternBindingEntry &getPatternEntry() const; @@ -1041,10 +1039,13 @@ class AbstractPatternEntryScope : public ASTScopeImpl { }; class PatternEntryDeclScope final : public AbstractPatternEntryScope { + const bool isLocalBinding; + public: PatternEntryDeclScope(PatternBindingDecl *pbDecl, unsigned entryIndex, - bool isLocalBinding) - : AbstractPatternEntryScope(pbDecl, entryIndex, isLocalBinding) {} + bool isLocalBinding, Optional endLoc) + : AbstractPatternEntryScope(pbDecl, entryIndex), + isLocalBinding(isLocalBinding), endLoc(endLoc) {} virtual ~PatternEntryDeclScope() {} protected: @@ -1070,9 +1071,8 @@ class PatternEntryInitializerScope final : public AbstractPatternEntryScope { Expr *initAsWrittenWhenCreated; public: - PatternEntryInitializerScope(PatternBindingDecl *pbDecl, unsigned entryIndex, - bool isLocalBinding) - : AbstractPatternEntryScope(pbDecl, entryIndex, isLocalBinding), + PatternEntryInitializerScope(PatternBindingDecl *pbDecl, unsigned entryIndex) + : AbstractPatternEntryScope(pbDecl, entryIndex), initAsWrittenWhenCreated(pbDecl->getOriginalInit(entryIndex)) {} virtual ~PatternEntryInitializerScope() {} diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 8d10d51974931..a726c05ac5719 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -989,7 +989,7 @@ PatternEntryDeclScope::expandAScopeThatCreatesANewInsertionPoint( "Original inits are always after the '='"); scopeCreator .constructExpandAndInsertUncheckable( - this, decl, patternEntryIndex, isLocalBinding); + this, decl, patternEntryIndex); } // Add accessors for the variables in this pattern. @@ -1378,10 +1378,8 @@ ASTScopeImpl *LabeledConditionalStmtScope::createNestedConditionalClauseScopes( } AbstractPatternEntryScope::AbstractPatternEntryScope( - PatternBindingDecl *declBeingScoped, unsigned entryIndex, - bool isLocalBinding) - : decl(declBeingScoped), patternEntryIndex(entryIndex), - isLocalBinding(isLocalBinding) { + PatternBindingDecl *declBeingScoped, unsigned entryIndex) + : decl(declBeingScoped), patternEntryIndex(entryIndex) { ASTScopeAssert(entryIndex < declBeingScoped->getPatternList().size(), "out of bounds"); } From 4f1ab9d8e5d225d462dd5f4963777cc4df3442d2 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 1 Oct 2020 22:39:49 -0400 Subject: [PATCH 187/745] ASTScope: Directly calculate end location of GuardStmtScope and PatternEntryDeclScope Today, the reported source range of a GuardStmtScope is just that of the statement itself, ending after the 'else' block. Similarly, a PatternEntryDeclScope ends immediately after the initializer expression. Since these scopes introduce names until the end of the parent BraceStmt (meaning they change the insertion point), we were relying on the addition of child scopes to extend the source range. While we still need the hack for extending source ranges to deal with string interpolation, at least in the other cases we can get rid of it. Note that this fixes a bug where a GuardStmtScope would end before an inactive '#if' block if the inactive '#if' block was the last element of a BraceStmt. --- include/swift/AST/ASTScope.h | 12 +++- lib/AST/ASTScopeCreation.cpp | 122 ++++++++++++++++++++++---------- lib/AST/ASTScopeSourceRange.cpp | 15 +++- test/decl/var/usage.swift | 9 +++ 4 files changed, 114 insertions(+), 44 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index aa373dcd6e750..c6f6c2a46cfdc 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -1040,6 +1040,7 @@ class AbstractPatternEntryScope : public ASTScopeImpl { class PatternEntryDeclScope final : public AbstractPatternEntryScope { const bool isLocalBinding; + Optional endLoc; public: PatternEntryDeclScope(PatternBindingDecl *pbDecl, unsigned entryIndex, @@ -1400,7 +1401,8 @@ class WhileStmtScope final : public LabeledConditionalStmtScope { class GuardStmtScope final : public LabeledConditionalStmtScope { public: GuardStmt *const stmt; - GuardStmtScope(GuardStmt *e) : stmt(e) {} + SourceLoc endLoc; + GuardStmtScope(GuardStmt *e, SourceLoc endLoc) : stmt(e), endLoc(endLoc) {} virtual ~GuardStmtScope() {} protected: @@ -1413,6 +1415,8 @@ class GuardStmtScope final : public LabeledConditionalStmtScope { public: std::string getClassName() const override; LabeledConditionalStmt *getLabeledConditionalStmt() const override; + SourceRange + getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; }; /// A scope after a guard statement that follows lookups into the conditions @@ -1427,9 +1431,11 @@ class LookupParentDiversionScope final : public ASTScopeImpl { public: ASTScopeImpl *const lookupParent; const SourceLoc startLoc; + const SourceLoc endLoc; - LookupParentDiversionScope(ASTScopeImpl *lookupParent, SourceLoc startLoc) - : lookupParent(lookupParent), startLoc(startLoc) {} + LookupParentDiversionScope(ASTScopeImpl *lookupParent, + SourceLoc startLoc, SourceLoc endLoc) + : lookupParent(lookupParent), startLoc(startLoc), endLoc(endLoc) {} SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index a726c05ac5719..b7324c68013c7 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -213,14 +213,17 @@ class ScopeCreator final { /// Given an array of ASTNodes or Decl pointers, add them /// Return the resultant insertionPoint + /// + /// \param endLoc The end location for any "scopes until the end" that + /// we introduce here, such as PatternEntryDeclScope and GuardStmtScope ASTScopeImpl * addSiblingsToScopeTree(ASTScopeImpl *const insertionPoint, - ASTScopeImpl *const organicInsertionPoint, - ArrayRef nodesOrDeclsToAdd) { + ArrayRef nodesOrDeclsToAdd, + Optional endLoc) { auto *ip = insertionPoint; for (auto nd : nodesOrDeclsToAdd) { auto *const newIP = - addToScopeTreeAndReturnInsertionPoint(nd, ip).getPtrOr(ip); + addToScopeTreeAndReturnInsertionPoint(nd, ip, endLoc).getPtrOr(ip); ip = newIP; } return ip; @@ -229,12 +232,16 @@ class ScopeCreator final { public: /// For each of searching, call this unless the insertion point is needed void addToScopeTree(ASTNode n, ASTScopeImpl *parent) { - (void)addToScopeTreeAndReturnInsertionPoint(n, parent); + (void)addToScopeTreeAndReturnInsertionPoint(n, parent, None); } /// Return new insertion point if the scope was not a duplicate /// For ease of searching, don't call unless insertion point is needed + /// + /// \param endLoc The end location for any "scopes until the end" that + /// we introduce here, such as PatternEntryDeclScope and GuardStmtScope NullablePtr - addToScopeTreeAndReturnInsertionPoint(ASTNode, ASTScopeImpl *parent); + addToScopeTreeAndReturnInsertionPoint(ASTNode, ASTScopeImpl *parent, + Optional endLoc); bool isWorthTryingToCreateScopeFor(ASTNode n) const { if (!n) @@ -419,6 +426,18 @@ class ScopeCreator final { void addChildrenForKnownAttributes(ValueDecl *decl, ASTScopeImpl *parent); + /// Add PatternEntryDeclScopes for each pattern binding entry. + /// + /// Returns the new insertion point. + /// + /// \param endLoc Must be valid iff the pattern binding is in a local + /// scope, in which case this is the last source location where the + /// pattern bindings are going to be visible. + NullablePtr + addPatternBindingToScopeTree(PatternBindingDecl *patternBinding, + ASTScopeImpl *parent, + Optional endLoc); + /// Remove VarDecls because we'll find them when we expand the /// PatternBindingDecls. Remove EnunCases /// because they overlap EnumElements and AST includes the elements in the @@ -541,7 +560,10 @@ class NodeAdder : public ASTVisitor, NullablePtr, NullablePtr, void, void, void, ASTScopeImpl *, ScopeCreator &> { + Optional endLoc; + public: + explicit NodeAdder(Optional endLoc) : endLoc(endLoc) {} #pragma mark ASTNodes that do not create scopes @@ -633,7 +655,9 @@ class NodeAdder // the deferred nodes. NullablePtr visitGuardStmt(GuardStmt *e, ASTScopeImpl *p, ScopeCreator &scopeCreator) { - return scopeCreator.ifUniqueConstructExpandAndInsert(p, e); + ASTScopeAssert(endLoc.hasValue(), "GuardStmt outside of a BraceStmt?"); + return scopeCreator.ifUniqueConstructExpandAndInsert( + p, e, *endLoc); } NullablePtr visitTopLevelCodeDecl(TopLevelCodeDecl *d, ASTScopeImpl *p, @@ -694,28 +718,8 @@ class NodeAdder visitPatternBindingDecl(PatternBindingDecl *patternBinding, ASTScopeImpl *parentScope, ScopeCreator &scopeCreator) { - if (auto *var = patternBinding->getSingleVar()) - scopeCreator.addChildrenForKnownAttributes(var, parentScope); - - auto *insertionPoint = parentScope; - for (auto i : range(patternBinding->getNumPatternEntries())) { - bool isLocalBinding = false; - if (auto *varDecl = patternBinding->getAnchoringVarDecl(i)) { - isLocalBinding = varDecl->getDeclContext()->isLocalContext(); - } - - insertionPoint = - scopeCreator - .ifUniqueConstructExpandAndInsert( - insertionPoint, patternBinding, i, isLocalBinding) - .getPtrOr(insertionPoint); - - ASTScopeAssert(isLocalBinding || insertionPoint == parentScope, - "Bindings at the top-level or members of types should " - "not change the insertion point"); - } - - return insertionPoint; + return scopeCreator.addPatternBindingToScopeTree( + patternBinding, parentScope, endLoc); } NullablePtr visitEnumElementDecl(EnumElementDecl *eed, @@ -769,15 +773,18 @@ class NodeAdder // NodeAdder NullablePtr ScopeCreator::addToScopeTreeAndReturnInsertionPoint(ASTNode n, - ASTScopeImpl *parent) { + ASTScopeImpl *parent, + Optional endLoc) { if (!isWorthTryingToCreateScopeFor(n)) return parent; + + NodeAdder adder(endLoc); if (auto *p = n.dyn_cast()) - return NodeAdder().visit(p, parent, *this); + return adder.visit(p, parent, *this); if (auto *p = n.dyn_cast()) - return NodeAdder().visit(p, parent, *this); + return adder.visit(p, parent, *this); auto *p = n.get(); - return NodeAdder().visit(p, parent, *this); + return adder.visit(p, parent, *this); } void ScopeCreator::addChildrenForAllLocalizableAccessorsInSourceOrder( @@ -827,6 +834,41 @@ void ScopeCreator::addChildrenForKnownAttributes(ValueDecl *decl, } } +NullablePtr +ScopeCreator::addPatternBindingToScopeTree(PatternBindingDecl *patternBinding, + ASTScopeImpl *parentScope, + Optional endLoc) { + if (auto *var = patternBinding->getSingleVar()) + addChildrenForKnownAttributes(var, parentScope); + + auto *insertionPoint = parentScope; + for (auto i : range(patternBinding->getNumPatternEntries())) { + bool isLocalBinding = false; + if (auto *varDecl = patternBinding->getAnchoringVarDecl(i)) { + isLocalBinding = varDecl->getDeclContext()->isLocalContext(); + } + + Optional endLocForBinding = None; + if (isLocalBinding) { + endLocForBinding = endLoc; + ASTScopeAssert(endLoc.hasValue() && endLoc->isValid(), + "PatternBindingDecl in local context outside of BraceStmt?"); + } + + insertionPoint = + ifUniqueConstructExpandAndInsert( + insertionPoint, patternBinding, i, + isLocalBinding, endLocForBinding) + .getPtrOr(insertionPoint); + + ASTScopeAssert(isLocalBinding || insertionPoint == parentScope, + "Bindings at the top-level or members of types should " + "not change the insertion point"); + } + + return insertionPoint; +} + #pragma mark creation helpers void ASTScopeImpl::addChild(ASTScopeImpl *child, ASTContext &ctx) { @@ -946,9 +988,10 @@ ASTSourceFileScope::expandAScopeThatCreatesANewInsertionPoint( // Assume that decls are only added at the end, in source order std::vector newNodes(decls.begin(), decls.end()); insertionPoint = - scopeCreator.addSiblingsToScopeTree(insertionPoint, this, + scopeCreator.addSiblingsToScopeTree(insertionPoint, scopeCreator.sortBySourceRange( - scopeCreator.cull(newNodes))); + scopeCreator.cull(newNodes)), + None); // Too slow to perform all the time: // ASTScopeAssert(scopeCreator->containsAllDeclContextsFromAST(), // "ASTScope tree missed some DeclContexts or made some up"); @@ -1048,7 +1091,7 @@ GuardStmtScope::expandAScopeThatCreatesANewInsertionPoint(ScopeCreator & auto *const lookupParentDiversionScope = scopeCreator .constructExpandAndInsertUncheckable( - this, conditionLookupParent, stmt->getEndLoc()); + this, conditionLookupParent, stmt->getEndLoc(), endLoc); return {lookupParentDiversionScope, "Succeeding code must be in scope of guard variables"}; } @@ -1066,10 +1109,11 @@ BraceStmtScope::expandAScopeThatCreatesANewInsertionPoint( // TODO: remove the sort after fixing parser to create brace statement // elements in source order auto *insertionPoint = - scopeCreator.addSiblingsToScopeTree(this, this, + scopeCreator.addSiblingsToScopeTree(this, scopeCreator.sortBySourceRange( scopeCreator.cull( - stmt->getElements()))); + stmt->getElements())), + stmt->getEndLoc()); if (auto *s = scopeCreator.getASTContext().Stats) ++s->getFrontendCounters().NumBraceStmtASTScopeExpansions; return { @@ -1083,7 +1127,7 @@ TopLevelCodeScope::expandAScopeThatCreatesANewInsertionPoint(ScopeCreator & if (auto *body = scopeCreator - .addToScopeTreeAndReturnInsertionPoint(decl->getBody(), this) + .addToScopeTreeAndReturnInsertionPoint(decl->getBody(), this, None) .getPtrOrNull()) return {body, "So next top level code scope and put its decls in its body " "under a guard statement scope (etc) from the last top level " @@ -1414,7 +1458,7 @@ void GenericTypeOrExtensionScope::expandBody(ScopeCreator &) {} void IterableTypeScope::expandBody(ScopeCreator &scopeCreator) { auto nodes = asNodeVector(getIterableDeclContext().get()->getMembers()); nodes = scopeCreator.sortBySourceRange(scopeCreator.cull(nodes)); - scopeCreator.addSiblingsToScopeTree(this, this, nodes); + scopeCreator.addSiblingsToScopeTree(this, nodes, None); if (auto *s = scopeCreator.getASTContext().Stats) ++s->getFrontendCounters().NumIterableTypeBodyASTScopeExpansions; } diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index 48a59b2f3ac59..1d93bdd762b84 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -248,7 +248,13 @@ SourceRange DefaultArgumentInitializerScope::getSourceRangeOfThisASTNode( SourceRange PatternEntryDeclScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - return getPatternEntry().getSourceRange(); + SourceRange range = getPatternEntry().getSourceRange(); + if (endLoc.hasValue()) { + ASTScopeAssert(endLoc->isValid(), + "BraceStmt ends before pattern binding entry?"); + range.End = *endLoc; + } + return range; } SourceRange PatternEntryInitializerScope::getSourceRangeOfThisASTNode( @@ -445,9 +451,14 @@ SourceRange AttachedPropertyWrapperScope::getSourceRangeOfThisASTNode( return sourceRangeWhenCreated; } +SourceRange GuardStmtScope::getSourceRangeOfThisASTNode( + const bool omitAssertions) const { + return SourceRange(getStmt()->getStartLoc(), endLoc); +} + SourceRange LookupParentDiversionScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - return SourceRange(startLoc); + return SourceRange(startLoc, endLoc); } #pragma mark source range caching diff --git a/test/decl/var/usage.swift b/test/decl/var/usage.swift index 76d145b314e56..9f89092728c4f 100644 --- a/test/decl/var/usage.swift +++ b/test/decl/var/usage.swift @@ -242,6 +242,15 @@ func testBuildConfigs() { #endif } +// same as above, but with a guard statement +func testGuardWithPoundIf(x: Int?) { + guard let x = x else { return } + +#if false + _ = x +#endif +} + // Bogus 'never mutated' warning when protocol variable is mutated only by mutating method protocol Fooable { mutating func mutFoo() From b7b6cfc782098e67db1a3f7d97c496e92aab67da Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 2 Oct 2020 21:39:07 -0700 Subject: [PATCH 188/745] test: repair the Windows CI host after #34142 --- test/SIL/Parser/async.sil | 1 + 1 file changed, 1 insertion(+) diff --git a/test/SIL/Parser/async.sil b/test/SIL/Parser/async.sil index 992fb8d34a95e..6516b47029eea 100644 --- a/test/SIL/Parser/async.sil +++ b/test/SIL/Parser/async.sil @@ -1,4 +1,5 @@ // RUN: %target-sil-opt -enable-sil-verify-all=true %s | %target-sil-opt -enable-sil-verify-all=true | %FileCheck %s +// REQUIRES: concurrency import Builtin import Swift From e36011def493dceec79381f2e4a051f1f1e610fe Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 30 Sep 2020 21:30:22 -0700 Subject: [PATCH 189/745] [Type checker] Delay the diagnosis of missing witnesses further. The handling of "global" missing witnesses would diagnose a missing witness (showing "almost" matches) and then, later, provide the Fix-It to add the declaration if the user wants it. Delay the diagnostic part until later, when we add the missing witness. While here, correct an existing issue with memory corruption that occurs because we would diagnose some of these witnesses, then clear a "global" witness list, while still iterating through parts of that list. --- lib/Sema/TypeCheckProtocol.cpp | 42 ++++++++++++++++++++-------------- lib/Sema/TypeCheckProtocol.h | 5 +++- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index dc0c1c182f14a..d69a00dadc30d 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -1607,7 +1607,8 @@ void MultiConformanceChecker::checkAllConformances() { for (auto It = AllUsedCheckers.rbegin(); It != AllUsedCheckers.rend(); ++It) { if (!It->getLocalMissingWitness().empty()) { - It->diagnoseMissingWitnesses(MissingWitnessDiagnosisKind::FixItOnly); + if (It->diagnoseMissingWitnesses(MissingWitnessDiagnosisKind::FixItOnly)) + break; } } } @@ -3099,13 +3100,30 @@ filterProtocolRequirements( return Filtered; } -void ConformanceChecker:: +bool ConformanceChecker:: diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) { auto LocalMissing = getLocalMissingWitness(); // If this conformance has nothing to complain, return. if (LocalMissing.empty()) - return; + return false; + + // Diagnose the missing witnesses. + for (auto &Missing : LocalMissing) { + auto requirement = Missing.requirement; + auto matches = Missing.matches; + auto nominal = Adoptee->getAnyNominal(); + diagnoseOrDefer(requirement, true, + [requirement, matches, nominal](NormalProtocolConformance *conformance) { + auto dc = conformance->getDeclContext(); + auto *protocol = conformance->getProtocol(); + // Possibly diagnose reason for automatic derivation failure + DerivedConformance::tryDiagnoseFailedDerivation(dc, nominal, protocol); + // Diagnose each of the matches. + for (const auto &match : matches) + diagnoseMatch(dc->getParentModule(), conformance, requirement, match); + }); + } const auto InsertFixit = []( NormalProtocolConformance *Conf, SourceLoc ComplainLoc, bool EditorMode, @@ -3226,19 +3244,19 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) { }); } clearGlobalMissingWitnesses(); - return; + return true; } case MissingWitnessDiagnosisKind::ErrorOnly: { diagnoseOrDefer( LocalMissing[0].requirement, true, [](NormalProtocolConformance *) {}); - return; + return true; } case MissingWitnessDiagnosisKind::FixItOnly: InsertFixit(Conformance, Loc, IsEditorMode, filterProtocolRequirements(GlobalMissingWitnesses.getArrayRef(), Adoptee)); clearGlobalMissingWitnesses(); - return; + return true; } } @@ -3744,17 +3762,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { if (!numViable) { // Save the missing requirement for later diagnosis. GlobalMissingWitnesses.insert({requirement, matches}); - diagnoseOrDefer(requirement, true, - [requirement, matches, nominal](NormalProtocolConformance *conformance) { - auto dc = conformance->getDeclContext(); - auto *protocol = conformance->getProtocol(); - // Possibly diagnose reason for automatic derivation failure - DerivedConformance::tryDiagnoseFailedDerivation(dc, nominal, protocol); - // Diagnose each of the matches. - for (const auto &match : matches) - diagnoseMatch(dc->getParentModule(), conformance, requirement, match); - }); - return ResolveWitnessResult::ExplicitFailed; + return ResolveWitnessResult::Missing; } diagnoseOrDefer(requirement, true, diff --git a/lib/Sema/TypeCheckProtocol.h b/lib/Sema/TypeCheckProtocol.h index 0735417f8ac49..988e88ac5f793 100644 --- a/lib/Sema/TypeCheckProtocol.h +++ b/lib/Sema/TypeCheckProtocol.h @@ -789,7 +789,10 @@ class ConformanceChecker : public WitnessChecker { public: /// Call this to diagnose currently known missing witnesses. - void diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind); + /// + /// \returns true if any witnesses were diagnosed. + bool diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind); + /// Emit any diagnostics that have been delayed. void emitDelayedDiags(); From fe4a8bb9f926684d2621fba13cc326580380bcbc Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 2 Oct 2020 18:35:08 -0700 Subject: [PATCH 190/745] [Clang importer] Allow both sync and async imports with the same name. The Clang importer was filtering out cases where the same declaration is imported twice under the same name, which can now happen when one is synchronous and one is asynchronous. This happens when, e.g., an Objective-C class provides both a completion-hander-based asynchronous version and a synchronous version, and the Swift names line up after the completion-handler parameter is dropped. Stop filtering these out. Overload resolution is capable of handling synchronous/asynchronous overloading based on context. --- lib/ClangImporter/ClangImporter.cpp | 82 +++++++++---------- lib/ClangImporter/ImportName.cpp | 15 ++-- test/ClangImporter/objc_async.swift | 12 +++ .../usr/include/ObjCConcurrency.h | 10 +++ 4 files changed, 72 insertions(+), 47 deletions(-) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index a4ce1d884f45d..0720569da9558 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3701,48 +3701,46 @@ void ClangImporter::Implementation::lookupValue( // If we have a declaration and nothing matched so far, try the names used // in other versions of Swift. - if (!anyMatching) { - if (auto clangDecl = entry.dyn_cast()) { - const clang::NamedDecl *recentClangDecl = - clangDecl->getMostRecentDecl(); - - CurrentVersion.forEachOtherImportNameVersion( - SwiftContext.LangOpts.EnableExperimentalConcurrency, - [&](ImportNameVersion nameVersion) { - if (anyMatching) - return; - - // Check to see if the name and context match what we expect. - ImportedName newName = importFullName(recentClangDecl, nameVersion); - if (!newName.getDeclName().matchesRef(name)) - return; - - // If we asked for an async import and didn't find one, skip this. - // This filters out duplicates. - if (nameVersion.supportsConcurrency() && - !newName.getAsyncInfo()) - return; - - const clang::DeclContext *clangDC = - newName.getEffectiveContext().getAsDeclContext(); - if (!clangDC || !clangDC->isFileContext()) - return; - - // Then try to import the decl under the alternate name. - auto alternateNamedDecl = - cast_or_null(importDeclReal(recentClangDecl, - nameVersion)); - if (!alternateNamedDecl || alternateNamedDecl == decl) - return; - assert(alternateNamedDecl->getName().matchesRef(name) && - "importFullName behaved differently from importDecl"); - if (alternateNamedDecl->getDeclContext()->isModuleScopeContext()) { - consumer.foundDecl(alternateNamedDecl, - DeclVisibilityKind::VisibleAtTopLevel); - anyMatching = true; - } - }); - } + if (auto clangDecl = entry.dyn_cast()) { + const clang::NamedDecl *recentClangDecl = + clangDecl->getMostRecentDecl(); + + CurrentVersion.forEachOtherImportNameVersion( + SwiftContext.LangOpts.EnableExperimentalConcurrency, + [&](ImportNameVersion nameVersion) { + if (anyMatching) + return; + + // Check to see if the name and context match what we expect. + ImportedName newName = importFullName(recentClangDecl, nameVersion); + if (!newName.getDeclName().matchesRef(name)) + return; + + // If we asked for an async import and didn't find one, skip this. + // This filters out duplicates. + if (nameVersion.supportsConcurrency() && + !newName.getAsyncInfo()) + return; + + const clang::DeclContext *clangDC = + newName.getEffectiveContext().getAsDeclContext(); + if (!clangDC || !clangDC->isFileContext()) + return; + + // Then try to import the decl under the alternate name. + auto alternateNamedDecl = + cast_or_null(importDeclReal(recentClangDecl, + nameVersion)); + if (!alternateNamedDecl || alternateNamedDecl == decl) + return; + assert(alternateNamedDecl->getName().matchesRef(name) && + "importFullName behaved differently from importDecl"); + if (alternateNamedDecl->getDeclContext()->isModuleScopeContext()) { + consumer.foundDecl(alternateNamedDecl, + DeclVisibilityKind::VisibleAtTopLevel); + anyMatching = true; + } + }); } } } diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 972e9ca887bf7..cae15627ba7ef 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -2084,13 +2084,15 @@ ImportedName NameImporter::importName(const clang::NamedDecl *decl, bool NameImporter::forEachDistinctImportName( const clang::NamedDecl *decl, ImportNameVersion activeVersion, llvm::function_ref action) { - using ImportNameKey = std::pair; + using ImportNameKey = std::tuple; SmallVector seenNames; ImportedName newName = importName(decl, activeVersion); if (!newName) return true; - ImportNameKey key(newName.getDeclName(), newName.getEffectiveContext()); + + ImportNameKey key(newName.getDeclName(), newName.getEffectiveContext(), + newName.getAsyncInfo().hasValue()); if (action(newName, activeVersion)) seenNames.push_back(key); @@ -2101,15 +2103,18 @@ bool NameImporter::forEachDistinctImportName( ImportedName newName = importName(decl, nameVersion); if (!newName) return; - ImportNameKey key(newName.getDeclName(), newName.getEffectiveContext()); + ImportNameKey key(newName.getDeclName(), newName.getEffectiveContext(), + newName.getAsyncInfo().hasValue()); bool seen = llvm::any_of( seenNames, [&key](const ImportNameKey &existing) -> bool { - return key.first == existing.first && - key.second.equalsWithoutResolving(existing.second); + return std::get<0>(key) == std::get<0>(existing) && + std::get<2>(key) == std::get<2>(existing) && + std::get<1>(key).equalsWithoutResolving(std::get<1>(existing)); }); if (seen) return; + if (action(newName, nameVersion)) seenNames.push_back(key); }); diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift index 201328e14ad9e..c3cb8a68ac511 100644 --- a/test/ClangImporter/objc_async.swift +++ b/test/ClangImporter/objc_async.swift @@ -11,6 +11,18 @@ func testSlowServer(slowServer: SlowServer) async throws { let _: String = await try slowServer.findAnswerFailingly() ?? "nope" let _: Void = await slowServer.doSomethingFun("jump") let _: (Int) -> Void = slowServer.completionHandler + + // async version + let _: Int = await slowServer.doSomethingConflicted("thinking") + + // still async version... + let _: Int = slowServer.doSomethingConflicted("thinking") + // expected-error@-1{{call is 'async' but is not marked with 'await'}} +} + +func testSlowServerSynchronous(slowServer: SlowServer) { + // synchronous version + let _: Int = slowServer.doSomethingConflicted("thinking") } func testSlowServerOldSchool(slowServer: SlowServer) { diff --git a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h index a74a5a8500499..39892d4ea9ac2 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h @@ -11,6 +11,9 @@ -(BOOL)findAnswerFailinglyWithError:(NSError * _Nullable * _Nullable)error completion:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswerFailingly(completionHandler:)"))); -(void)doSomethingFun:(NSString *)operation then:(void (^)(void))completionHandler; @property(readwrite) void (^completionHandler)(NSInteger); + +-(void)doSomethingConflicted:(NSString *)operation completionHandler:(void (^)(NSInteger))handler; +-(NSInteger)doSomethingConflicted:(NSString *)operation; @end @protocol RefrigeratorDelegate @@ -21,4 +24,11 @@ - (BOOL)refrigerator:(id)fridge didRemoveItem:(id)item; @end +@protocol ConcurrentProtocol +-(void)askUserToSolvePuzzle:(NSString *)puzzle completionHandler:(void (^ _Nullable)(NSString * _Nullable, NSError * _Nullable))completionHandler; + +@optional +-(void)askUserToJumpThroughHoop:(NSString *)hoop completionHandler:(void (^ _Nullable)(NSString *))completionHandler; +@end + #pragma clang assume_nonnull end From 50f870566a1a35b4073adb1bce078451790b74a5 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 2 Oct 2020 18:38:26 -0700 Subject: [PATCH 191/745] Handle conformance with multiple protocol requirements with the same selector. When concurrency is enabled, the same Objective-C method on a protocol can be imported as two different protocol requirements, both of which have the same selector. When performing conformance checking, only treat a given @objc protocol requirement as "unsatisfied" by a conformance if none of the requirements with the same Objective-C selector (+ instance/class designation) are satisfied. --- lib/Sema/TypeCheckDeclObjC.cpp | 36 ++-- lib/Sema/TypeCheckProtocol.cpp | 175 ++++++++++++++++++- lib/Sema/TypeCheckProtocol.h | 21 +++ test/decl/protocol/conforms/objc_async.swift | 52 ++++++ 4 files changed, 266 insertions(+), 18 deletions(-) create mode 100644 test/decl/protocol/conforms/objc_async.swift diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 2659e564d6d92..aceefa5e695e7 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -2372,12 +2372,22 @@ bool swift::diagnoseObjCUnsatisfiedOptReqConflicts(SourceFile &sf) { if (conflicts.empty()) continue; + auto bestConflict = conflicts[0]; + for (auto conflict : conflicts) { + if (conflict->getName().isCompoundName() && + conflict->getName().getArgumentNames().size() == + req->getName().getArgumentNames().size()) { + bestConflict = conflict; + break; + } + } + // Diagnose the conflict. auto reqDiagInfo = getObjCMethodDiagInfo(unsatisfied.second); - auto conflictDiagInfo = getObjCMethodDiagInfo(conflicts[0]); + auto conflictDiagInfo = getObjCMethodDiagInfo(bestConflict); auto protocolName = cast(req->getDeclContext())->getName(); - Ctx.Diags.diagnose(conflicts[0], + Ctx.Diags.diagnose(bestConflict, diag::objc_optional_requirement_conflict, conflictDiagInfo.first, conflictDiagInfo.second, @@ -2387,9 +2397,9 @@ bool swift::diagnoseObjCUnsatisfiedOptReqConflicts(SourceFile &sf) { protocolName); // Fix the name of the witness, if we can. - if (req->getName() != conflicts[0]->getName() && - req->getKind() == conflicts[0]->getKind() && - isa(req) == isa(conflicts[0])) { + if (req->getName() != bestConflict->getName() && + req->getKind() == bestConflict->getKind() && + isa(req) == isa(bestConflict)) { // They're of the same kind: fix the name. unsigned kind; if (isa(req)) @@ -2402,29 +2412,29 @@ bool swift::diagnoseObjCUnsatisfiedOptReqConflicts(SourceFile &sf) { llvm_unreachable("unhandled @objc declaration kind"); } - auto diag = Ctx.Diags.diagnose(conflicts[0], + auto diag = Ctx.Diags.diagnose(bestConflict, diag::objc_optional_requirement_swift_rename, kind, req->getName()); // Fix the Swift name. - fixDeclarationName(diag, conflicts[0], req->getName()); + fixDeclarationName(diag, bestConflict, req->getName()); // Fix the '@objc' attribute, if needed. - if (!conflicts[0]->canInferObjCFromRequirement(req)) - fixDeclarationObjCName(diag, conflicts[0], - conflicts[0]->getObjCRuntimeName(), + if (!bestConflict->canInferObjCFromRequirement(req)) + fixDeclarationObjCName(diag, bestConflict, + bestConflict->getObjCRuntimeName(), req->getObjCRuntimeName(), /*ignoreImpliedName=*/true); } // @nonobjc will silence this warning. bool hasExplicitObjCAttribute = false; - if (auto objcAttr = conflicts[0]->getAttrs().getAttribute()) + if (auto objcAttr = bestConflict->getAttrs().getAttribute()) hasExplicitObjCAttribute = !objcAttr->isImplicit(); if (!hasExplicitObjCAttribute) - Ctx.Diags.diagnose(conflicts[0], diag::req_near_match_nonobjc, true) + Ctx.Diags.diagnose(bestConflict, diag::req_near_match_nonobjc, true) .fixItInsert( - conflicts[0]->getAttributeInsertionLoc(/*forModifier=*/false), + bestConflict->getAttributeInsertionLoc(/*forModifier=*/false), "@nonobjc "); Ctx.Diags.diagnose(getDeclContextLoc(unsatisfied.first), diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index d69a00dadc30d..0a4af7369f9f7 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -1525,7 +1525,9 @@ class swift::MultiConformanceChecker { NormalProtocolConformance *conformance, bool issueFixit); /// Determine whether the given requirement was left unsatisfied. - bool isUnsatisfiedReq(NormalProtocolConformance *conformance, ValueDecl *req); + bool isUnsatisfiedReq( + ConformanceChecker &checker, NormalProtocolConformance *conformance, + ValueDecl *req); public: MultiConformanceChecker(ASTContext &ctx) : Context(ctx) {} @@ -1551,7 +1553,8 @@ class swift::MultiConformanceChecker { }; bool MultiConformanceChecker:: -isUnsatisfiedReq(NormalProtocolConformance *conformance, ValueDecl *req) { +isUnsatisfiedReq(ConformanceChecker &checker, + NormalProtocolConformance *conformance, ValueDecl *req) { if (conformance->isInvalid()) return false; if (isa(req)) return false; @@ -1559,9 +1562,25 @@ isUnsatisfiedReq(NormalProtocolConformance *conformance, ValueDecl *req) { ? conformance->getWitnessUncached(req).getDecl() : nullptr; - // An optional requirement might not have a witness... - if (!witness) + if (!witness) { + // If another @objc requirement refers to the same Objective-C + // method, this requirement isn't unsatisfied. + if (conformance->getProtocol()->isObjC() && + isa(req)) { + auto funcReq = cast(req); + auto key = checker.getObjCMethodKey(funcReq); + for (auto otherReq : checker.getObjCRequirements(key)) { + if (otherReq == req) + continue; + + if (conformance->hasWitness(otherReq)) + return false; + } + } + + // An optional requirement might not have a witness. return req->getAttrs().hasAttribute(); + } // If the witness lands within the declaration context of the conformance, // record it as a "covered" member. @@ -1586,13 +1605,26 @@ void MultiConformanceChecker::checkAllConformances() { continue; // Check whether there are any unsatisfied requirements. auto proto = conformance->getProtocol(); + Optional checker; + auto getChecker = [&] () -> ConformanceChecker& { + if (checker) + return *checker; + + if (!AllUsedCheckers.empty() && + AllUsedCheckers.back().Conformance == conformance) + return AllUsedCheckers.back(); + + checker.emplace(getASTContext(), conformance, MissingWitnesses); + return *checker; + }; + for (auto member : proto->getMembers()) { auto req = dyn_cast(member); if (!req || !req->isProtocolRequirement()) continue; // If the requirement is unsatisfied, we might want to warn // about near misses; record it. - if (isUnsatisfiedReq(conformance, req)) { + if (isUnsatisfiedReq(getChecker(), conformance, req)) { UnsatisfiedReqs.push_back(req); continue; } @@ -3100,10 +3132,108 @@ filterProtocolRequirements( return Filtered; } +/// Prune the set of missing witnesses for the given conformance, eliminating +/// any requirements that do not actually need to satisfied. +static ArrayRef pruneMissingWitnesses( + ConformanceChecker &checker, + ProtocolDecl *proto, + NormalProtocolConformance *conformance, + ArrayRef missingWitnesses, + SmallVectorImpl &scratch) { + if (missingWitnesses.empty()) + return missingWitnesses; + + // For an @objc protocol defined in Objective-C, the Clang importer might + // have imported the same underlying Objective-C declaration as more than + // one Swift declaration. If we aren't in an imported @objc protocol, there + // is nothing to do. + if (!proto->isObjC()) + return missingWitnesses; + + // Consider each of the missing witnesses to remove any that should not + // longer be considered "missing". + llvm::SmallDenseSet + alreadyReportedAsMissing; + bool removedAny = false; + for (unsigned missingWitnessIdx : indices(missingWitnesses)) { + const auto &missingWitness = missingWitnesses[missingWitnessIdx]; + + // Local function to skip this particular witness. + auto skipWitness = [&] { + if (removedAny) + return; + + // This is the first witness we skipped. Copy all of the earlier + // missing witnesses over. + scratch.clear(); + scratch.append( + missingWitnesses.begin(), + missingWitnesses.begin() + missingWitnessIdx); + removedAny = true; + }; + + // Local function to add this particular witness. + auto addWitness = [&] { + if (removedAny) + scratch.push_back(missingWitness); + }; + + // We only care about functions + auto funcRequirement = dyn_cast( + missingWitness.requirement); + if (!funcRequirement) { + addWitness(); + continue; + } + + // ... whose selector is one that maps to multiple requirement declarations. + auto key = checker.getObjCMethodKey(funcRequirement); + auto matchingRequirements = checker.getObjCRequirements(key); + if (matchingRequirements.size() < 2) { + addWitness(); + continue; + } + + // If we have already reported a function with this selector as missing, + // don't do it again. + if (!alreadyReportedAsMissing.insert(key).second) { + skipWitness(); + continue; + } + + // If there is a witness for any of the *other* requirements with this + // same selector, don't report it. + bool foundOtherWitness = false; + for (auto otherReq : matchingRequirements) { + if (otherReq == funcRequirement) + continue; + + if (conformance->getWitness(otherReq)) { + foundOtherWitness = true; + break; + } + } + + if (foundOtherWitness) + skipWitness(); + else + addWitness(); + } + + if (removedAny) + return scratch; + + return missingWitnesses; +} + bool ConformanceChecker:: diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) { auto LocalMissing = getLocalMissingWitness(); + SmallVector MissingWitnessScratch; + LocalMissing = pruneMissingWitnesses( + *this, Proto, Conformance, LocalMissing, MissingWitnessScratch); + // If this conformance has nothing to complain, return. if (LocalMissing.empty()) return false; @@ -4451,6 +4581,41 @@ void ConformanceChecker::checkConformance(MissingWitnessDiagnosisKind Kind) { } } +/// Retrieve the Objective-C method key from the given function. +auto ConformanceChecker::getObjCMethodKey(AbstractFunctionDecl *func) + -> ObjCMethodKey { + return ObjCMethodKey(func->getObjCSelector(), func->isInstanceMember()); +} + +/// Retrieve the Objective-C requirements in this protocol that have the +/// given Objective-C method key. +ArrayRef +ConformanceChecker::getObjCRequirements(ObjCMethodKey key) { + auto proto = Conformance->getProtocol(); + if (!proto->isObjC()) + return { }; + + // Fill in the data structure if we haven't done so yet. + if (!computedObjCMethodRequirements) { + for (auto requirement : proto->getSemanticMembers()) { + auto funcRequirement = dyn_cast(requirement); + if (!funcRequirement) + continue; + + objcMethodRequirements[getObjCMethodKey(funcRequirement)] + .push_back(funcRequirement); + } + + computedObjCMethodRequirements = true; + } + + auto known = objcMethodRequirements.find(key); + if (known == objcMethodRequirements.end()) + return { }; + + return known->second; +} + void swift::diagnoseConformanceFailure(Type T, ProtocolDecl *Proto, DeclContext *DC, diff --git a/lib/Sema/TypeCheckProtocol.h b/lib/Sema/TypeCheckProtocol.h index 988e88ac5f793..3d3e8e08d9596 100644 --- a/lib/Sema/TypeCheckProtocol.h +++ b/lib/Sema/TypeCheckProtocol.h @@ -678,6 +678,12 @@ class DelayedMissingWitnesses : public MissingWitnessesBase { /// This helper class handles most of the details of checking whether a /// given type (\c Adoptee) conforms to a protocol (\c Proto). class ConformanceChecker : public WitnessChecker { +public: + /// Key that can be used to uniquely identify a particular Objective-C + /// method. + using ObjCMethodKey = std::pair; + +private: friend class MultiConformanceChecker; friend class AssociatedTypeInference; @@ -712,6 +718,14 @@ class ConformanceChecker : public WitnessChecker { /// Whether we checked the requirement signature already. bool CheckedRequirementSignature = false; + /// Mapping from Objective-C methods to the set of requirements within this + /// protocol that have the same selector and instance/class designation. + llvm::SmallDenseMap, 4> + objcMethodRequirements; + + /// Whether objcMethodRequirements has been computed. + bool computedObjCMethodRequirements = false; + /// Retrieve the associated types that are referenced by the given /// requirement with a base of 'Self'. ArrayRef getReferencedAssociatedTypes(ValueDecl *req); @@ -827,6 +841,13 @@ class ConformanceChecker : public WitnessChecker { /// Check the entire protocol conformance, ensuring that all /// witnesses are resolved and emitting any diagnostics. void checkConformance(MissingWitnessDiagnosisKind Kind); + + /// Retrieve the Objective-C method key from the given function. + ObjCMethodKey getObjCMethodKey(AbstractFunctionDecl *func); + + /// Retrieve the Objective-C requirements in this protocol that have the + /// given Objective-C method key. + ArrayRef getObjCRequirements(ObjCMethodKey key); }; /// Captures the state needed to infer associated types. diff --git a/test/decl/protocol/conforms/objc_async.swift b/test/decl/protocol/conforms/objc_async.swift new file mode 100644 index 0000000000000..8284b0e00564f --- /dev/null +++ b/test/decl/protocol/conforms/objc_async.swift @@ -0,0 +1,52 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -enable-experimental-concurrency %s -verify -verify-ignore-unknown + +// REQUIRES: objc_interop +import Foundation +import ObjCConcurrency + +// Conform via async method +class C1: ConcurrentProtocol { + func askUser(toSolvePuzzle puzzle: String) async throws -> String? { nil } + + func askUser(toJumpThroughHoop hoop: String) async -> String { "hello" } +} + +// Conform via completion-handler method +class C2: ConcurrentProtocol { + func askUser(toSolvePuzzle puzzle: String, completionHandler: ((String?, Error?) -> Void)?) { + completionHandler?("hello", nil) + } + + func askUser(toJumpThroughHoop hoop: String, completionHandler: ((String) -> Void)?) { + completionHandler?("hello") + } +} + +// Conform via both; this is an error +class C3: ConcurrentProtocol { + // expected-note@+1{{method 'askUser(toSolvePuzzle:)' declared here}} + func askUser(toSolvePuzzle puzzle: String) async throws -> String? { nil } + + // expected-error@+1{{'askUser(toSolvePuzzle:completionHandler:)' with Objective-C selector 'askUserToSolvePuzzle:completionHandler:' conflicts with method 'askUser(toSolvePuzzle:)' with the same Objective-C selector}} + func askUser(toSolvePuzzle puzzle: String, completionHandler: ((String?, Error?) -> Void)?) { + completionHandler?("hello", nil) + } +} + +// Conform but forget to supply either. Also an error. +// FIXME: Suppress one of the notes? +class C4: ConcurrentProtocol { // expected-error{{type 'C4' does not conform to protocol 'ConcurrentProtocol'}} +} + +class C5 { +} + +extension C5: ConcurrentProtocol { + func askUser(toSolvePuzzle puzzle: String, completionHandler: ((String?, Error?) -> Void)?) { + completionHandler?("hello", nil) + } + + func askUser(toJumpThroughHoop hoop: String, completionHandler: ((String) -> Void)?) { + completionHandler?("hello") + } +} From 591e6e89e259818a8dd56bbba41057e776d92c8a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 2 Oct 2020 22:01:51 -0700 Subject: [PATCH 192/745] [Concurrency] Only infer @asyncHandler for witnesses within actor classes. Actor classes are by definition new code, so only perform inference of @asyncHandler from a protocol requirement to witnesses when those witnesses are within an actor class. This is partly because @asyncHandler might not have reasonable semantics outside of actor classes anywhere (where would you execute the detached task?), and partly because it is a source-compatibility problem due to the combination of... 1) Inferring @asyncHandler on imported Objective-C protocol methods for existing protocols, 2) Inferring @asyncHandler from those protocol methods to an existing method in a class that conforms to that protocol, and 3) Using an API that is now overloaded for both synchronous and asynchronous callers will end up preferring the asynchronous version, then fail due to a missing "await". The individual steps here are all reasonable, but the result is a source break, so back off on @asyncHandler inference for witnesses outside of actor classes. New code gets the more-asynchronous behavior for free. --- lib/Sema/TypeCheckConcurrency.cpp | 6 ++- lib/Sema/TypeCheckProtocol.cpp | 58 +++++++++++++------------- lib/Sema/TypeCheckProtocol.h | 5 --- test/attr/asynchandler.swift | 25 ++++++++--- test/decl/protocol/special/Actor.swift | 2 +- 5 files changed, 52 insertions(+), 44 deletions(-) diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index a7543e8265f6e..e40e5656d8fc5 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -110,14 +110,16 @@ bool IsAsyncHandlerRequest::evaluate( // Are we in a context where inference is possible? auto dc = func->getDeclContext(); - if (!dc->isTypeContext() || !dc->getParentSourceFile() || - isa(dc) || !func->hasBody()) + if (!dc->getSelfClassDecl() || !dc->getParentSourceFile() || !func->hasBody()) return false; // Is it possible to infer @asyncHandler for this function at all? if (!func->canBeAsyncHandler()) return false; + if (!dc->getSelfClassDecl()->isActor()) + return false; + // Add an implicit @asyncHandler attribute and return true. We're done. auto addImplicitAsyncHandlerAttr = [&] { func->getAttrs().add(new (func->getASTContext()) AsyncHandlerAttr(true)); diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 0a4af7369f9f7..89030a79f4321 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -737,18 +737,6 @@ swift::matchWitness( } } - // Check for actor-isolation consistency. - switch (getActorIsolation(witness)) { - case ActorIsolation::ActorInstance: - // Actor-isolated witnesses cannot conform to protocol requirements. - return RequirementMatch(witness, MatchKind::ActorIsolatedWitness); - - case ActorIsolation::ActorPrivileged: - case ActorIsolation::Independent: - case ActorIsolation::Unspecified: - break; - } - // Now finalize the match. auto result = finalize(anyRenaming, optionalAdjustments); // Check if the requirement's `@differentiable` attributes are satisfied by @@ -2487,24 +2475,6 @@ diagnoseMatch(ModuleDecl *module, NormalProtocolConformance *conformance, case MatchKind::NonObjC: diags.diagnose(match.Witness, diag::protocol_witness_not_objc); break; - case MatchKind::ActorIsolatedWitness: { - bool canBeAsyncHandler = false; - if (auto witnessFunc = dyn_cast(match.Witness)) { - canBeAsyncHandler = !witnessFunc->isAsyncHandler() && - witnessFunc->canBeAsyncHandler(); - } - auto diag = match.Witness->diagnose( - canBeAsyncHandler ? diag::actor_isolated_witness_could_be_async_handler - : diag::actor_isolated_witness, - match.Witness->getDescriptiveKind(), match.Witness->getName()); - - if (canBeAsyncHandler) { - diag.fixItInsert( - match.Witness->getAttributeInsertionLoc(false), "@asyncHandler "); - } - break; - } - case MatchKind::MissingDifferentiableAttr: { auto *witness = match.Witness; // Emit a note and fix-it showing the missing requirement `@differentiable` @@ -4367,6 +4337,34 @@ void ConformanceChecker::resolveValueWitnesses() { return; } + // Check for actor-isolation consistency. + switch (getActorIsolation(witness)) { + case ActorIsolation::ActorInstance: { + // Actor-isolated witnesses cannot conform to protocol requirements. + bool canBeAsyncHandler = false; + if (auto witnessFunc = dyn_cast(witness)) { + canBeAsyncHandler = !witnessFunc->isAsyncHandler() && + witnessFunc->canBeAsyncHandler(); + } + auto diag = witness->diagnose( + canBeAsyncHandler + ? diag::actor_isolated_witness_could_be_async_handler + : diag::actor_isolated_witness, + witness->getDescriptiveKind(), witness->getName()); + + if (canBeAsyncHandler) { + diag.fixItInsert( + witness->getAttributeInsertionLoc(false), "@asyncHandler "); + } + return; + } + + case ActorIsolation::ActorPrivileged: + case ActorIsolation::Independent: + case ActorIsolation::Unspecified: + break; + } + // Objective-C checking for @objc requirements. if (requirement->isObjC() && requirement->getName() == witness->getName() && diff --git a/lib/Sema/TypeCheckProtocol.h b/lib/Sema/TypeCheckProtocol.h index 3d3e8e08d9596..3a8ecf1e34693 100644 --- a/lib/Sema/TypeCheckProtocol.h +++ b/lib/Sema/TypeCheckProtocol.h @@ -268,9 +268,6 @@ enum class MatchKind : uint8_t { /// The witness is explicitly @nonobjc but the requirement is @objc. NonObjC, - /// The witness is part of actor-isolated state. - ActorIsolatedWitness, - /// The witness is missing a `@differentiable` attribute from the requirement. MissingDifferentiableAttr, @@ -503,7 +500,6 @@ struct RequirementMatch { case MatchKind::AsyncConflict: case MatchKind::ThrowsConflict: case MatchKind::NonObjC: - case MatchKind::ActorIsolatedWitness: case MatchKind::MissingDifferentiableAttr: case MatchKind::EnumCaseWithAssociatedValues: return false; @@ -536,7 +532,6 @@ struct RequirementMatch { case MatchKind::AsyncConflict: case MatchKind::ThrowsConflict: case MatchKind::NonObjC: - case MatchKind::ActorIsolatedWitness: case MatchKind::MissingDifferentiableAttr: case MatchKind::EnumCaseWithAssociatedValues: return false; diff --git a/test/attr/asynchandler.swift b/test/attr/asynchandler.swift index e3147f62edbdd..ce5d9a022f5a6 100644 --- a/test/attr/asynchandler.swift +++ b/test/attr/asynchandler.swift @@ -27,18 +27,13 @@ func asyncHandlerBad3() throws { } func asyncHandlerBad4(result: inout Int) { } // expected-error@-1{{'inout' parameter is not allowed in '@asyncHandler' function}} -struct X { +actor class X { @asyncHandler func asyncHandlerMethod() { } - @asyncHandler - mutating func asyncHandlerMethodBad1() { } - // expected-error@-1{{'@asyncHandler' function cannot be 'mutating'}}{{3-12=}} - @asyncHandler init() { } // expected-error@-1{{@asyncHandler may only be used on 'func' declarations}} } - // Inference of @asyncHandler protocol P { @asyncHandler func callback() @@ -50,3 +45,21 @@ extension X: P { let _ = await globalAsyncFunction() } } + +class Y: P { + // @asyncHandler is not inferred for classes + + func callback() { + // expected-note@-1{{add 'async' to function 'callback()' to make it asynchronous}} + // expected-note@-2{{add '@asyncHandler' to function 'callback()' to create an implicit asynchronous context}} + + // okay, it's an async context + let _ = await globalAsyncFunction() // expected-error{{'async' in a function that does not support concurrency}} + } +} + +struct Z { + @asyncHandler + mutating func asyncHandlerMethodBad1() { } + // expected-error@-1{{'@asyncHandler' function cannot be 'mutating'}}{{3-12=}} +} diff --git a/test/decl/protocol/special/Actor.swift b/test/decl/protocol/special/Actor.swift index 76c8f83b8a6ab..e1f7a569afd3a 100644 --- a/test/decl/protocol/special/Actor.swift +++ b/test/decl/protocol/special/Actor.swift @@ -42,7 +42,7 @@ class C1: Actor { // Method that is not usable as a witness. actor class BA1 { - func enqueue(partialTask: PartialAsyncTask) { } // expected-error{{invalid redeclaration of synthesized implementation for protocol requirement 'enqueue(partialTask:)'}} + func enqueue(partialTask: PartialAsyncTask) { } // expected-note{{actor-isolated instance method 'enqueue(partialTask:)' cannot be used to satisfy a protocol requirement; did you mean to make it an asychronous handler?}} } // Method that isn't part of the main class definition cannot be used to From e1004499c4a223765710ffc960bcb93dd2fdb166 Mon Sep 17 00:00:00 2001 From: John McCall Date: Sat, 3 Oct 2020 02:24:10 -0400 Subject: [PATCH 193/745] [NFC] Split ProtocolDispatchStrategy out of MetadataValues.h. This allows us to avoid rebuilding most of the compiler whenever we add a new ABI constant. --- cmake/modules/AddSwift.cmake | 6 +++ include/swift/ABI/MetadataValues.h | 29 +---------- include/swift/ABI/ProtocolDispatchStrategy.h | 54 ++++++++++++++++++++ include/swift/SIL/TypeLowering.h | 5 +- lib/IRGen/IRGen.cpp | 1 + 5 files changed, 65 insertions(+), 30 deletions(-) create mode 100644 include/swift/ABI/ProtocolDispatchStrategy.h diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index ec61d2abb836a..d1e2825f5f1af 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -630,3 +630,9 @@ macro(add_swift_tool_symlink name dest component) add_llvm_tool_symlink(${name} ${dest} ALWAYS_GENERATE) llvm_install_symlink(${name} ${dest} ALWAYS_GENERATE COMPONENT ${component}) endmacro() + +# Declare that files in this library are built with LLVM's support +# libraries available. +macro(set_swift_llvm_is_available) + add_compile_options(-DSWIFT_LLVM_SUPPORT_IS_AVAILABLE) +endmacro() diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index e45629dce240a..bd044c5e36361 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -20,10 +20,10 @@ #define SWIFT_ABI_METADATAVALUES_H #include "swift/ABI/KeyPath.h" +#include "swift/ABI/ProtocolDispatchStrategy.h" #include "swift/AST/Ownership.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/FlagSet.h" -#include "swift/Runtime/Unreachable.h" #include #include @@ -410,20 +410,6 @@ enum class SpecialProtocol: uint8_t { Error = 1, }; -/// Identifiers for protocol method dispatch strategies. -enum class ProtocolDispatchStrategy: uint8_t { - /// Uses ObjC method dispatch. - /// - /// This must be 0 for ABI compatibility with Objective-C protocol_t records. - ObjC = 0, - - /// Uses Swift protocol witness table dispatch. - /// - /// To invoke methods of this protocol, a pointer to a protocol witness table - /// corresponding to the protocol conformance must be available. - Swift = 1, -}; - /// Flags for protocol descriptors. class ProtocolDescriptorFlags { typedef uint32_t int_type; @@ -486,18 +472,7 @@ class ProtocolDescriptorFlags { /// Does the protocol require a witness table for method dispatch? bool needsWitnessTable() const { - return needsWitnessTable(getDispatchStrategy()); - } - - static bool needsWitnessTable(ProtocolDispatchStrategy strategy) { - switch (strategy) { - case ProtocolDispatchStrategy::ObjC: - return false; - case ProtocolDispatchStrategy::Swift: - return true; - } - - swift_runtime_unreachable("Unhandled ProtocolDispatchStrategy in switch."); + return swift::protocolRequiresWitnessTable(getDispatchStrategy()); } /// Return the identifier if this is a special runtime-known protocol. diff --git a/include/swift/ABI/ProtocolDispatchStrategy.h b/include/swift/ABI/ProtocolDispatchStrategy.h new file mode 100644 index 0000000000000..c5276ee7b541e --- /dev/null +++ b/include/swift/ABI/ProtocolDispatchStrategy.h @@ -0,0 +1,54 @@ +//===--- ProtocolDispatchStrategy.h - ---------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This header declares the ProtocolDispatchStrategy enum and some +// related operations. It's split out because we would otherwise need +// to include MetadataValues.h in some relatively central headers.s +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_ABI_PROTOCOLDISPATCHSTRATEGY_H +#define SWIFT_ABI_PROTOCOLDISPATCHSTRATEGY_H + +#include "swift/Runtime/Unreachable.h" + +namespace swift { + +/// Identifiers for protocol method dispatch strategies. +enum class ProtocolDispatchStrategy: uint8_t { + /// Uses ObjC method dispatch. + /// + /// This must be 0 for ABI compatibility with Objective-C protocol_t records. + ObjC = 0, + + /// Uses Swift protocol witness table dispatch. + /// + /// To invoke methods of this protocol, a pointer to a protocol witness table + /// corresponding to the protocol conformance must be available. + Swift = 1, +}; + +/// Does a protocol using the given strategy require a witness table? +inline bool protocolRequiresWitnessTable(ProtocolDispatchStrategy strategy) { + switch (strategy) { + case ProtocolDispatchStrategy::ObjC: + return false; + case ProtocolDispatchStrategy::Swift: + return true; + } + + swift_runtime_unreachable("Unhandled ProtocolDispatchStrategy in switch."); +} + +} + +#endif diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index ed135eccf8e2f..98ff7ec7624df 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -13,7 +13,7 @@ #ifndef SWIFT_SIL_TYPELOWERING_H #define SWIFT_SIL_TYPELOWERING_H -#include "swift/ABI/MetadataValues.h" +#include "swift/ABI/ProtocolDispatchStrategy.h" #include "swift/AST/CaptureInfo.h" #include "swift/AST/Module.h" #include "swift/SIL/AbstractionPattern.h" @@ -792,8 +792,7 @@ class TypeConverter { /// True if a protocol uses witness tables for dynamic dispatch. static bool protocolRequiresWitnessTable(ProtocolDecl *P) { - return ProtocolDescriptorFlags::needsWitnessTable - (getProtocolDispatchStrategy(P)); + return swift::protocolRequiresWitnessTable(getProtocolDispatchStrategy(P)); } /// True if a type is passed indirectly at +0 when used as the "self" diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 7fe0c9ffffdcb..1c58d8206d624 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -16,6 +16,7 @@ #define DEBUG_TYPE "irgen" #include "IRGenModule.h" +#include "swift/ABI/MetadataValues.h" #include "swift/AST/DiagnosticsIRGen.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/IRGenRequests.h" From bb066b6fa68f06885c84838be7f7e264d70bc0e6 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 2 Oct 2020 23:41:44 -0700 Subject: [PATCH 194/745] [Concurrency] Make actor-isolated protocol witnesses an error. With actor isolation checking for protocol witnesses moved out of the witness-matching phase, move the corresponding diagnostics from notes (that would have been on the "type does not conform" error) to freestanding errors. --- include/swift/AST/DiagnosticsSema.def | 4 ++-- test/decl/class/actor/conformance.swift | 15 +++++---------- test/decl/protocol/special/Actor.swift | 2 +- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 3b222cc32cf25..14ecc3321a45c 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4172,10 +4172,10 @@ NOTE(actor_mutable_state,none, WARNING(shared_mutable_state_access,none, "reference to %0 %1 is not concurrency-safe because it involves " "shared mutable state", (DescriptiveDeclKind, DeclName)) -NOTE(actor_isolated_witness,none, +ERROR(actor_isolated_witness,none, "actor-isolated %0 %1 cannot be used to satisfy a protocol requirement", (DescriptiveDeclKind, DeclName)) -NOTE(actor_isolated_witness_could_be_async_handler,none, +ERROR(actor_isolated_witness_could_be_async_handler,none, "actor-isolated %0 %1 cannot be used to satisfy a protocol requirement; " "did you mean to make it an asychronous handler?", (DescriptiveDeclKind, DeclName)) diff --git a/test/decl/class/actor/conformance.swift b/test/decl/class/actor/conformance.swift index 70f10d4afab2f..8bd77ebacdc4e 100644 --- a/test/decl/class/actor/conformance.swift +++ b/test/decl/class/actor/conformance.swift @@ -12,37 +12,32 @@ extension MyActor: AsyncProtocol { func asyncMethod() async -> Int { return 0 } } -// FIXME: "Do you want to add a stub?" diagnostics should be suppressed here. protocol SyncProtocol { var propertyA: Int { get } - // expected-note@-1{{do you want to add a stub}} var propertyB: Int { get set } - // expected-note@-1{{do you want to add a stub}} func syncMethodA() - // expected-note@-1{{do you want to add a stub}} func syncMethodB() func syncMethodC() -> Int subscript (index: Int) -> String { get } - // expected-note@-1{{do you want to add a stub}} static func staticMethod() static var staticProperty: Int { get } } -actor class OtherActor: SyncProtocol { // expected-error{{type 'OtherActor' does not conform to protocol 'SyncProtocol'}} +actor class OtherActor: SyncProtocol { var propertyB: Int = 17 - // expected-note@-1{{actor-isolated property 'propertyB' cannot be used to satisfy a protocol requirement}} + // expected-error@-1{{actor-isolated property 'propertyB' cannot be used to satisfy a protocol requirement}} var propertyA: Int { 17 } - // expected-note@-1{{actor-isolated property 'propertyA' cannot be used to satisfy a protocol requirement}} + // expected-error@-1{{actor-isolated property 'propertyA' cannot be used to satisfy a protocol requirement}} func syncMethodA() { } - // expected-note@-1{{actor-isolated instance method 'syncMethodA()' cannot be used to satisfy a protocol requirement; did you mean to make it an asychronous handler?}}{{3-3=@asyncHandler }} + // expected-error@-1{{actor-isolated instance method 'syncMethodA()' cannot be used to satisfy a protocol requirement; did you mean to make it an asychronous handler?}}{{3-3=@asyncHandler }} // Async handlers are okay. @asyncHandler @@ -53,7 +48,7 @@ actor class OtherActor: SyncProtocol { // expected-error{{type 'OtherActor' does @actorIndependent func syncMethodC() -> Int { 5 } subscript (index: Int) -> String { "\(index)" } - // expected-note@-1{{actor-isolated subscript 'subscript(_:)' cannot be used to satisfy a protocol requirement}} + // expected-error@-1{{actor-isolated subscript 'subscript(_:)' cannot be used to satisfy a protocol requirement}} // Static methods and properties are okay. static func staticMethod() { } diff --git a/test/decl/protocol/special/Actor.swift b/test/decl/protocol/special/Actor.swift index e1f7a569afd3a..b6b0efb547d23 100644 --- a/test/decl/protocol/special/Actor.swift +++ b/test/decl/protocol/special/Actor.swift @@ -42,7 +42,7 @@ class C1: Actor { // Method that is not usable as a witness. actor class BA1 { - func enqueue(partialTask: PartialAsyncTask) { } // expected-note{{actor-isolated instance method 'enqueue(partialTask:)' cannot be used to satisfy a protocol requirement; did you mean to make it an asychronous handler?}} + func enqueue(partialTask: PartialAsyncTask) { } // expected-error{{actor-isolated instance method 'enqueue(partialTask:)' cannot be used to satisfy a protocol requirement; did you mean to make it an asychronous handler?}} } // Method that isn't part of the main class definition cannot be used to From 66a42d8de74ceffdd6b4ee21b7d833828b7a76ff Mon Sep 17 00:00:00 2001 From: John McCall Date: Sat, 3 Oct 2020 02:36:25 -0400 Subject: [PATCH 195/745] [NFC] Move some of the runtime's compiler abstraction into Compiler.h --- include/swift/Basic/Compiler.h | 49 ++++++++++++++++++++++++++++++ include/swift/Runtime/Config.h | 54 ++++------------------------------ 2 files changed, 55 insertions(+), 48 deletions(-) diff --git a/include/swift/Basic/Compiler.h b/include/swift/Basic/Compiler.h index b422175695ade..1958e762b2b83 100644 --- a/include/swift/Basic/Compiler.h +++ b/include/swift/Basic/Compiler.h @@ -34,10 +34,59 @@ #define SWIFT_ASSUME(x) #endif +/// Attributes. + #if __has_attribute(constructor) #define SWIFT_CONSTRUCTOR __attribute__((constructor)) #else #define SWIFT_CONSTRUCTOR #endif +/// \macro SWIFT_GNUC_PREREQ +/// Extend the default __GNUC_PREREQ even if glibc's features.h isn't +/// available. +#ifndef SWIFT_GNUC_PREREQ +# if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) +# define SWIFT_GNUC_PREREQ(maj, min, patch) \ + ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= \ + ((maj) << 20) + ((min) << 10) + (patch)) +# elif defined(__GNUC__) && defined(__GNUC_MINOR__) +# define SWIFT_GNUC_PREREQ(maj, min, patch) \ + ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10)) +# else +# define SWIFT_GNUC_PREREQ(maj, min, patch) 0 +# endif +#endif + + +/// SWIFT_ATTRIBUTE_NOINLINE - On compilers where we have a directive to do so, +/// mark a method "not for inlining". +#if __has_attribute(noinline) || SWIFT_GNUC_PREREQ(3, 4, 0) +#define SWIFT_ATTRIBUTE_NOINLINE __attribute__((noinline)) +#elif defined(_MSC_VER) +#define SWIFT_ATTRIBUTE_NOINLINE __declspec(noinline) +#else +#define SWIFT_ATTRIBUTE_NOINLINE +#endif + +/// SWIFT_ATTRIBUTE_ALWAYS_INLINE - On compilers where we have a directive to do +/// so, mark a method "always inline" because it is performance sensitive. GCC +/// 3.4 supported this but is buggy in various cases and produces unimplemented +/// errors, just use it in GCC 4.0 and later. +#if __has_attribute(always_inline) || SWIFT_GNUC_PREREQ(4, 0, 0) +#define SWIFT_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline)) +#elif defined(_MSC_VER) +#define SWIFT_ATTRIBUTE_ALWAYS_INLINE __forceinline +#else +#define SWIFT_ATTRIBUTE_ALWAYS_INLINE +#endif + +#ifdef __GNUC__ +#define SWIFT_ATTRIBUTE_NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define SWIFT_ATTRIBUTE_NORETURN __declspec(noreturn) +#else +#define SWIFT_ATTRIBUTE_NORETURN +#endif + #endif // SWIFT_BASIC_COMPILER_H diff --git a/include/swift/Runtime/Config.h b/include/swift/Runtime/Config.h index 372328a145d5d..da49b452f3b6e 100644 --- a/include/swift/Runtime/Config.h +++ b/include/swift/Runtime/Config.h @@ -17,71 +17,29 @@ #ifndef SWIFT_RUNTIME_CONFIG_H #define SWIFT_RUNTIME_CONFIG_H +#include "swift/Basic/Compiler.h" #include "swift/Runtime/CMakeConfig.h" -/// \macro SWIFT_RUNTIME_GNUC_PREREQ -/// Extend the default __GNUC_PREREQ even if glibc's features.h isn't -/// available. -#ifndef SWIFT_RUNTIME_GNUC_PREREQ -# if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) -# define SWIFT_RUNTIME_GNUC_PREREQ(maj, min, patch) \ - ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= \ - ((maj) << 20) + ((min) << 10) + (patch)) -# elif defined(__GNUC__) && defined(__GNUC_MINOR__) -# define SWIFT_RUNTIME_GNUC_PREREQ(maj, min, patch) \ - ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10)) -# else -# define SWIFT_RUNTIME_GNUC_PREREQ(maj, min, patch) 0 -# endif -#endif - /// SWIFT_RUNTIME_LIBRARY_VISIBILITY - If a class marked with this attribute is /// linked into a shared library, then the class should be private to the /// library and not accessible from outside it. Can also be used to mark /// variables and functions, making them private to any shared library they are /// linked into. /// On PE/COFF targets, library visibility is the default, so this isn't needed. -#if (__has_attribute(visibility) || SWIFT_RUNTIME_GNUC_PREREQ(4, 0, 0)) && \ +#if (__has_attribute(visibility) || SWIFT_GNUC_PREREQ(4, 0, 0)) && \ !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(_WIN32) #define SWIFT_RUNTIME_LIBRARY_VISIBILITY __attribute__ ((visibility("hidden"))) #else #define SWIFT_RUNTIME_LIBRARY_VISIBILITY #endif -/// Attributes. -/// SWIFT_RUNTIME_ATTRIBUTE_NOINLINE - On compilers where we have a directive to do so, -/// mark a method "not for inlining". -#if __has_attribute(noinline) || SWIFT_RUNTIME_GNUC_PREREQ(3, 4, 0) -#define SWIFT_RUNTIME_ATTRIBUTE_NOINLINE __attribute__((noinline)) -#elif defined(_MSC_VER) -#define SWIFT_RUNTIME_ATTRIBUTE_NOINLINE __declspec(noinline) -#else -#define SWIFT_RUNTIME_ATTRIBUTE_NOINLINE -#endif - -/// SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE - On compilers where we have a directive to do -/// so, mark a method "always inline" because it is performance sensitive. GCC -/// 3.4 supported this but is buggy in various cases and produces unimplemented -/// errors, just use it in GCC 4.0 and later. -#if __has_attribute(always_inline) || SWIFT_RUNTIME_GNUC_PREREQ(4, 0, 0) -#define SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline)) -#elif defined(_MSC_VER) -#define SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE __forceinline -#else -#define SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE -#endif - -#ifdef __GNUC__ -#define SWIFT_RUNTIME_ATTRIBUTE_NORETURN __attribute__((noreturn)) -#elif defined(_MSC_VER) -#define SWIFT_RUNTIME_ATTRIBUTE_NORETURN __declspec(noreturn) -#else -#define SWIFT_RUNTIME_ATTRIBUTE_NORETURN -#endif +#define SWIFT_RUNTIME_ATTRIBUTE_NOINLINE SWIFT_ATTRIBUTE_NOINLINE +#define SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE SWIFT_ATTRIBUTE_ALWAYS_INLINE +#define SWIFT_RUNTIME_ATTRIBUTE_NORETURN SWIFT_ATTRIBUTE_NORETURN /// SWIFT_RUNTIME_BUILTIN_TRAP - On compilers which support it, expands to an expression /// which causes the program to exit abnormally. -#if __has_builtin(__builtin_trap) || SWIFT_RUNTIME_GNUC_PREREQ(4, 3, 0) +#if __has_builtin(__builtin_trap) || SWIFT_GNUC_PREREQ(4, 3, 0) # define SWIFT_RUNTIME_BUILTIN_TRAP __builtin_trap() #elif defined(_MSC_VER) // The __debugbreak intrinsic is supported by MSVC, does not require forward From 0fb407943f40fc0b0ac078498fbb33d7763f08de Mon Sep 17 00:00:00 2001 From: John McCall Date: Sat, 3 Oct 2020 02:39:21 -0400 Subject: [PATCH 196/745] [NFC] Rename swift_runtime_unreachable to swift_unreachable and make it use LLVM's support when available. --- include/swift/ABI/Metadata.h | 14 +++++----- include/swift/ABI/ProtocolDispatchStrategy.h | 4 +-- .../swift/{Runtime => Basic}/Unreachable.h | 28 ++++++++++++++----- include/swift/Demangling/TypeDecoder.h | 2 +- include/swift/Reflection/ReflectionContext.h | 4 +-- include/swift/Reflection/TypeRef.h | 4 +-- include/swift/Remote/MetadataReader.h | 6 ++-- include/swift/Runtime/Debug.h | 4 +-- lib/AST/CMakeLists.txt | 2 ++ lib/ASTSectionImporter/CMakeLists.txt | 2 ++ lib/ClangImporter/CMakeLists.txt | 2 ++ lib/Driver/CMakeLists.txt | 2 ++ lib/Frontend/CMakeLists.txt | 1 + lib/FrontendTool/CMakeLists.txt | 1 + lib/IDE/CMakeLists.txt | 1 + lib/IRGen/CMakeLists.txt | 1 + lib/LLVMPasses/CMakeLists.txt | 1 + lib/Migrator/CMakeLists.txt | 2 ++ lib/Parse/CMakeLists.txt | 2 ++ lib/PrintAsObjC/CMakeLists.txt | 1 + lib/SIL/CMakeLists.txt | 1 + lib/SILGen/CMakeLists.txt | 1 + lib/SILOptimizer/CMakeLists.txt | 1 + lib/Sema/CMakeLists.txt | 1 + lib/Serialization/CMakeLists.txt | 1 + lib/SyntaxParse/CMakeLists.txt | 1 + lib/TBDGen/CMakeLists.txt | 1 + stdlib/public/Reflection/TypeLowering.cpp | 12 ++++---- .../SwiftRemoteMirror/SwiftRemoteMirror.cpp | 4 +-- stdlib/public/runtime/Casting.cpp | 8 +++--- stdlib/public/runtime/DynamicCast.cpp | 6 ++-- stdlib/public/runtime/Exclusivity.cpp | 2 +- stdlib/public/runtime/Metadata.cpp | 26 ++++++++--------- stdlib/public/runtime/MetadataCache.h | 18 ++++++------ stdlib/public/runtime/MetadataLookup.cpp | 8 +++--- stdlib/public/runtime/ProtocolConformance.cpp | 6 ++-- stdlib/public/runtime/ReflectionMirror.cpp | 2 +- .../Compatibility51/ProtocolConformance.cpp | 2 +- 38 files changed, 112 insertions(+), 73 deletions(-) rename include/swift/{Runtime => Basic}/Unreachable.h (53%) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 0ac4ab8943c42..9fbb260dc66ad 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -38,7 +38,7 @@ #include "swift/Basic/RelativePointer.h" #include "swift/Demangling/Demangle.h" #include "swift/Demangling/ManglingMacros.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #include "../../../stdlib/public/SwiftShims/HeapObject.h" #if SWIFT_OBJC_INTEROP #include @@ -4664,7 +4664,7 @@ int32_t TargetTypeContextDescriptor::getGenericArgumentOffset() const { return llvm::cast>(this) ->getGenericArgumentOffset(); default: - swift_runtime_unreachable("Not a type context descriptor."); + swift_unreachable("Not a type context descriptor."); } } @@ -4682,7 +4682,7 @@ TargetTypeContextDescriptor::getFullGenericContextHeader() const { return llvm::cast>(this) ->getFullGenericContextHeader(); default: - swift_runtime_unreachable("Not a type context descriptor."); + swift_unreachable("Not a type context descriptor."); } } @@ -4699,7 +4699,7 @@ TargetTypeContextDescriptor::getGenericParams() const { case ContextDescriptorKind::OpaqueType: return llvm::cast>(this)->getGenericParams(); default: - swift_runtime_unreachable("Not a type context descriptor."); + swift_unreachable("Not a type context descriptor."); } } @@ -4717,7 +4717,7 @@ TargetTypeContextDescriptor::getForeignMetadataInitialization() const { return llvm::cast>(this) ->getForeignMetadataInitialization(); default: - swift_runtime_unreachable("Not a type context descriptor."); + swift_unreachable("Not a type context descriptor."); } } @@ -4735,7 +4735,7 @@ TargetTypeContextDescriptor::getSingletonMetadataInitialization() const return llvm::cast>(this) ->getSingletonMetadataInitialization(); default: - swift_runtime_unreachable("Not a enum, struct or class type descriptor."); + swift_unreachable("Not a enum, struct or class type descriptor."); } } @@ -4753,7 +4753,7 @@ TargetTypeContextDescriptor::getCanonicicalMetadataPrespecializations() return llvm::cast>(this) ->getCanonicicalMetadataPrespecializations(); default: - swift_runtime_unreachable("Not a type context descriptor."); + swift_unreachable("Not a type context descriptor."); } } diff --git a/include/swift/ABI/ProtocolDispatchStrategy.h b/include/swift/ABI/ProtocolDispatchStrategy.h index c5276ee7b541e..eb89c8f138f4a 100644 --- a/include/swift/ABI/ProtocolDispatchStrategy.h +++ b/include/swift/ABI/ProtocolDispatchStrategy.h @@ -19,7 +19,7 @@ #ifndef SWIFT_ABI_PROTOCOLDISPATCHSTRATEGY_H #define SWIFT_ABI_PROTOCOLDISPATCHSTRATEGY_H -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" namespace swift { @@ -46,7 +46,7 @@ inline bool protocolRequiresWitnessTable(ProtocolDispatchStrategy strategy) { return true; } - swift_runtime_unreachable("Unhandled ProtocolDispatchStrategy in switch."); + swift_unreachable("Unhandled ProtocolDispatchStrategy in switch."); } } diff --git a/include/swift/Runtime/Unreachable.h b/include/swift/Basic/Unreachable.h similarity index 53% rename from include/swift/Runtime/Unreachable.h rename to include/swift/Basic/Unreachable.h index 4147d4ce80d23..8d0ed35b5a49d 100644 --- a/include/swift/Runtime/Unreachable.h +++ b/include/swift/Basic/Unreachable.h @@ -1,4 +1,4 @@ -//===--- Unreachable.h - Implements swift_runtime_unreachable ---*- C++ -*-===// +//===--- Unreachable.h - Implements swift_unreachable ---*- C++ -*-===// // // This source file is part of the Swift.org open source project // @@ -10,13 +10,25 @@ // //===----------------------------------------------------------------------===// // -// This file defines swift_runtime_unreachable, an LLVM-independent -// implementation of llvm_unreachable. +// This file defines swift_unreachable, which provides the +// functionality of llvm_unreachable without necessarily depending on +// the LLVM support libraries. // //===----------------------------------------------------------------------===// -#ifndef SWIFT_RUNTIME_UNREACHABLE_H -#define SWIFT_RUNTIME_UNREACHABLE_H +#ifndef SWIFT_BASIC_UNREACHABLE_H +#define SWIFT_BASIC_UNREACHABLE_H + +#ifdef SWIFT_LLVM_SUPPORT_IS_AVAILABLE + +// The implementation when LLVM is available. + +#include "llvm/Support/ErrorHandling.h" +#define swift_unreachable llvm_unreachable + +#else + +// The implementation when LLVM is not available. #include #include @@ -24,10 +36,12 @@ #include "swift/Runtime/Config.h" SWIFT_RUNTIME_ATTRIBUTE_NORETURN -inline static void swift_runtime_unreachable(const char *msg) { +inline static void swift_unreachable(const char *msg) { assert(false && msg); (void)msg; abort(); } -#endif // SWIFT_RUNTIME_UNREACHABLE_H +#endif + +#endif diff --git a/include/swift/Demangling/TypeDecoder.h b/include/swift/Demangling/TypeDecoder.h index fb33edf878252..296076adbf225 100644 --- a/include/swift/Demangling/TypeDecoder.h +++ b/include/swift/Demangling/TypeDecoder.h @@ -24,7 +24,7 @@ #include "swift/Demangling/Demangler.h" #include "swift/Demangling/NamespaceMacros.h" #include "swift/Runtime/Portability.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #include "swift/Strings.h" #include "llvm/ADT/ArrayRef.h" #include diff --git a/include/swift/Reflection/ReflectionContext.h b/include/swift/Reflection/ReflectionContext.h index 9e26669aa8f38..e52185c0b8ebe 100644 --- a/include/swift/Reflection/ReflectionContext.h +++ b/include/swift/Reflection/ReflectionContext.h @@ -33,7 +33,7 @@ #include "swift/Reflection/TypeLowering.h" #include "swift/Reflection/TypeRef.h" #include "swift/Reflection/TypeRefBuilder.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #include #include @@ -1302,7 +1302,7 @@ class ReflectionContext return true; } - swift_runtime_unreachable("Unhandled MetadataSourceKind in switch."); + swift_unreachable("Unhandled MetadataSourceKind in switch."); } /// Read metadata for a captured generic type from a closure context. diff --git a/include/swift/Reflection/TypeRef.h b/include/swift/Reflection/TypeRef.h index fb03cbc9e2e82..3d38e47128f85 100644 --- a/include/swift/Reflection/TypeRef.h +++ b/include/swift/Reflection/TypeRef.h @@ -22,7 +22,7 @@ #include "llvm/Support/Casting.h" #include "swift/ABI/MetadataValues.h" #include "swift/Remote/MetadataReader.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" namespace swift { namespace reflection { @@ -856,7 +856,7 @@ class TypeRefVisitor { #include "swift/Reflection/TypeRefs.def" } - swift_runtime_unreachable("Unhandled TypeRefKind in switch."); + swift_unreachable("Unhandled TypeRefKind in switch."); } }; diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index 0090ae0746ed6..589bef52d8a68 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -28,7 +28,7 @@ #include "swift/ABI/TypeIdentity.h" #include "swift/Runtime/ExistentialContainer.h" #include "swift/Runtime/HeapObject.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #include #include @@ -923,7 +923,7 @@ class MetadataReader { } } - swift_runtime_unreachable("Unhandled MetadataKind in switch"); + swift_unreachable("Unhandled MetadataKind in switch"); } TypeLookupErrorOr @@ -1295,7 +1295,7 @@ class MetadataReader { } } - swift_runtime_unreachable("Unhandled IsaEncodingKind in switch."); + swift_unreachable("Unhandled IsaEncodingKind in switch."); } /// Read the offset of the generic parameters of a class from the nominal diff --git a/include/swift/Runtime/Debug.h b/include/swift/Runtime/Debug.h index f14e8d16f1554..f82fee34bc805 100644 --- a/include/swift/Runtime/Debug.h +++ b/include/swift/Runtime/Debug.h @@ -18,7 +18,7 @@ #define SWIFT_RUNTIME_DEBUG_HELPERS_H #include "swift/Runtime/Config.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #include #include #include @@ -79,7 +79,7 @@ static inline void crash(const char *message) { CRSetCrashLogMessage(message); SWIFT_RUNTIME_BUILTIN_TRAP; - swift_runtime_unreachable("Expected compiler to crash."); + swift_unreachable("Expected compiler to crash."); } // swift::fatalError() halts with a crash log message, diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 6762297a9f098..e9e4ab76ccb30 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -9,6 +9,8 @@ else() ) endif() +set_swift_llvm_is_available() + add_swift_host_library(swiftAST STATIC AbstractSourceFileDepGraphFactory.cpp AccessRequests.cpp diff --git a/lib/ASTSectionImporter/CMakeLists.txt b/lib/ASTSectionImporter/CMakeLists.txt index 0bd9f991fc45e..1a8b262e2a2c0 100644 --- a/lib/ASTSectionImporter/CMakeLists.txt +++ b/lib/ASTSectionImporter/CMakeLists.txt @@ -1,3 +1,5 @@ +set_swift_llvm_is_available() + add_swift_host_library(swiftASTSectionImporter STATIC ASTSectionImporter.cpp LLVM_LINK_COMPONENTS core) diff --git a/lib/ClangImporter/CMakeLists.txt b/lib/ClangImporter/CMakeLists.txt index a9076c210e033..21294ee6da4ef 100644 --- a/lib/ClangImporter/CMakeLists.txt +++ b/lib/ClangImporter/CMakeLists.txt @@ -4,6 +4,8 @@ set(SWIFT_GYB_FLAGS add_gyb_target(generated_sorted_cf_database SortedCFDatabase.def.gyb) +set_swift_llvm_is_available() + add_swift_host_library(swiftClangImporter STATIC CFTypeInfo.cpp ClangAdapter.cpp diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt index 380c3c6dc8a0c..d210da923bce6 100644 --- a/lib/Driver/CMakeLists.txt +++ b/lib/Driver/CMakeLists.txt @@ -1,3 +1,5 @@ +set_swift_llvm_is_available() + set(swiftDriver_sources Action.cpp Compilation.cpp diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt index 08988f9a078ee..2c8c24f9e4dd9 100644 --- a/lib/Frontend/CMakeLists.txt +++ b/lib/Frontend/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftFrontend STATIC ArgsToFrontendInputsConverter.cpp ArgsToFrontendOptionsConverter.cpp diff --git a/lib/FrontendTool/CMakeLists.txt b/lib/FrontendTool/CMakeLists.txt index 0e08c752eb30d..26b37f661b82b 100644 --- a/lib/FrontendTool/CMakeLists.txt +++ b/lib/FrontendTool/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftFrontendTool STATIC FrontendTool.cpp ImportedModules.cpp diff --git a/lib/IDE/CMakeLists.txt b/lib/IDE/CMakeLists.txt index d028e874d39b7..48ebc21981d30 100644 --- a/lib/IDE/CMakeLists.txt +++ b/lib/IDE/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftIDE STATIC CodeCompletion.cpp CodeCompletionCache.cpp diff --git a/lib/IRGen/CMakeLists.txt b/lib/IRGen/CMakeLists.txt index 7cfe61b987e86..777096b39e6e0 100644 --- a/lib/IRGen/CMakeLists.txt +++ b/lib/IRGen/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftIRGen STATIC AllocStackHoisting.cpp ClassLayout.cpp diff --git a/lib/LLVMPasses/CMakeLists.txt b/lib/LLVMPasses/CMakeLists.txt index 80edee61d1236..f5a3ee28b7562 100644 --- a/lib/LLVMPasses/CMakeLists.txt +++ b/lib/LLVMPasses/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftLLVMPasses STATIC LLVMSwiftAA.cpp LLVMSwiftRCIdentity.cpp diff --git a/lib/Migrator/CMakeLists.txt b/lib/Migrator/CMakeLists.txt index 3e37ff2ca48a9..473ad151f0640 100644 --- a/lib/Migrator/CMakeLists.txt +++ b/lib/Migrator/CMakeLists.txt @@ -47,6 +47,8 @@ swift_install_in_component(FILES ${datafiles} DESTINATION "lib/swift/migrator" COMPONENT compiler) +set_swift_llvm_is_available() + add_swift_host_library(swiftMigrator STATIC APIDiffMigratorPass.cpp EditorAdapter.cpp diff --git a/lib/Parse/CMakeLists.txt b/lib/Parse/CMakeLists.txt index 7158bdce8b24e..550af6bf5954a 100644 --- a/lib/Parse/CMakeLists.txt +++ b/lib/Parse/CMakeLists.txt @@ -1,3 +1,5 @@ +set_swift_llvm_is_available() + if(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows) set(SWIFT_GYB_FLAGS --line-directive "^\"#line %(line)d \\\"%(file)s\\\"^\"") else() diff --git a/lib/PrintAsObjC/CMakeLists.txt b/lib/PrintAsObjC/CMakeLists.txt index ae667af64ac31..66044fa3fc7ff 100644 --- a/lib/PrintAsObjC/CMakeLists.txt +++ b/lib/PrintAsObjC/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftPrintAsObjC STATIC DeclAndTypePrinter.cpp ModuleContentsWriter.cpp diff --git a/lib/SIL/CMakeLists.txt b/lib/SIL/CMakeLists.txt index 74ca0507393d3..96a6b852ad8b0 100644 --- a/lib/SIL/CMakeLists.txt +++ b/lib/SIL/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftSIL STATIC SIL.cpp) target_link_libraries(swiftSIL PUBLIC diff --git a/lib/SILGen/CMakeLists.txt b/lib/SILGen/CMakeLists.txt index c144b14bf69ee..c4daab77fd3e6 100644 --- a/lib/SILGen/CMakeLists.txt +++ b/lib/SILGen/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftSILGen STATIC ArgumentSource.cpp Cleanup.cpp diff --git a/lib/SILOptimizer/CMakeLists.txt b/lib/SILOptimizer/CMakeLists.txt index ec1440d2b06a1..c9ddd901ab5dc 100644 --- a/lib/SILOptimizer/CMakeLists.txt +++ b/lib/SILOptimizer/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftSILOptimizer STATIC SILOptimizer.cpp) target_link_libraries(swiftSILOptimizer PRIVATE diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index 633a64b014896..e71cb7ff910bc 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftSema STATIC BuilderTransform.cpp CSApply.cpp diff --git a/lib/Serialization/CMakeLists.txt b/lib/Serialization/CMakeLists.txt index 55a20aef848ea..1e7c14b1ce161 100644 --- a/lib/Serialization/CMakeLists.txt +++ b/lib/Serialization/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftSerialization STATIC Deserialization.cpp DeserializeSIL.cpp diff --git a/lib/SyntaxParse/CMakeLists.txt b/lib/SyntaxParse/CMakeLists.txt index b1b1c562988aa..230f230ebd68f 100644 --- a/lib/SyntaxParse/CMakeLists.txt +++ b/lib/SyntaxParse/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftSyntaxParse STATIC RawSyntaxTokenCache.cpp SyntaxTreeCreator.cpp) diff --git a/lib/TBDGen/CMakeLists.txt b/lib/TBDGen/CMakeLists.txt index 06181211d4a67..60796eab5e73c 100644 --- a/lib/TBDGen/CMakeLists.txt +++ b/lib/TBDGen/CMakeLists.txt @@ -1,3 +1,4 @@ +set_swift_llvm_is_available() add_swift_host_library(swiftTBDGen STATIC TBDGen.cpp TBDGenRequests.cpp diff --git a/stdlib/public/Reflection/TypeLowering.cpp b/stdlib/public/Reflection/TypeLowering.cpp index 729765edd1893..09bc0b6b258b4 100644 --- a/stdlib/public/Reflection/TypeLowering.cpp +++ b/stdlib/public/Reflection/TypeLowering.cpp @@ -23,7 +23,7 @@ #include "swift/Reflection/TypeLowering.h" #include "swift/Reflection/TypeRef.h" #include "swift/Reflection/TypeRefBuilder.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #ifdef DEBUG_TYPE_LOWERING #define DEBUG_LOG(expr) expr; @@ -212,7 +212,7 @@ class PrintTypeInfo { } } - swift_runtime_unreachable("Bad TypeInfo kind"); + swift_unreachable("Bad TypeInfo kind"); } }; @@ -2007,7 +2007,7 @@ class LowerType return nullptr; } - swift_runtime_unreachable("Unhandled FieldDescriptorKind in switch."); + swift_unreachable("Unhandled FieldDescriptorKind in switch."); } const TypeInfo *visitNominalTypeRef(const NominalTypeRef *N) { @@ -2039,7 +2039,7 @@ class LowerType return TC.getTypeInfo(TC.getThinFunctionTypeRef(), ExternalTypeInfo); } - swift_runtime_unreachable("Unhandled FunctionMetadataConvention in switch."); + swift_unreachable("Unhandled FunctionMetadataConvention in switch."); } const TypeInfo * @@ -2060,7 +2060,7 @@ class LowerType return TC.getTypeInfo(TC.getAnyMetatypeTypeRef(), ExternalTypeInfo); } - swift_runtime_unreachable("Unhandled MetatypeRepresentation in switch."); + swift_unreachable("Unhandled MetatypeRepresentation in switch."); } const TypeInfo * @@ -2256,7 +2256,7 @@ const TypeInfo *TypeConverter::getClassInstanceTypeInfo( return nullptr; } - swift_runtime_unreachable("Unhandled FieldDescriptorKind in switch."); + swift_unreachable("Unhandled FieldDescriptorKind in switch."); } } // namespace reflection diff --git a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp index 9d5afad1cf41f..0af1e53b3f9d7 100644 --- a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp +++ b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp @@ -23,7 +23,7 @@ unsigned long long swift_reflection_classIsSwiftMask = 2; #include "swift/Reflection/ReflectionContext.h" #include "swift/Reflection/TypeLowering.h" #include "swift/Remote/CMemoryReader.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #if defined(__APPLE__) && defined(__MACH__) #include @@ -406,7 +406,7 @@ swift_layout_kind_t getTypeInfoKind(const TypeInfo &TI) { } } - swift_runtime_unreachable("Unhandled TypeInfoKind in switch"); + swift_unreachable("Unhandled TypeInfoKind in switch"); } static swift_typeinfo_t convertTypeInfo(const TypeInfo *TI) { diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index 99e08243588bb..487bc05fb871a 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -35,7 +35,7 @@ # define SWIFT_CASTING_SUPPORTS_MUTEX 1 # include "swift/Runtime/Mutex.h" #endif -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerIntPair.h" #if SWIFT_OBJC_INTEROP @@ -1016,7 +1016,7 @@ static bool _dynamicCastToExistential(OpaqueValue *dest, } } - swift_runtime_unreachable("Unhandled ExistentialTypeRepresentation in switch."); + swift_unreachable("Unhandled ExistentialTypeRepresentation in switch."); } /******************************************************************************/ @@ -1214,7 +1214,7 @@ swift_dynamicCastMetatypeImpl(const Metadata *sourceType, return nullptr; } - swift_runtime_unreachable("Unhandled MetadataKind in switch."); + swift_unreachable("Unhandled MetadataKind in switch."); } static const Metadata * @@ -1424,7 +1424,7 @@ static bool _dynamicCastToUnknownClassFromExistential(OpaqueValue *dest, } } - swift_runtime_unreachable( + swift_unreachable( "Unhandled ExistentialTypeRepresentation in switch."); } diff --git a/stdlib/public/runtime/DynamicCast.cpp b/stdlib/public/runtime/DynamicCast.cpp index f19af13ab6311..e7f624ba8c555 100644 --- a/stdlib/public/runtime/DynamicCast.cpp +++ b/stdlib/public/runtime/DynamicCast.cpp @@ -1806,7 +1806,7 @@ static tryCastFunctionType *selectCasterForDest(const Metadata *destType) { case ExistentialTypeRepresentation::Error: // => Error existential return tryCastToErrorExistential; } - swift_runtime_unreachable( + swift_unreachable( "Unknown existential type representation in dynamic cast dispatch"); } case MetadataKind::Metatype: @@ -1821,14 +1821,14 @@ static tryCastFunctionType *selectCasterForDest(const Metadata *destType) { // These are internal details of runtime-only structures, // so will never appear in compiler-generated types. // As such, they don't need support here. - swift_runtime_unreachable( + swift_unreachable( "Unexpected MetadataKind in dynamic cast dispatch"); return nullptr; default: // If you see this message, then there is a new MetadataKind that I didn't // know about when I wrote this code. Please figure out what it is, how to // handle it, and add a case for it. - swift_runtime_unreachable( + swift_unreachable( "Unknown MetadataKind in dynamic cast dispatch"); } } diff --git a/stdlib/public/runtime/Exclusivity.cpp b/stdlib/public/runtime/Exclusivity.cpp index 73628b7cdaae4..7750fbae41e8b 100644 --- a/stdlib/public/runtime/Exclusivity.cpp +++ b/stdlib/public/runtime/Exclusivity.cpp @@ -219,7 +219,7 @@ class AccessSet { } } - swift_runtime_unreachable("access not found in set"); + swift_unreachable("access not found in set"); } #ifndef NDEBUG diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 56fd8670c640f..5fc6dafd8baac 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -194,7 +194,7 @@ computeMetadataBoundsForSuperclass(const void *ref, #endif } } - swift_runtime_unreachable("unsupported superclass reference kind"); + swift_unreachable("unsupported superclass reference kind"); } static ClassMetadataBounds computeMetadataBoundsFromSuperclass( @@ -3716,7 +3716,7 @@ getExistentialValueWitnesses(ProtocolClassConstraint classConstraint, return getOpaqueExistentialValueWitnesses(numWitnessTables); } - swift_runtime_unreachable("Unhandled ProtocolClassConstraint in switch."); + swift_unreachable("Unhandled ProtocolClassConstraint in switch."); } template<> ExistentialTypeRepresentation @@ -3761,7 +3761,7 @@ ExistentialTypeMetadata::mayTakeValue(const OpaqueValue *container) const { } } - swift_runtime_unreachable( + swift_unreachable( "Unhandled ExistentialTypeRepresentation in switch."); } @@ -3810,7 +3810,7 @@ ExistentialTypeMetadata::projectValue(const OpaqueValue *container) const { } } - swift_runtime_unreachable( + swift_unreachable( "Unhandled ExistentialTypeRepresentation in switch."); } @@ -3835,7 +3835,7 @@ ExistentialTypeMetadata::getDynamicType(const OpaqueValue *container) const { } } - swift_runtime_unreachable( + swift_unreachable( "Unhandled ExistentialTypeRepresentation in switch."); } @@ -4030,7 +4030,7 @@ class ForeignMetadataCacheEntry return Value; } void setValue(ValueType value) { - swift_runtime_unreachable("should never be called"); + swift_unreachable("should never be called"); } public: @@ -4094,7 +4094,7 @@ class ForeignMetadataCacheEntry } AllocationResult allocate(Metadata *candidate) { - swift_runtime_unreachable( + swift_unreachable( "always flags allocation complete during construction"); } @@ -4555,7 +4555,7 @@ static void initProtocolWitness(void **slot, void *witness, reqt); return; } - swift_runtime_unreachable("bad witness kind"); + swift_unreachable("bad witness kind"); #else *slot = witness; #endif @@ -4588,7 +4588,7 @@ static void copyProtocolWitness(void **dest, void * const *src, swift_ptrauth_copy(dest, src, reqt.Flags.getExtraDiscriminator()); return; } - swift_runtime_unreachable("bad witness kind"); + swift_unreachable("bad witness kind"); #else *dest = *src; #endif @@ -5141,7 +5141,7 @@ static const WitnessTable *swift_getAssociatedConformanceWitnessSlowImpl( return assocWitnessTable; } - swift_runtime_unreachable("Invalid mangled associate conformance"); + swift_unreachable("Invalid mangled associate conformance"); } const WitnessTable *swift::swift_getAssociatedConformanceWitness( @@ -5234,7 +5234,7 @@ static Result performOnMetadataCache(const Metadata *metadata, case TypeContextDescriptorFlags::SingletonMetadataInitialization: return std::move(callbacks).forSingletonMetadata(description); } - swift_runtime_unreachable("bad metadata initialization kind"); + swift_unreachable("bad metadata initialization kind"); } auto &generics = description->getFullGenericContextHeader(); @@ -5280,7 +5280,7 @@ bool swift::addToMetadataQueue(MetadataCompletionQueueEntry *queueEntry, } bool forOtherMetadata(const Metadata *metadata) && { - swift_runtime_unreachable("metadata should always be complete"); + swift_unreachable("metadata should always be complete"); } } callbacks = { queueEntry, dependency }; @@ -5312,7 +5312,7 @@ void swift::resumeMetadataCompletion(MetadataCompletionQueueEntry *queueEntry) { } void forOtherMetadata(const Metadata *metadata) && { - swift_runtime_unreachable("metadata should always be complete"); + swift_unreachable("metadata should always be complete"); } } callbacks = { queueEntry }; diff --git a/stdlib/public/runtime/MetadataCache.h b/stdlib/public/runtime/MetadataCache.h index d6f188f971e3c..0c9a186f8b574 100644 --- a/stdlib/public/runtime/MetadataCache.h +++ b/stdlib/public/runtime/MetadataCache.h @@ -366,7 +366,7 @@ class SimpleLockingCacheEntryBase { template Status beginInitialization(ConcurrencyControl &concurrency, ArgTys &&...args) { - swift_runtime_unreachable("beginAllocation always short-circuits"); + swift_unreachable("beginAllocation always short-circuits"); } }; @@ -592,7 +592,7 @@ inline bool satisfies(PrivateMetadataState state, MetadataState requirement) { case MetadataState::Complete: return state >= PrivateMetadataState::Complete; } - swift_runtime_unreachable("unsupported requirement kind"); + swift_unreachable("unsupported requirement kind"); } class PrivateMetadataTrackingInfo { @@ -642,7 +642,7 @@ class PrivateMetadataTrackingInfo { MetadataState getAccomplishedRequestState() const { switch (getState()) { case PrivateMetadataState::Allocating: - swift_runtime_unreachable("cannot call on allocating state"); + swift_unreachable("cannot call on allocating state"); case PrivateMetadataState::Abstract: return MetadataState::Abstract; case PrivateMetadataState::LayoutComplete: @@ -652,7 +652,7 @@ class PrivateMetadataTrackingInfo { case PrivateMetadataState::Complete: return MetadataState::Complete; } - swift_runtime_unreachable("bad state"); + swift_unreachable("bad state"); } bool satisfies(MetadataState requirement) { @@ -678,7 +678,7 @@ class PrivateMetadataTrackingInfo { // Otherwise, if it's a non-blocking request, we do not need to block. return (request.isBlocking() && !satisfies(request.getState())); } - swift_runtime_unreachable("bad state"); + swift_unreachable("bad state"); } constexpr RawType getRawValue() const { return Data; } @@ -1124,9 +1124,9 @@ class MetadataCacheEntryBase return; case LSK::Complete: - swift_runtime_unreachable("preparing to enqueue when already complete?"); + swift_unreachable("preparing to enqueue when already complete?"); } - swift_runtime_unreachable("bad kind"); + swift_unreachable("bad kind"); } /// Claim all the satisfied completion queue entries, given that @@ -1288,7 +1288,7 @@ class MetadataCacheEntryBase switch (LockedStorageKind) { case LSK::Complete: - swift_runtime_unreachable("enqueuing on complete cache entry?"); + swift_unreachable("enqueuing on complete cache entry?"); case LSK::AllocatingThread: LockedStorageKind = LSK::CompletionQueue; @@ -1340,7 +1340,7 @@ class MetadataCacheEntryBase // Check for an existing dependency. switch (LockedStorageKind) { case LSK::Complete: - swift_runtime_unreachable("dependency on complete cache entry?"); + swift_unreachable("dependency on complete cache entry?"); case LSK::AllocatingThread: case LSK::CompletionQueue: diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index fef3ff3f82e76..f271908ee1d50 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -88,10 +88,10 @@ static uintptr_t resolveSymbolicReferenceOffset(SymbolicReferenceKind kind, return (uintptr_t)contextPtr; } case SymbolicReferenceKind::AccessorFunctionReference: { - swift_runtime_unreachable("should not be indirectly referenced"); + swift_unreachable("should not be indirectly referenced"); } } - swift_runtime_unreachable("unknown symbolic reference kind"); + swift_unreachable("unknown symbolic reference kind"); } else { return ptr; } @@ -176,7 +176,7 @@ _buildDemanglingForSymbolicReference(SymbolicReferenceKind kind, (uintptr_t)resolvedReference); } - swift_runtime_unreachable("invalid symbolic reference kind"); + swift_unreachable("invalid symbolic reference kind"); } NodePointer @@ -1168,7 +1168,7 @@ findAssociatedTypeByName(const ProtocolDescriptor *protocol, StringRef name) { ++currentAssocTypeIdx; } - swift_runtime_unreachable("associated type names don't line up"); + swift_unreachable("associated type names don't line up"); } namespace { diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index c434011faea85..f3fa79c578a72 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -20,7 +20,7 @@ #include "swift/Runtime/Concurrent.h" #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Metadata.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #include "CompatibilityOverride.h" #include "ImageInspection.h" #include "Private.h" @@ -116,7 +116,7 @@ const ClassMetadata *TypeReference::getObjCClass(TypeReferenceKind kind) const { return nullptr; } - swift_runtime_unreachable("Unhandled TypeReferenceKind in switch."); + swift_unreachable("Unhandled TypeReferenceKind in switch."); } #endif @@ -155,7 +155,7 @@ ProtocolConformanceDescriptor::getCanonicalTypeMetadata() const { } } - swift_runtime_unreachable("Unhandled TypeReferenceKind in switch."); + swift_unreachable("Unhandled TypeReferenceKind in switch."); } template<> diff --git a/stdlib/public/runtime/ReflectionMirror.cpp b/stdlib/public/runtime/ReflectionMirror.cpp index 5fbad4fb3c136..ca7d6601c4cec 100644 --- a/stdlib/public/runtime/ReflectionMirror.cpp +++ b/stdlib/public/runtime/ReflectionMirror.cpp @@ -17,7 +17,7 @@ #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Metadata.h" #include "swift/Runtime/Enum.h" -#include "swift/Runtime/Unreachable.h" +#include "swift/Basic/Unreachable.h" #include "swift/Demangling/Demangle.h" #include "swift/Runtime/Debug.h" #include "swift/Runtime/Portability.h" diff --git a/stdlib/toolchain/Compatibility51/ProtocolConformance.cpp b/stdlib/toolchain/Compatibility51/ProtocolConformance.cpp index 4f22fd2bfda2d..09bd46ea02700 100644 --- a/stdlib/toolchain/Compatibility51/ProtocolConformance.cpp +++ b/stdlib/toolchain/Compatibility51/ProtocolConformance.cpp @@ -231,7 +231,7 @@ override_getCanonicalTypeMetadata(const ProtocolConformanceDescriptor *conf) { } } - swift_runtime_unreachable("Unhandled TypeReferenceKind in switch."); + swift_unreachable("Unhandled TypeReferenceKind in switch."); } class ConformanceCandidate { From f7576a7bef5ca33c5598ad6b63a37f6d4c69aa98 Mon Sep 17 00:00:00 2001 From: Butta Date: Fri, 2 Oct 2020 14:03:49 +0530 Subject: [PATCH 197/745] [android] Add support for building the toolchain for ARMv7 --- cmake/modules/AddSwift.cmake | 10 ++++++++++ lib/ClangImporter/ImportType.cpp | 2 +- stdlib/public/runtime/Float16Support.cpp | 2 +- utils/build-script-impl | 4 ++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index ec61d2abb836a..0b9ce10842ac2 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -291,6 +291,16 @@ function(_add_host_variant_c_compile_flags target) target_compile_options(${target} PRIVATE -march=core2) endif() endif() + + # The LLVM backend is built with these defines on most 32-bit platforms, + # llvm/llvm-project@66395c9, which can cause incompatibilities with the Swift + # frontend if not built the same way. + if("${SWIFT_HOST_VARIANT_ARCH}" MATCHES "armv6|armv7|i686" AND + NOT (SWIFT_HOST_VARIANT_SDK STREQUAL ANDROID AND SWIFT_ANDROID_API_LEVEL LESS 24)) + target_compile_definitions(${target} PRIVATE + _LARGEFILE_SOURCE + _FILE_OFFSET_BITS=64) + endif() endfunction() function(_add_host_variant_link_flags target) diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index f58dbb837e5dd..70bd70dff9ac6 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -556,7 +556,7 @@ namespace { if (size > 4096) return Type(); - SmallVector elts{size, elementType}; + SmallVector elts{static_cast(size), elementType}; return TupleType::get(elts, elementType->getASTContext()); } diff --git a/stdlib/public/runtime/Float16Support.cpp b/stdlib/public/runtime/Float16Support.cpp index d7377400ba0be..9d8c4940054df 100644 --- a/stdlib/public/runtime/Float16Support.cpp +++ b/stdlib/public/runtime/Float16Support.cpp @@ -29,7 +29,7 @@ // Android NDK Date: Thu, 1 Oct 2020 23:21:05 -0400 Subject: [PATCH 198/745] Sema: Diagnose duplicate capture list entires when parser lookup is off Another spot where we have to check for redeclaration manually now. As before, we can use the existing mechanism though. --- lib/Sema/CSGen.cpp | 2 ++ lib/Sema/TypeCheckDeclPrimary.cpp | 8 ++++++++ lib/Sema/TypeChecker.h | 2 ++ 3 files changed, 12 insertions(+) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 751d893323e44..5d15df3ada4f4 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3440,6 +3440,8 @@ namespace { // Generate constraints for each of the entries in the capture list. if (auto captureList = dyn_cast(expr)) { + TypeChecker::diagnoseDuplicateCaptureVars(captureList); + auto &CS = CG.getConstraintSystem(); for (const auto &capture : captureList->getCaptureList()) { SolutionApplicationTarget target(capture.Init); diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 5092bf7c99b92..9e44995acd561 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1334,6 +1334,14 @@ void TypeChecker::diagnoseDuplicateBoundVars(Pattern *pattern) { diagnoseDuplicateDecls(boundVars); } +void TypeChecker::diagnoseDuplicateCaptureVars(CaptureListExpr *expr) { + SmallVector captureListVars; + for (auto &capture : expr->getCaptureList()) + captureListVars.push_back(capture.Var); + + diagnoseDuplicateDecls(captureListVars); +} + namespace { class DeclChecker : public DeclVisitor { public: diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index b30e9810a614f..6d21f393431d5 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -502,6 +502,8 @@ void checkParameterList(ParameterList *params); void diagnoseDuplicateBoundVars(Pattern *pattern); +void diagnoseDuplicateCaptureVars(CaptureListExpr *expr); + Type checkReferenceOwnershipAttr(VarDecl *D, Type interfaceType, ReferenceOwnershipAttr *attr); From 854e1e482f7544e0fa0a26101dac8eb7df3ebc85 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 23 Sep 2020 17:35:36 -0400 Subject: [PATCH 199/745] Frontend: Add -enable-parser-lookup flag This is for re-enabling it once it is turned off by default. --- include/swift/Option/Options.td | 6 +++++- lib/Driver/ToolChains.cpp | 1 + lib/Frontend/CompilerInvocation.cpp | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 7c80c7c03e68e..3c412e153c7dc 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -1102,7 +1102,11 @@ def scan_clang_dependencies : Flag<["-"], "scan-clang-dependencies">, def disable_parser_lookup : Flag<["-"], "disable-parser-lookup">, Flags<[FrontendOption]>, - HelpText<"Disable parser lookup & use ast scope lookup only (experimental)">; + HelpText<"Disable parser lookup & use ASTScope lookup only (experimental)">; + +def enable_parser_lookup : Flag<["-"], "enable-parser-lookup">, + Flags<[FrontendOption]>, + HelpText<"Enable parser lookup">; def enable_request_based_incremental_dependencies : Flag<["-"], "enable-request-based-incremental-dependencies">, diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 2ff01dd73b4fa..b3dce353410b2 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -269,6 +269,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_print_educational_notes); inputArgs.AddLastArg(arguments, options::OPT_diagnostic_style); inputArgs.AddLastArg(arguments, options::OPT_disable_parser_lookup); + inputArgs.AddLastArg(arguments, options::OPT_enable_parser_lookup); inputArgs.AddLastArg(arguments, options::OPT_enable_experimental_concise_pound_file); inputArgs.AddLastArg( diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 3d0f6a6180bb1..d02bc2222b758 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -425,7 +425,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, = A->getOption().matches(OPT_enable_target_os_checking); } - Opts.DisableParserLookup |= Args.hasArg(OPT_disable_parser_lookup); + Opts.DisableParserLookup |= Args.hasFlag(OPT_disable_parser_lookup, + OPT_enable_parser_lookup, + /*default*/ false); Opts.EnableNewOperatorLookup = Args.hasFlag(OPT_enable_new_operator_lookup, OPT_disable_new_operator_lookup, /*default*/ false); From bd36100cb3bb10e448991d7aac061e8984bc858b Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 1 Oct 2020 23:43:33 -0400 Subject: [PATCH 200/745] Update tests in preparation for disabling parser lookup I created a second copy of each test where the output changes after disabling parser lookup. The primary copy now explicitly calls the frontend with -disable-parser-lookup and expects the new diagnostics; the *_parser_lookup.swift version calls the frontend with -enable-parser-lookup and has the old expectations. This allows us to turn parser lookup on and off by default without disturbing tests. Once parser lookup is completely removed we can remove the *_parser_lookup.swift variants. --- test/Constraints/tuple.swift | 4 +- test/Constraints/tuple_parser_lookup.swift | 337 +++++++ test/Parse/matching_patterns.swift | 4 +- .../matching_patterns_parser_lookup.swift | 332 ++++++ test/Parse/switch.swift | 4 +- test/Parse/switch_parser_lookup.swift | 647 ++++++++++++ test/Sema/diag_variable_used_in_initial.swift | 32 +- ...riable_used_in_initial_parser_lookup.swift | 56 ++ test/decl/circularity.swift | 5 +- test/decl/circularity_parser_lookup.swift | 114 +++ test/decl/typealias/generic.swift | 6 +- .../typealias/generic_parser_lookup.swift | 443 ++++++++ test/decl/var/variables.swift | 31 +- test/decl/var/variables_parser_lookup.swift | 121 +++ .../Localization/en_localization.swift | 7 +- .../en_localization_parser_lookup.swift | 9 + .../Localization/fr_localization.swift | 9 +- .../fr_localization_parser_lookup.swift | 13 + ...no_localization_files_and_wrong_path.swift | 7 +- ...n_files_and_wrong_path_parser_lookup.swift | 11 + test/expr/closure/closures.swift | 13 +- .../expr/closure/closures_parser_lookup.swift | 526 ++++++++++ test/expr/expressions.swift | 7 +- test/expr/expressions_parser_lookup.swift | 947 ++++++++++++++++++ test/stmt/statements.swift | 8 +- test/stmt/statements_parser_lookup.swift | 723 +++++++++++++ .../0180-rdar45557325-parser-lookup.swift | 11 + .../0180-rdar45557325.swift | 5 +- 28 files changed, 4381 insertions(+), 51 deletions(-) create mode 100644 test/Constraints/tuple_parser_lookup.swift create mode 100644 test/Parse/matching_patterns_parser_lookup.swift create mode 100644 test/Parse/switch_parser_lookup.swift create mode 100644 test/Sema/diag_variable_used_in_initial_parser_lookup.swift create mode 100644 test/decl/circularity_parser_lookup.swift create mode 100644 test/decl/typealias/generic_parser_lookup.swift create mode 100644 test/decl/var/variables_parser_lookup.swift create mode 100644 test/diagnostics/Localization/en_localization_parser_lookup.swift create mode 100644 test/diagnostics/Localization/fr_localization_parser_lookup.swift create mode 100644 test/diagnostics/Localization/no_localization_files_and_wrong_path_parser_lookup.swift create mode 100644 test/expr/closure/closures_parser_lookup.swift create mode 100644 test/expr/expressions_parser_lookup.swift create mode 100644 test/stmt/statements_parser_lookup.swift create mode 100644 validation-test/compiler_crashers_2_fixed/0180-rdar45557325-parser-lookup.swift diff --git a/test/Constraints/tuple.swift b/test/Constraints/tuple.swift index ec557012b835a..2d9a1d7d2742e 100644 --- a/test/Constraints/tuple.swift +++ b/test/Constraints/tuple.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-parser-lookup // Test various tuple constraints. @@ -305,6 +305,8 @@ if case (foo: let x, foo: let y) = zeroTuple { print(x+y) } // expected-error {{ // expected-warning@-1 {{'if' condition is always true}} enum BishBash { case bar(foo: Int, foo: String) } +// expected-error@-1 {{invalid redeclaration of 'foo'}} +// expected-note@-2 {{'foo' previously declared here}} let enumLabelDup: BishBash = .bar(foo: 0, foo: "") // expected-error {{cannot create a tuple with a duplicate element label}} func dupLabelClosure(_ fn: () -> Void) {} diff --git a/test/Constraints/tuple_parser_lookup.swift b/test/Constraints/tuple_parser_lookup.swift new file mode 100644 index 0000000000000..a3acaf4f356e4 --- /dev/null +++ b/test/Constraints/tuple_parser_lookup.swift @@ -0,0 +1,337 @@ +// RUN: %target-typecheck-verify-swift -enable-parser-lookup + +// Test various tuple constraints. + +func f0(x: Int, y: Float) {} + +var i : Int +var j : Int +var f : Float + +func f1(y: Float, rest: Int...) {} + +func f2(_: (_ x: Int, _ y: Int) -> Int) {} +func f2xy(x: Int, y: Int) -> Int {} +func f2ab(a: Int, b: Int) -> Int {} +func f2yx(y: Int, x: Int) -> Int {} + +func f3(_ x: (_ x: Int, _ y: Int) -> ()) {} +func f3a(_ x: Int, y: Int) {} +func f3b(_: Int) {} + +func f4(_ rest: Int...) {} +func f5(_ x: (Int, Int)) {} + +func f6(_: (i: Int, j: Int), k: Int = 15) {} + +//===----------------------------------------------------------------------===// +// Conversions and shuffles +//===----------------------------------------------------------------------===// + +func foo(a : [(some: Int, (key: Int, value: String))]) -> String { + for (i , (j, k)) in a { + if i == j { return k } + } +} + +func rdar28207648() -> [(Int, CustomStringConvertible)] { + let v : [(Int, Int)] = [] + return v as [(Int, CustomStringConvertible)] +} + +class rdar28207648Base {} +class rdar28207648Derived : rdar28207648Base {} + +func rdar28207648(x: (Int, rdar28207648Derived)) -> (Int, rdar28207648Base) { + return x as (Int, rdar28207648Base) +} + +public typealias Success = (response: T, data: V?) + +public enum Result { + case success(Success) + case error(Error) +} + + +let a = Success(response: 3, data: 3) +let success: Result = .success(a) + +// Variadic functions. +f4() +f4(1) +f4(1, 2, 3) + +f2(f2xy) +f2(f2ab) +f2(f2yx) + +f3(f3a) +f3(f3b) // expected-error{{cannot convert value of type '(Int) -> ()' to expected argument type '(Int, Int) -> ()'}} + +func getIntFloat() -> (int: Int, float: Float) {} +var values = getIntFloat() +func wantFloat(_: Float) {} +wantFloat(values.float) + +var e : (x: Int..., y: Int) // expected-error{{cannot create a variadic tuple}} + +typealias Interval = (a:Int, b:Int) +func takeInterval(_ x: Interval) {} +takeInterval(Interval(1, 2)) + +f5((1,1)) + +// Tuples with existentials +var any : Any = () +any = (1, 2) +any = (label: 4) // expected-error {{cannot create a single-element tuple with an element label}} + +// Scalars don't have .0/.1/etc +i = j.0 // expected-error{{value of type 'Int' has no member '0'}} +any.1 // expected-error{{value of type 'Any' has no member '1'}} +// expected-note@-1{{cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members}} +any = (5.0, 6.0) as (Float, Float) +_ = (any as! (Float, Float)).1 + +// Fun with tuples +protocol PosixErrorReturn { + static func errorReturnValue() -> Self +} + +extension Int : PosixErrorReturn { + static func errorReturnValue() -> Int { return -1 } +} + +func posixCantFail + (_ f: @escaping (A) -> T) -> (_ args:A) -> T +{ + return { args in + let result = f(args) + assert(result != T.errorReturnValue()) + return result + } +} + +func open(_ name: String, oflag: Int) -> Int { } + +var foo: Int = 0 + +var fd = posixCantFail(open)(("foo", 0)) + +// Tuples and lvalues +class C { + init() {} + func f(_: C) {} +} + +func testLValue(_ c: C) { + var c = c + c.f(c) + + let x = c + c = x +} + + +// Crash in TypeChecker::coercePatternToType +func invalidPatternCrash(_ k : Int) { + switch k { + case (k, cph_: k) as UInt8: // expected-error {{tuple pattern cannot match values of the non-tuple type 'UInt8'}} expected-warning {{cast from 'Int' to unrelated type 'UInt8' always fails}} + break + } +} + +// Tuple to tuple conversion with IdentityExpr / AnyTryExpr hang +class Paws { + init() throws {} +} + +func scruff() -> (AnyObject?, Error?) { + do { + return try (Paws(), nil) + } catch { + return (nil, error) + } +} + +// Test variadics with trailing closures. +func variadicWithTrailingClosure(_ x: Int..., y: Int = 2, fn: (Int, Int) -> Int) { +} + +variadicWithTrailingClosure(1, 2, 3) { $0 + $1 } +variadicWithTrailingClosure(1) { $0 + $1 } +variadicWithTrailingClosure() { $0 + $1 } +variadicWithTrailingClosure { $0 + $1 } + +variadicWithTrailingClosure(1, 2, 3, y: 0) { $0 + $1 } +variadicWithTrailingClosure(1, y: 0) { $0 + $1 } +variadicWithTrailingClosure(y: 0) { $0 + $1 } + +variadicWithTrailingClosure(1, 2, 3, y: 0, fn: +) +variadicWithTrailingClosure(1, y: 0, fn: +) +variadicWithTrailingClosure(y: 0, fn: +) + +variadicWithTrailingClosure(1, 2, 3, fn: +) +variadicWithTrailingClosure(1, fn: +) +variadicWithTrailingClosure(fn: +) + + +// QoI: Terrible diagnostic in tuple assignment +func gcd_23700031(_ a: T, b: T) { + var a = a + var b = b + (a, b) = (b, a % b) // expected-error {{binary operator '%' cannot be applied to two 'T' operands}} +} + +// +// Don't ignore tuple labels in same-type constraints or stronger. +protocol Kingdom { + associatedtype King +} +struct Victory { + init(_ king: K) where K.King == General {} // expected-note {{where 'General' = '(x: Int, y: Int)', 'K.King' = 'MagicKingdom<(Int, Int)>.King' (aka '(Int, Int)')}} +} +struct MagicKingdom : Kingdom { + typealias King = K +} +func magify(_ t: T) -> MagicKingdom { return MagicKingdom() } +func foo(_ pair: (Int, Int)) -> Victory<(x: Int, y: Int)> { + return Victory(magify(pair)) // expected-error {{initializer 'init(_:)' requires the types '(x: Int, y: Int)' and 'MagicKingdom<(Int, Int)>.King' (aka '(Int, Int)') be equivalent}} +} + + +// https://bugs.swift.org/browse/SR-596 +// Compiler crashes when accessing a non-existent property of a closure parameter +func call(_ f: (C) -> Void) {} +func makeRequest() { + call { obj in + print(obj.invalidProperty) // expected-error {{value of type 'C' has no member 'invalidProperty'}} + } +} + +// QoI: Misleading error message when expression result can't be inferred from closure +struct r25271859 { +} + +extension r25271859 { + func map(f: (T) -> U) -> r25271859 { + } + + func andThen(f: (T) -> r25271859) { // expected-note {{in call to function 'andThen(f:)'}} + } +} + +func f(a : r25271859<(Float, Int)>) { + a.map { $0.0 } // expected-error {{generic parameter 'U' could not be inferred}} (This is related to how solver is setup with multiple statements) + .andThen { _ in + print("hello") // comment this out and it runs, leave any form of print in and it doesn't + return r25271859() + } +} + +// LValue to rvalue conversions. + +func takesRValue(_: (Int, (Int, Int))) {} +func takesAny(_: Any) {} + +var x = 0 +var y = 0 + +let _ = (x, (y, 0)) +takesRValue((x, (y, 0))) +takesAny((x, (y, 0))) + +// SR-2600 - Closure cannot infer tuple parameter names +typealias Closure = ((a: A, b: B)) -> String + +func invoke(a: A, b: B, _ closure: Closure) { + print(closure((a, b))) +} + +invoke(a: 1, b: "B") { $0.b } + +invoke(a: 1, b: "B") { $0.1 } + +invoke(a: 1, b: "B") { (c: (a: Int, b: String)) in + return c.b +} + +invoke(a: 1, b: "B") { c in + return c.b +} + +// Crash with one-element tuple with labeled element +class Dinner {} + +func microwave() -> Dinner? { + let d: Dinner? = nil + return (n: d) // expected-error{{cannot convert return expression of type '(n: Dinner?)' to return type 'Dinner?'}} +} + +func microwave() -> Dinner { + let d: Dinner? = nil + return (n: d) // expected-error{{cannot convert return expression of type '(n: Dinner?)' to return type 'Dinner'}} +} + +// Tuple conversion with an optional +func f(b: Bool) -> (a: Int, b: String)? { + let x = 3 + let y = "" + return b ? (x, y) : nil +} + +// Single element tuple expressions +func singleElementTuple() { + let _ = (label: 123) // expected-error {{cannot create a single-element tuple with an element label}} {{12-19=}} + let _ = (label: 123).label // expected-error {{cannot create a single-element tuple with an element label}} {{12-19=}} + let _ = ((label: 123)) // expected-error {{cannot create a single-element tuple with an element label}} {{13-20=}} + let _ = ((label: 123)).label // expected-error {{cannot create a single-element tuple with an element label}} {{13-20=}} +} + +// Tuples with duplicate labels + +let dupLabel1: (foo: Int, foo: Int) = (foo: 1, foo: 2) // expected-error 2{{cannot create a tuple with a duplicate element label}} + +func dupLabel2(x a: Int, x b: Int) -> (y: Int, y: Int) { // expected-error {{cannot create a tuple with a duplicate element label}} + return (a, b) +} + +let _ = (bar: 0, bar: "") // expected-error {{cannot create a tuple with a duplicate element label}} + +let zeroTuple = (0,0) + +if case (foo: let x, foo: let y) = zeroTuple { print(x+y) } // expected-error {{cannot create a tuple with a duplicate element label}} +// expected-warning@-1 {{'if' condition is always true}} + +enum BishBash { case bar(foo: Int, foo: String) } +let enumLabelDup: BishBash = .bar(foo: 0, foo: "") // expected-error {{cannot create a tuple with a duplicate element label}} + +func dupLabelClosure(_ fn: () -> Void) {} +dupLabelClosure { print((bar: "", bar: 5).bar) } // expected-error {{cannot create a tuple with a duplicate element label}} + +struct DupLabelSubscript { + subscript(foo x: Int, foo y: Int) -> Int { + return 0 + } +} + +let dupLabelSubscriptStruct = DupLabelSubscript() +let _ = dupLabelSubscriptStruct[foo: 5, foo: 5] // ok + +// SR-12869 + +var dict: [String: (Int, Int)] = [:] +let bignum: Int64 = 1337 +dict["test"] = (bignum, 1) // expected-error {{cannot assign value of type '(Int64, Int)' to subscript of type '(Int, Int)'}} + +var tuple: (Int, Int) +tuple = (bignum, 1) // expected-error {{cannot assign value of type '(Int64, Int)' to type '(Int, Int)'}} + +var optionalTuple: (Int, Int)? +var optionalTuple2: (Int64, Int)? = (bignum, 1) +var optionalTuple3: (UInt64, Int)? = (bignum, 1) // expected-error {{cannot convert value of type '(Int64, Int)' to specified type '(UInt64, Int)?'}} + +optionalTuple = (bignum, 1) // expected-error {{cannot assign value of type '(Int64, Int)' to type '(Int, Int)'}} +// Optional to Optional +optionalTuple = optionalTuple2 // expected-error {{cannot assign value of type '(Int64, Int)?' to type '(Int, Int)?'}} diff --git a/test/Parse/matching_patterns.swift b/test/Parse/matching_patterns.swift index 1d32472a264bc..abaf38e7aa06d 100644 --- a/test/Parse/matching_patterns.swift +++ b/test/Parse/matching_patterns.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -swift-version 4 -I %S/Inputs -enable-source-import +// RUN: %target-typecheck-verify-swift -swift-version 4 -I %S/Inputs -enable-source-import -disable-parser-lookup import imported_enums @@ -47,7 +47,7 @@ case 1 + (_): // expected-error{{'_' can only appear in a pattern or on the left } switch (x,x) { -case (var a, var a): // expected-error {{definition conflicts with previous value}} expected-note {{previous definition of 'a' is here}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} +case (var a, var a): // expected-error {{invalid redeclaration of 'a'}} expected-note {{'a' previously declared here}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} fallthrough case _: // expected-warning {{case is already handled by previous patterns; consider removing it}} () diff --git a/test/Parse/matching_patterns_parser_lookup.swift b/test/Parse/matching_patterns_parser_lookup.swift new file mode 100644 index 0000000000000..172689808f50f --- /dev/null +++ b/test/Parse/matching_patterns_parser_lookup.swift @@ -0,0 +1,332 @@ +// RUN: %target-typecheck-verify-swift -swift-version 4 -I %S/Inputs -enable-source-import -enable-parser-lookup + +import imported_enums + +// TODO: Implement tuple equality in the library. +// BLOCKED: +func ~= (x: (Int,Int,Int), y: (Int,Int,Int)) -> Bool { + return true +} + +var x:Int + +func square(_ x: Int) -> Int { return x*x } + +struct A { + struct C { } +} + +switch x { +// Expressions as patterns. +case 0: + () +case 1 + 2: + () +case square(9): + () + +// 'var' and 'let' patterns. +case var a: + a = 1 +case let a: + a = 1 // expected-error {{cannot assign}} +case var var a: // expected-error {{'var' cannot appear nested inside another 'var' or 'let' pattern}} + a += 1 +case var let a: // expected-error {{'let' cannot appear nested inside another 'var' or 'let' pattern}} + print(a, terminator: "") +case var (var b): // expected-error {{'var' cannot appear nested inside another 'var'}} + b += 1 + +// 'Any' pattern. +case _: + () + +// patterns are resolved in expression-only positions are errors. +case 1 + (_): // expected-error{{'_' can only appear in a pattern or on the left side of an assignment}} + () +} + +switch (x,x) { +case (var a, var a): // expected-error {{definition conflicts with previous value}} expected-note {{previous definition of 'a' is here}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} + fallthrough +case _: // expected-warning {{case is already handled by previous patterns; consider removing it}} + () +} + +var e : Any = 0 + +switch e { // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} +// 'is' pattern. +case is Int, + is A, + is A.C, + is (Int, Int), + is (a: Int, b: Int): + () +} + +// Enum patterns. +enum Foo { case A, B, C } + +func == (_: Voluntary, _: Voluntary) -> Bool { return true } + +enum Voluntary : Equatable { + case Naught + case Mere(T) + case Twain(T, T) + + + func enumMethod(_ other: Voluntary, foo: Foo) { + switch self { + case other: + () + + case .Naught, + .Naught(), // expected-error {{pattern with associated values does not match enum case 'Naught'}} + // expected-note@-1 {{remove associated values to make the pattern match}} {{17-19=}} + .Naught(_), // expected-error{{pattern with associated values does not match enum case 'Naught'}} + // expected-note@-1 {{remove associated values to make the pattern match}} {{17-20=}} + .Naught(_, _): // expected-error{{pattern with associated values does not match enum case 'Naught'}} + // expected-note@-1 {{remove associated values to make the pattern match}} {{17-23=}} + () + + case .Mere, + .Mere(), // expected-error{{tuple pattern cannot match values of the non-tuple type 'T'}} + .Mere(_), + .Mere(_, _): // expected-error{{tuple pattern cannot match values of the non-tuple type 'T'}} + () + + case .Twain(), // expected-error{{tuple pattern has the wrong length for tuple type '(T, T)'}} + .Twain(_), // expected-warning {{enum case 'Twain' has 2 associated values; matching them as a tuple is deprecated}} + // expected-note@-25 {{'Twain' declared here}} + .Twain(_, _), + .Twain(_, _, _): // expected-error{{tuple pattern has the wrong length for tuple type '(T, T)'}} + () + } + + switch foo { + case .Naught: // expected-error{{type 'Foo' has no member 'Naught'}} + () + case .A, .B, .C: + () + } + } +} + +var n : Voluntary = .Naught +if case let .Naught(value) = n {} // expected-error{{pattern with associated values does not match enum case 'Naught'}} + // expected-note@-1 {{remove associated values to make the pattern match}} {{20-27=}} +if case let .Naught(value1, value2, value3) = n {} // expected-error{{pattern with associated values does not match enum case 'Naught'}} + // expected-note@-1 {{remove associated values to make the pattern match}} {{20-44=}} + + + +switch n { +case Foo.A: // expected-error{{enum case 'A' is not a member of type 'Voluntary'}} + () +case Voluntary.Naught, + Voluntary.Naught(), // expected-error {{pattern with associated values does not match enum case 'Naught'}} + // expected-note@-1 {{remove associated values to make the pattern match}} {{27-29=}} + Voluntary.Naught(_, _), // expected-error{{pattern with associated values does not match enum case 'Naught'}} + // expected-note@-1 {{remove associated values to make the pattern match}} {{27-33=}} + Voluntary.Naught, + .Naught: + () +case Voluntary.Mere, + Voluntary.Mere(_), + Voluntary.Mere(_, _), // expected-error{{tuple pattern cannot match values of the non-tuple type 'Int'}} + Voluntary.Mere, + Voluntary.Mere(_), + .Mere, + .Mere(_): + () +case .Twain, + .Twain(_), // expected-warning {{enum case 'Twain' has 2 associated values; matching them as a tuple is deprecated}} + // expected-note@-69 {{'Twain' declared here}} + .Twain(_, _), + .Twain(_, _, _): // expected-error{{tuple pattern has the wrong length for tuple type '(Int, Int)'}} + () +} + +var notAnEnum = 0 + +switch notAnEnum { +case .Foo: // expected-error{{type 'Int' has no member 'Foo'}} + () +} + +struct ContainsEnum { + enum Possible { + case Naught + case Mere(T) + case Twain(T, T) + } + + func member(_ n: Possible) { + switch n { // expected-error {{switch must be exhaustive}} + // expected-note@-1 {{missing case: '.Mere(_)'}} + // expected-note@-2 {{missing case: '.Twain(_, _)'}} + case ContainsEnum.Possible.Naught, + ContainsEnum.Possible.Naught, // expected-warning {{case is already handled by previous patterns; consider removing it}} + Possible.Naught, // expected-warning {{case is already handled by previous patterns; consider removing it}} + Possible.Naught, // expected-warning {{case is already handled by previous patterns; consider removing it}} + .Naught: // expected-warning {{case is already handled by previous patterns; consider removing it}} + () + } + } +} + +func nonmemberAccessesMemberType(_ n: ContainsEnum.Possible) { + switch n { // expected-error {{switch must be exhaustive}} + // expected-note@-1 {{missing case: '.Mere(_)'}} + // expected-note@-2 {{missing case: '.Twain(_, _)'}} + case ContainsEnum.Possible.Naught, + .Naught: // expected-warning {{case is already handled by previous patterns; consider removing it}} + () + } +} + +var m : ImportedEnum = .Simple + +switch m { +case imported_enums.ImportedEnum.Simple, + ImportedEnum.Simple, // expected-warning {{case is already handled by previous patterns; consider removing it}} + .Simple: // expected-warning {{case is already handled by previous patterns; consider removing it}} + () +case imported_enums.ImportedEnum.Compound, + imported_enums.ImportedEnum.Compound(_), // expected-warning {{case is already handled by previous patterns; consider removing it}} + ImportedEnum.Compound, // expected-warning {{case is already handled by previous patterns; consider removing it}} + ImportedEnum.Compound(_), // expected-warning {{case is already handled by previous patterns; consider removing it}} + .Compound, // expected-warning {{case is already handled by previous patterns; consider removing it}} + .Compound(_): // expected-warning {{case is already handled by previous patterns; consider removing it}} + () +} + +// Check that single-element tuple payloads work sensibly in patterns. + +enum LabeledScalarPayload { + case Payload(name: Int) +} + +var lsp: LabeledScalarPayload = .Payload(name: 0) +func acceptInt(_: Int) {} +func acceptString(_: String) {} + +switch lsp { +case .Payload(0): + () +case .Payload(name: 0): + () +case let .Payload(x): + acceptInt(x) + acceptString("\(x)") +case let .Payload(name: x): // expected-warning {{case is already handled by previous patterns; consider removing it}} + acceptInt(x) + acceptString("\(x)") +case let .Payload((name: x)): // expected-warning {{case is already handled by previous patterns; consider removing it}} + acceptInt(x) + acceptString("\(x)") +case .Payload(let (name: x)): // expected-warning {{case is already handled by previous patterns; consider removing it}} + acceptInt(x) + acceptString("\(x)") +case .Payload(let (name: x)): // expected-warning {{case is already handled by previous patterns; consider removing it}} + acceptInt(x) + acceptString("\(x)") +case .Payload(let x): // expected-warning {{case is already handled by previous patterns; consider removing it}} + acceptInt(x) + acceptString("\(x)") +case .Payload((let x)): // expected-warning {{case is already handled by previous patterns; consider removing it}} + acceptInt(x) + acceptString("\(x)") +} + +// Property patterns. + +struct S { + static var stat: Int = 0 + var x, y : Int + var comp : Int { + return x + y + } + + func nonProperty() {} +} + + + + + +// Tuple patterns. + +var t = (1, 2, 3) + +prefix operator +++ +infix operator +++ +prefix func +++(x: (Int,Int,Int)) -> (Int,Int,Int) { return x } +func +++(x: (Int,Int,Int), y: (Int,Int,Int)) -> (Int,Int,Int) { + return (x.0+y.0, x.1+y.1, x.2+y.2) +} + +switch t { +case (_, var a, 3): + a += 1 +case var (_, b, 3): + b += 1 +case var (_, var c, 3): // expected-error{{'var' cannot appear nested inside another 'var'}} + c += 1 +case (1, 2, 3): + () + +// patterns in expression-only positions are errors. +case +++(_, var d, 3): +// expected-error@-1{{'_' can only appear in a pattern or on the left side of an assignment}} + () +case (_, var e, 3) +++ (1, 2, 3): +// expected-error@-1{{'_' can only appear in a pattern or on the left side of an assignment}} + () +case (let (_, _, _)) + 1: +// expected-error@-1 {{'_' can only appear in a pattern or on the left side of an assignment}} + () +} + +// FIXME: We don't currently allow subpatterns for "isa" patterns that +// require interesting conditional downcasts. +class Base { } +class Derived : Base { } + + +switch [Derived(), Derived(), Base()] { +case let ds as [Derived]: // expected-error{{collection downcast in cast pattern is not implemented; use an explicit downcast to '[Derived]' instead}} + () +case is [Derived]: // expected-error{{collection downcast in cast pattern is not implemented; use an explicit downcast to '[Derived]' instead}} + () + +default: + () +} + + +// Optional patterns. +let op1 : Int? +let op2 : Int?? + +switch op1 { +case nil: break +case 1?: break +case _?: break +} + +switch op2 { +case nil: break +case _?: break +case (1?)?: break +case (_?)?: break // expected-warning {{case is already handled by previous patterns; consider removing it}} +} + + + +// Bogus diagnostic "refutable pattern match can fail" +let (responseObject: Int?) = op1 +// expected-error @-1 {{expected ',' separator}} {{25-25=,}} +// expected-error @-2 {{expected pattern}} +// expected-error @-3 {{type of expression is ambiguous without more context}} diff --git a/test/Parse/switch.swift b/test/Parse/switch.swift index d44fcacd457cb..7baf7e3ceb554 100644 --- a/test/Parse/switch.swift +++ b/test/Parse/switch.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-parser-lookup // TODO: Implement tuple equality in the library. // BLOCKED: @@ -246,7 +246,7 @@ case (1, _): func patternVarUsedInAnotherPattern(x: Int) { switch x { case let a, // expected-error {{'a' must be bound in every pattern}} - a: + a: // expected-error {{cannot find 'a' in scope}} break } } diff --git a/test/Parse/switch_parser_lookup.swift b/test/Parse/switch_parser_lookup.swift new file mode 100644 index 0000000000000..34a6398d7869b --- /dev/null +++ b/test/Parse/switch_parser_lookup.swift @@ -0,0 +1,647 @@ +// RUN: %target-typecheck-verify-swift -enable-parser-lookup + +// TODO: Implement tuple equality in the library. +// BLOCKED: +func ~= (x: (Int,Int), y: (Int,Int)) -> Bool { + return true +} + +func parseError1(x: Int) { + switch func {} // expected-error {{expected expression in 'switch' statement}} expected-error {{expected '{' after 'switch' subject expression}} expected-error {{expected identifier in function declaration}} expected-error {{closure expression is unused}} expected-note{{did you mean to use a 'do' statement?}} {{15-15=do }} +} + +func parseError2(x: Int) { + switch x // expected-error {{expected '{' after 'switch' subject expression}} +} + +func parseError3(x: Int) { + switch x { + case // expected-error {{expected pattern}} expected-error {{expected ':' after 'case'}} + } +} + +func parseError4(x: Int) { + switch x { + case var z where // expected-error {{expected expression for 'where' guard of 'case'}} expected-error {{expected ':' after 'case'}} + } +} + +func parseError5(x: Int) { + switch x { + case let z // expected-error {{expected ':' after 'case'}} expected-warning {{immutable value 'z' was never used}} {{8-13=_}} + } +} + +func parseError6(x: Int) { + switch x { + default // expected-error {{expected ':' after 'default'}} + } +} + +var x: Int + +switch x {} // expected-error {{'switch' statement body must have at least one 'case' or 'default' block}} + +switch x { +case 0: + x = 0 +// Multiple patterns per case +case 1, 2, 3: + x = 0 +// 'where' guard +case _ where x % 2 == 0: + x = 1 + x = 2 + x = 3 +case _ where x % 2 == 0, + _ where x % 3 == 0: + x = 1 +case 10, + _ where x % 3 == 0: + x = 1 +case _ where x % 2 == 0, + 20: + x = 1 +case var y where y % 2 == 0: + x = y + 1 +case _ where 0: // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} + x = 0 +default: + x = 1 +} + +// Multiple cases per case block +switch x { // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} +case 0: // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{8-8= break}} +case 1: + x = 0 +} + +switch x { +case 0: // expected-error{{'case' label in a 'switch' should have at least one executable statement}} {{8-8= break}} +default: + x = 0 +} + +switch x { // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} +case 0: + x = 0 +case 1: // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{8-8= break}} +} + +switch x { +case 0: + x = 0 +default: // expected-error {{'default' label in a 'switch' should have at least one executable statement}} {{9-9= break}} +} + +switch x { // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} +case 0: + ; // expected-error {{';' statements are not allowed}} {{3-5=}} +case 1: + x = 0 +} + + + +switch x { + x = 1 // expected-error{{all statements inside a switch must be covered by a 'case' or 'default'}} +default: + x = 0 +case 0: // expected-error{{additional 'case' blocks cannot appear after the 'default' block of a 'switch'}} + x = 0 +case 1: + x = 0 +} + +switch x { +default: + x = 0 +default: // expected-error{{additional 'case' blocks cannot appear after the 'default' block of a 'switch'}} + x = 0 +} + +switch x { // expected-error{{'switch' statement body must have at least one 'case' or 'default' block}} + x = 1 // expected-error{{all statements inside a switch must be covered by a 'case' or 'default'}} +} + +switch x { // expected-error{{'switch' statement body must have at least one 'case' or 'default' block}} + x = 1 // expected-error{{all statements inside a switch must be covered by a 'case' or 'default'}} + x = 2 +} + +switch x { +default: // expected-error{{'default' label in a 'switch' should have at least one executable statement}} {{9-9= break}} +case 0: // expected-error{{additional 'case' blocks cannot appear after the 'default' block of a 'switch'}} + x = 0 +} + +switch x { +default: // expected-error{{'default' label in a 'switch' should have at least one executable statement}} {{9-9= break}} +default: // expected-error{{additional 'case' blocks cannot appear after the 'default' block of a 'switch'}} + x = 0 +} + +switch x { // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} +default where x == 0: // expected-error{{'default' cannot be used with a 'where' guard expression}} + x = 0 +} + +switch x { // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} +case 0: // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{8-8= break}} +} + +switch x { // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} +case 0: // expected-error{{'case' label in a 'switch' should have at least one executable statement}} {{8-8= break}} +case 1: + x = 0 +} + +switch x { // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} +case 0: + x = 0 +case 1: // expected-error{{'case' label in a 'switch' should have at least one executable statement}} {{8-8= break}} +} + + +case 0: // expected-error{{'case' label can only appear inside a 'switch' statement}} +var y = 0 +default: // expected-error{{'default' label can only appear inside a 'switch' statement}} +var z = 1 + +fallthrough // expected-error{{'fallthrough' is only allowed inside a switch}} + +switch x { +case 0: + fallthrough +case 1: + fallthrough +default: + fallthrough // expected-error{{'fallthrough' without a following 'case' or 'default' block}} +} + +// Fallthrough can transfer control anywhere within a case and can appear +// multiple times in the same case. +switch x { +case 0: + if true { fallthrough } + if false { fallthrough } + x += 1 +default: + x += 1 +} + +// Cases cannot contain 'var' bindings if there are multiple matching patterns +// attached to a block. They may however contain other non-binding patterns. + +var t = (1, 2) + +switch t { +case (var a, 2), (1, _): // expected-error {{'a' must be bound in every pattern}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} + () + +case (_, 2), (var a, _): // expected-error {{'a' must be bound in every pattern}} + () + +case (var a, 2), (1, var b): // expected-error {{'a' must be bound in every pattern}} expected-error {{'b' must be bound in every pattern}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} + () + +case (var a, 2): // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{17-17= break}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} +case (1, _): + () + +case (_, 2): // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{13-13= break}} +case (1, var a): // expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} + () + +case (var a, 2): // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{17-17= break}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} +case (1, var b): // expected-warning {{variable 'b' was never used; consider replacing with '_' or removing it}} + () + +case (1, let b): // let bindings expected-warning {{immutable value 'b' was never used; consider replacing with '_' or removing it}} + () + +case (_, 2), (let a, _): // expected-error {{'a' must be bound in every pattern}} expected-warning {{case is already handled by previous patterns; consider removing it}} + () + +// OK +case (_, 2), (1, _): + () + +case (_, var a), (_, var a): // expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} + // expected-warning@-1 {{case is already handled by previous patterns; consider removing it}} + // expected-warning@-2 {{case is already handled by previous patterns; consider removing it}} + () + +case (var a, var b), (var b, var a): // expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} expected-warning {{variable 'b' was never used; consider replacing with '_' or removing it}} + // expected-warning@-1 {{case is already handled by previous patterns; consider removing it}} + // expected-warning@-2 {{case is already handled by previous patterns; consider removing it}} + () + +case (_, 2): // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{13-13= break}} +case (1, _): + () +} + +func patternVarUsedInAnotherPattern(x: Int) { + switch x { + case let a, // expected-error {{'a' must be bound in every pattern}} + a: + break + } +} + +// Fallthroughs can only transfer control into a case label with bindings if the previous case binds a superset of those vars. +switch t { +case (1, 2): + fallthrough // expected-error {{'fallthrough' from a case which doesn't bind variable 'a'}} expected-error {{'fallthrough' from a case which doesn't bind variable 'b'}} +case (var a, var b): // expected-warning {{variable 'a' was never mutated; consider changing to 'let' constant}} expected-warning {{variable 'b' was never mutated; consider changing to 'let' constant}} + t = (b, a) +} + +switch t { // specifically notice on next line that we shouldn't complain that a is unused - just never mutated +case (var a, let b): // expected-warning {{variable 'a' was never mutated; consider changing to 'let' constant}} + t = (b, b) + fallthrough // ok - notice that subset of bound variables falling through is fine +case (2, let a): + t = (a, a) +} + +func patternVarDiffType(x: Int, y: Double) { + switch (x, y) { + case (1, let a): // expected-error {{pattern variable bound to type 'Double', fallthrough case bound to type 'Int'}} + fallthrough + case (let a, _): + break + } +} + +func patternVarDiffMutability(x: Int, y: Double) { + switch x { + case let a where a < 5, var a where a > 10: // expected-error {{'var' pattern binding must match previous 'let' pattern binding}}{{27-30=let}} + break + default: + break + } + switch (x, y) { + // Would be nice to have a fixit in the following line if we detect that all bindings in the same pattern have the same problem. + case let (a, b) where a < 5, var (a, b) where a > 10: // expected-error 2{{'var' pattern binding must match previous 'let' pattern binding}}{{none}} + break + case (let a, var b) where a < 5, (let a, let b) where a > 10: // expected-error {{'let' pattern binding must match previous 'var' pattern binding}}{{44-47=var}} + break + case (let a, let b) where a < 5, (var a, let b) where a > 10, (let a, var b) where a == 8: + // expected-error@-1 {{'var' pattern binding must match previous 'let' pattern binding}}{{37-40=let}} + // expected-error@-2 {{'var' pattern binding must match previous 'let' pattern binding}}{{73-76=let}} + break + default: + break + } +} + +func test_label(x : Int) { +Gronk: // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} + switch x { + case 42: return + } +} + +func enumElementSyntaxOnTuple() { + switch (1, 1) { + case .Bar: // expected-error {{value of tuple type '(Int, Int)' has no member 'Bar'}} + break + default: + break + } +} + +// sr-176 +enum Whatever { case Thing } +func f0(values: [Whatever]) { // expected-note {{'values' declared here}} + switch value { // expected-error {{cannot find 'value' in scope; did you mean 'values'?}} + case .Thing: // Ok. Don't emit diagnostics about enum case not found in type <>. + break + } +} + +// sr-720 +enum Whichever { + case Thing + static let title = "title" + static let alias: Whichever = .Thing +} +func f1(x: String, y: Whichever) { + switch x { + case Whichever.title: // Ok. Don't emit diagnostics for static member of enum. + break + case Whichever.buzz: // expected-error {{type 'Whichever' has no member 'buzz'}} + break + case Whichever.alias: // expected-error {{expression pattern of type 'Whichever' cannot match values of type 'String'}} + // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists: (Substring, String)}} + break + default: + break + } + switch y { + case Whichever.Thing: // Ok. + break + case Whichever.alias: // Ok. Don't emit diagnostics for static member of enum. + break + case Whichever.title: // expected-error {{expression pattern of type 'String' cannot match values of type 'Whichever'}} + break + } +} + + +switch Whatever.Thing { +case .Thing: // expected-error{{'case' label in a 'switch' should have at least one executable statement}} {{13-13= break}} +@unknown case _: + x = 0 +} + +switch Whatever.Thing { +case .Thing: // expected-error{{'case' label in a 'switch' should have at least one executable statement}} {{13-13= break}} +@unknown default: + x = 0 +} + +switch Whatever.Thing { +case .Thing: + x = 0 +@unknown case _: // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{17-17= break}} +} + +switch Whatever.Thing { +case .Thing: + x = 0 +@unknown default: // expected-error {{'default' label in a 'switch' should have at least one executable statement}} {{18-18= break}} +} + + +switch Whatever.Thing { +@unknown default: + x = 0 +default: // expected-error{{additional 'case' blocks cannot appear after the 'default' block of a 'switch'}} + x = 0 +case .Thing: + x = 0 +} + +switch Whatever.Thing { +default: + x = 0 +@unknown case _: // expected-error{{additional 'case' blocks cannot appear after the 'default' block of a 'switch'}} expected-error {{'@unknown' can only be applied to the last case in a switch}} + x = 0 +case .Thing: + x = 0 +} + +switch Whatever.Thing { +default: + x = 0 +@unknown default: // expected-error{{additional 'case' blocks cannot appear after the 'default' block of a 'switch'}} + x = 0 +case .Thing: + x = 0 +} + +switch Whatever.Thing { // expected-warning {{switch must be exhaustive}} expected-note{{add missing case: '.Thing'}} +@unknown default where x == 0: // expected-error{{'default' cannot be used with a 'where' guard expression}} + x = 0 +} + +switch Whatever.Thing { // expected-warning {{switch must be exhaustive}} expected-note{{add missing case: '.Thing'}} +@unknown case _: + fallthrough // expected-error{{'fallthrough' without a following 'case' or 'default' block}} +} + +switch Whatever.Thing { +@unknown case _: // expected-error {{'@unknown' can only be applied to the last case in a switch}} + fallthrough +case .Thing: + break +} + +switch Whatever.Thing { +@unknown default: + fallthrough +case .Thing: // expected-error{{additional 'case' blocks cannot appear after the 'default' block of a 'switch'}} + break +} + +switch Whatever.Thing { +@unknown case _, _: // expected-error {{'@unknown' cannot be applied to multiple patterns}} + break +} + +switch Whatever.Thing { +@unknown case _, _, _: // expected-error {{'@unknown' cannot be applied to multiple patterns}} + break +} + +switch Whatever.Thing { // expected-warning {{switch must be exhaustive}} expected-note{{add missing case: '.Thing'}} +@unknown case let value: // expected-error {{'@unknown' is only supported for catch-all cases ("case _")}} + _ = value +} + +switch (Whatever.Thing, Whatever.Thing) { // expected-warning {{switch must be exhaustive}} expected-note{{add missing case: '(_, _)'}} +@unknown case (_, _): // expected-error {{'@unknown' is only supported for catch-all cases ("case _")}} + break +} + +switch Whatever.Thing { // expected-warning {{switch must be exhaustive}} expected-note{{add missing case: '.Thing'}} +@unknown case is Whatever: // expected-error {{'@unknown' is only supported for catch-all cases ("case _")}} + // expected-warning@-1 {{'is' test is always true}} + break +} + +switch Whatever.Thing { // expected-warning {{switch must be exhaustive}} expected-note{{add missing case: '.Thing'}} +@unknown case .Thing: // expected-error {{'@unknown' is only supported for catch-all cases ("case _")}} + break +} + +switch Whatever.Thing { // expected-warning {{switch must be exhaustive}} expected-note{{add missing case: '.Thing'}} +@unknown case (_): // okay + break +} + +switch Whatever.Thing { // expected-warning {{switch must be exhaustive}} expected-note{{add missing case: '.Thing'}} +@unknown case _ where x == 0: // expected-error {{'where' cannot be used with '@unknown'}} + break +} + +switch Whatever.Thing { // expected-warning {{switch must be exhaustive}} expected-note{{add missing case: '.Thing'}} +@unknown default where x == 0: // expected-error {{'default' cannot be used with a 'where' guard expression}} + break +} + +switch Whatever.Thing { +case .Thing: + x = 0 +#if true +@unknown case _: + x = 0 +#endif +} + +switch x { +case 0: + break +@garbage case _: // expected-error {{unknown attribute 'garbage'}} + break +} + +switch x { +case 0: + break +@garbage @moreGarbage default: // expected-error {{unknown attribute 'garbage'}} expected-error {{unknown attribute 'moreGarbage'}} + break +} + +@unknown let _ = 1 // expected-error {{unknown attribute 'unknown'}} + +switch x { +case _: + @unknown let _ = 1 // expected-error {{unknown attribute 'unknown'}} +} + +switch Whatever.Thing { +case .Thing: + break +@unknown(garbage) case _: // expected-error {{unexpected '(' in attribute 'unknown'}} + break +} +switch Whatever.Thing { +case .Thing: + break +@unknown // expected-note {{attribute already specified here}} +@unknown // expected-error {{duplicate attribute}} +case _: + break +} +switch Whatever.Thing { // expected-warning {{switch must be exhaustive}} expected-note {{add missing case: '.Thing'}} +@unknown @garbage(foobar) // expected-error {{unknown attribute 'garbage'}} +case _: + break +} + +switch x { // expected-error {{switch must be exhaustive}} +case 1: + break +@unknown case _: // expected-note {{remove '@unknown' to handle remaining values}} {{1-10=}} + break +} + +switch x { // expected-error {{switch must be exhaustive}} +@unknown case _: // expected-note {{remove '@unknown' to handle remaining values}} {{1-10=}} + break +} + +switch x { // expected-error {{switch must be exhaustive}} +@unknown default: // expected-note {{remove '@unknown' to handle remaining values}} {{1-10=}} + break +} + +switch Whatever.Thing { +case .Thing: + break +@unknown case _: // expected-error {{'@unknown' can only be applied to the last case in a switch}} + break +@unknown case _: + break +} + +switch Whatever.Thing { +case .Thing: + break +@unknown case _: // expected-error {{'@unknown' can only be applied to the last case in a switch}} + break +@unknown default: + break +} + +switch Whatever.Thing { +case .Thing: + break +@unknown default: + break +@unknown default: // expected-error {{additional 'case' blocks cannot appear after the 'default' block of a 'switch'}} + break +} + +switch Whatever.Thing { +@unknown case _: // expected-error {{'@unknown' can only be applied to the last case in a switch}} + break +@unknown case _: + break +} + +switch Whatever.Thing { +@unknown case _: // expected-error {{'@unknown' can only be applied to the last case in a switch}} + break +@unknown default: + break +} + +switch Whatever.Thing { +@unknown default: + break +@unknown default: // expected-error {{additional 'case' blocks cannot appear after the 'default' block of a 'switch'}} + break +} + + +switch x { +@unknown case _: // expected-error {{'@unknown' can only be applied to the last case in a switch}} + break +@unknown case _: + break +} + +switch x { +@unknown case _: // expected-error {{'@unknown' can only be applied to the last case in a switch}} + break +@unknown default: + break +} + +switch x { +@unknown default: + break +@unknown default: // expected-error {{additional 'case' blocks cannot appear after the 'default' block of a 'switch'}} + break +} + +func testReturnBeforeUnknownDefault() { + switch x { // expected-error {{switch must be exhaustive}} + case 1: + return + @unknown default: // expected-note {{remove '@unknown' to handle remaining values}} + break + } +} + +func testReturnBeforeIncompleteUnknownDefault() { + switch x { // expected-error {{switch must be exhaustive}} + case 1: + return + @unknown default // expected-error {{expected ':' after 'default'}} + // expected-note@-1 {{remove '@unknown' to handle remaining values}} + } +} + +func testReturnBeforeIncompleteUnknownDefault2() { + switch x { // expected-error {{switch must be exhaustive}} expected-note {{do you want to add a default clause?}} + case 1: + return + @unknown // expected-error {{unknown attribute 'unknown'}} + } // expected-error {{expected declaration}} +} + +func testIncompleteArrayLiteral() { + switch x { // expected-error {{switch must be exhaustive}} + case 1: + _ = [1 // expected-error {{expected ']' in container literal expression}} expected-note {{to match this opening '['}} + @unknown default: // expected-note {{remove '@unknown' to handle remaining values}} + () + } +} diff --git a/test/Sema/diag_variable_used_in_initial.swift b/test/Sema/diag_variable_used_in_initial.swift index c84cdc9d0cdc6..c60dc5c0dc0fa 100644 --- a/test/Sema/diag_variable_used_in_initial.swift +++ b/test/Sema/diag_variable_used_in_initial.swift @@ -1,45 +1,49 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-parser-lookup class A1 { - func foo1() {} + func foo1() -> Int {} func foo2() { - var foo1 = foo1() // expected-error {{variable used within its own initial value}} + var foo1 = foo1() + // expected-warning@-1 {{initialization of variable 'foo1' was never used; consider replacing with assignment to '_' or removing it}} } } class A2 { var foo1 = 2 func foo2() { - // FIXME: "the var" doesn't sound right. - var foo1 = foo1 // expected-error {{variable used within its own initial value}} + var foo1 = foo1 + // expected-warning@-1 {{initialization of variable 'foo1' was never used; consider replacing with assignment to '_' or removing it}} } } class A3 { func foo2() { - // FIXME: this should also add fixit. - var foo1 = foo1() // expected-error {{variable used within its own initial value}}{{none}} + var foo1 = foo1() + // expected-warning@-1 {{initialization of variable 'foo1' was never used; consider replacing with assignment to '_' or removing it}} } - func foo1() {} + func foo1() -> Int {} } class A4 { func foo2() { - var foo1 = foo1 // expected-error {{variable used within its own initial value}}{{none}} + var foo1 = foo1 // expected-error {{use of local variable 'foo1' before its declaration}} + // expected-note@-1 {{'foo1' declared here}} } } func localContext() { class A5 { - func foo1() {} + func foo1() -> Int {} func foo2() { - var foo1 = foo1() // expected-error {{variable used within its own initial value}} + var foo1 = foo1() + // expected-warning@-1 {{initialization of variable 'foo1' was never used; consider replacing with assignment to '_' or removing it}} } class A6 { - func foo1() {} + func foo1() -> Int {} func foo2() { - var foo1 = foo1() // expected-error {{variable used within its own initial value}} + var foo1 = foo1() + // expected-warning@-1 {{initialization of variable 'foo1' was never used; consider replacing with assignment to '_' or removing it}} } } @@ -48,7 +52,7 @@ func localContext() { class A7 { func foo1() {} func foo2() { - var foo1 = foo1() // expected-error {{variable used within its own initial value}} + var foo1 = foo1() } } } diff --git a/test/Sema/diag_variable_used_in_initial_parser_lookup.swift b/test/Sema/diag_variable_used_in_initial_parser_lookup.swift new file mode 100644 index 0000000000000..6b2d7f900343e --- /dev/null +++ b/test/Sema/diag_variable_used_in_initial_parser_lookup.swift @@ -0,0 +1,56 @@ +// RUN: %target-typecheck-verify-swift -enable-parser-lookup + +class A1 { + func foo1() {} + func foo2() { + var foo1 = foo1() // expected-error {{variable used within its own initial value}} + } +} + +class A2 { + var foo1 = 2 + func foo2() { + // FIXME: "the var" doesn't sound right. + var foo1 = foo1 // expected-error {{variable used within its own initial value}} + } +} + +class A3 { + func foo2() { + // FIXME: this should also add fixit. + var foo1 = foo1() // expected-error {{variable used within its own initial value}}{{none}} + } + func foo1() {} +} + +class A4 { + func foo2() { + var foo1 = foo1 // expected-error {{variable used within its own initial value}}{{none}} + } +} + +func localContext() { + class A5 { + func foo1() {} + func foo2() { + var foo1 = foo1() // expected-error {{variable used within its own initial value}} + } + + class A6 { + func foo1() {} + func foo2() { + var foo1 = foo1() // expected-error {{variable used within its own initial value}} + } + } + + extension E { // expected-error {{declaration is only valid at file scope}} + // expected-error@-1{{cannot find type 'E' in scope}} + class A7 { + func foo1() {} + func foo2() { + var foo1 = foo1() // expected-error {{variable used within its own initial value}} + } + } + } + } +} diff --git a/test/decl/circularity.swift b/test/decl/circularity.swift index 1f2db505630a4..29af1f663a23c 100644 --- a/test/decl/circularity.swift +++ b/test/decl/circularity.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-parser-lookup // N.B. Validating the pattern binding initializer for `pickMe` used to cause // recursive validation of the VarDecl. Check that we don't regress now that @@ -40,7 +40,8 @@ class Base { class Sub: Base { var foo = { () -> Int in let x = 42 - return foo(1) // expected-error {{variable used within its own initial value}} + // FIXME: Bogus diagnostic + return foo(1) // expected-error {{cannot convert return expression of type '()' to return type 'Int'}} }() } diff --git a/test/decl/circularity_parser_lookup.swift b/test/decl/circularity_parser_lookup.swift new file mode 100644 index 0000000000000..be25472613ee6 --- /dev/null +++ b/test/decl/circularity_parser_lookup.swift @@ -0,0 +1,114 @@ +// RUN: %target-typecheck-verify-swift -enable-parser-lookup + +// N.B. Validating the pattern binding initializer for `pickMe` used to cause +// recursive validation of the VarDecl. Check that we don't regress now that +// this isn't the case. +public struct Cyclic { + static func pickMe(please: Bool) -> Int { return 42 } + public static let pickMe = Cyclic.pickMe(please: true) +} + +struct Node {} +struct Parameterized { + func please(_ transform: @escaping (_ otherValue: NewValue) -> Value) -> Parameterized { + fatalError() + } +} + +extension Parameterized where Value == [Node], Format == String { + static var pickMe: Parameterized { + fatalError() + } +} + +extension Parameterized where Value == Node, Format == String { + static let pickMe = Parameterized<[Node], String>.pickMe.please { [$0] } +} + +enum Loop: Circle { + struct DeLoop { } +} + +protocol Circle { + typealias DeLoop = Loop.DeLoop +} + +class Base { + static func foo(_ x: Int) {} +} + +class Sub: Base { + var foo = { () -> Int in + let x = 42 + return foo(1) // expected-error {{variable used within its own initial value}} + }() +} + +extension Float { + static let pickMe: Float = 1 +} + +extension SIMD3 { + init(_ scalar: Scalar) { self.init(repeating: scalar) } +} + +extension SIMD3 where SIMD3.Scalar == Float { + static let pickMe = SIMD3(.pickMe) +} + +// Test case with circular overrides +protocol P { + associatedtype A + // expected-note@-1 {{protocol requires nested type 'A'; do you want to add it?}} + // expected-note@-2 {{through reference here}} + func run(a: A) +} + +class C1 { + func run(a: Int) {} +} + +class C2: C1, P { + override func run(a: A) {} + // expected-error@-1 {{circular reference}} + // expected-note@-2 {{while resolving type 'A'}} + // expected-note@-3 2{{through reference here}} +} + +// Another crash to the above +open class G1 { + open func run(a: A) {} +} + +class C3: G1, P { + // expected-error@-1 {{type 'C3' does not conform to protocol 'P'}} + // expected-error@-2 {{cannot find type 'A' in scope}} + override func run(a: A) {} + // expected-error@-1 {{method does not override any method from its superclass}} +} + +// Another case that triggers circular override checking. +protocol P1 { + associatedtype X = Int // expected-note {{through reference here}} + init(x: X) +} + +class C4 { + required init(x: Int) {} +} + +class D4 : C4, P1 { // expected-note 2 {{through reference here}} + required init(x: X) { // expected-error {{circular reference}} + // expected-note@-1 {{while resolving type 'X'}} + // expected-note@-2 2{{through reference here}} + super.init(x: x) + } +} + +// SR-12236 +// N.B. This used to compile in 5.1. +protocol SR12236 { } +class SR12236_A { // expected-note {{through reference here}} + typealias Nest = SR12236 // expected-error {{circular reference}} expected-note {{through reference here}} +} +extension SR12236_A: SR12236_A.Nest { } diff --git a/test/decl/typealias/generic.swift b/test/decl/typealias/generic.swift index faf486048ec3a..732800f558492 100644 --- a/test/decl/typealias/generic.swift +++ b/test/decl/typealias/generic.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-parser-lookup struct MyType { // expected-note {{generic type 'MyType' declared here}} // expected-note @-1 {{arguments to generic parameter 'TyB' ('S' and 'Int') are expected to be equal}} @@ -35,8 +35,8 @@ typealias BadA = MyType // expected-error {{type 'T' constr typealias BadB = MyType // expected-error {{associated types must not have a generic parameter list}} // expected-error@-1 {{same-type requirement makes generic parameter 'T' non-generic}} -typealias BadC = MyType // expected-error {{definition conflicts with previous value}} -// expected-note @-1 {{previous definition of 'T' is here}} +typealias BadC = MyType // expected-error {{invalid redeclaration of 'T'}} +// expected-note @-1 {{'T' previously declared here}} typealias Tuple2 = (T1, T2) diff --git a/test/decl/typealias/generic_parser_lookup.swift b/test/decl/typealias/generic_parser_lookup.swift new file mode 100644 index 0000000000000..63c0cb22af2f0 --- /dev/null +++ b/test/decl/typealias/generic_parser_lookup.swift @@ -0,0 +1,443 @@ +// RUN: %target-typecheck-verify-swift -enable-parser-lookup + +struct MyType { // expected-note {{generic type 'MyType' declared here}} + // expected-note @-1 {{arguments to generic parameter 'TyB' ('S' and 'Int') are expected to be equal}} + // expected-note @-2 4 {{arguments to generic parameter 'TyA' ('Float' and 'Int') are expected to be equal}} + var a : TyA, b : TyB +} + +// +// Type aliases that reference unbound generic types -- not really generic, +// but they behave as such, in the sense that you can apply generic +// arguments to them. +// + +typealias OurType = MyType + +typealias YourType = Swift.Optional + +struct Container { + typealias YourType = Swift.Optional +} + +let _: OurType +let _: YourType +let _: Container.YourType + +// +// Bona-fide generic type aliases +// + +typealias DS = MyType + +typealias BadA = MyType // expected-error {{type 'T' constrained to non-protocol, non-class type 'Int'}} + +typealias BadB = MyType // expected-error {{associated types must not have a generic parameter list}} +// expected-error@-1 {{same-type requirement makes generic parameter 'T' non-generic}} + +typealias BadC = MyType // expected-error {{definition conflicts with previous value}} +// expected-note @-1 {{previous definition of 'T' is here}} + +typealias Tuple2 = (T1, T2) + +typealias Tuple3 = (T1, T1) where T1 : Hashable + + +let _ : Tuple2 = (1, "foo") +let _ : Tuple2 = (1, "foo") +let _ : Tuple2 = ("bar", // expected-error {{cannot convert value of type '(String, String)' to specified type 'Tuple2' (aka '(Int, String)')}} + "foo") + +func f() { + typealias Tuple2b = (T1, T2) + let _ : Tuple2b = (1, "foo") + +} + + +typealias A = MyType // expected-note {{generic type 'A' declared here}} + +typealias B = MyType + +typealias C = MyType + +// Type aliases with unused generic params. +typealias D = MyType // expected-note 3 {{'T3' declared as parameter to type 'D'}} + +typealias E = Int // expected-note {{generic type 'E' declared here}} +// expected-note@-1 {{'T1' declared as parameter to type 'E'}} +// expected-note@-2 {{'T2' declared as parameter to type 'E'}} + +typealias F = (T1) -> T2 + +// Type alias of type alias. +typealias G = A + +let _ : E = 42 +let _ : E = 42 // expected-error {{generic type 'E' specialized with too few type parameters (got 1, but expected 2)}} +let _ : E = 42 +// expected-error@-1 {{generic parameter 'T1' could not be inferred}} +// expected-error@-2 {{generic parameter 'T2' could not be inferred}} +let _ : D = D(a: 1, b: 2) +// expected-error@-1 {{generic parameter 'T3' could not be inferred}} +// expected-note@-2 {{explicitly specify the generic arguments to fix this issue}} {{14-14=}} + +let _ : D = D(a: 1, b: 2) + +let _ : D = D(a: 1, b: 2) +// expected-error@-1 {{generic parameter 'T3' could not be inferred}} + + +// expected-error @+2 {{generic parameter 'T3' could not be inferred}} +// expected-note @+1 {{explicitly specify the generic arguments to fix this issue}} {{31-31=}} +let _ : D = D(a: 1, b: 2) + +let _ : F = { (a : Int) -> Int in a } // Infer the types of F + +let _ : F = { a in a } // expected-error {{unable to infer type of a closure parameter 'a' in the current context}} + +_ = MyType(a: "foo", b: 42) +_ = A(a: "foo", b: 42) +_ = A(a: "foo", b: 42) +_ = A(a: "foo", // expected-error {{cannot convert value of type 'String' to expected argument type 'Int'}} + b: 42) // expected-error {{cannot convert value of type 'Int' to expected argument type 'String'}} +_ = B(a: 12, b: 42) +_ = B(a: 12, b: 42 as Float) +_ = B(a: "foo", b: 42) // expected-error {{conflicting arguments to generic parameter 'T1' ('Int' vs. 'String')}} +_ = C(a: "foo", b: 42) +_ = C(a: 42, // expected-error {{cannot convert value of type 'Int' to expected argument type 'String'}} + b: 42) + +_ = G(a: "foo", b: 42) +_ = G(a: "foo", b: 42) + +// Generic typealias cannot have unbound generic type. +typealias VeryBad1 = MyType // expected-error {{reference to generic type 'MyType' requires arguments in <...>}} +typealias VeryBad2 = Swift.Array // expected-error {{reference to generic type 'Array' requires arguments in <...>}} + +struct MyTypeWithHashable { +} + +typealias MTWHInt = MyTypeWithHashable +typealias MTWHInt2 = MyTypeWithHashable // expected-error {{type 'HT' does not conform to protocol 'Hashable'}} + +func f(a : MyTypeWithHashable) { + f(a: MyTypeWithHashable()) + f(a: MTWHInt()) +} + + +// Unqualified lookup of generic typealiases nested inside generic contexts +class GenericClass { + typealias TA = MyType + typealias TAI = MyType + + func testCapture(s: S, t: T) -> TA { + return TA(a: t, b: s) + } + + func testCaptureUnbound(s: S, t: T) -> TA { + return TA(a: t, b: s) + } + + func testConcrete1(s: Int, t: T) -> TA { + return TA(a: t, b: s) + } + + func testConcreteUnbound1(s: Int, t: T) -> TA { + return TA(a: t, b: s) + } + + func testConcrete2(s: Float, t: Int) -> TAI { + return TAI(a: t, b: s) + } + + func testConcreteUnbound2(s: Float, t: Int) -> TAI { + return TAI(a: t, b: s) + } + + func testCaptureInvalid1(s: S, t: T) -> TA { + return TA(a: t, b: s) // expected-error {{cannot convert return expression of type 'GenericClass.TA' (aka 'MyType') to return type 'GenericClass.TA' (aka 'MyType')}} + } + + func testCaptureInvalid2(s: Int, t: T) -> TA { + return TA(a: t, b: s) // expected-error {{cannot convert value of type 'Int' to expected argument type 'S'}} + } + + struct NestedStruct { + typealias TA = MyType<(T, V), (U, V)> + + func testCapture(x: (T, S), y: (U, S)) -> TA { + return TA(a: x, b: y) + } + } + + // Stupid corner case -- underlying type is not dependent + typealias NotDependent = Int + + func misleadingCode(_: NotDependent) {} +} + +let gc = GenericClass() +let fn: MyType = gc.testCapture(s: 1, t: 1.0) + +func use(_ t: T) {} +use(fn) + +// Make sure we apply base substitutions to the interface type of the typealias +class ConcreteClass : GenericClass { + func testSubstitutedCapture1(s: S, t: String) -> TA { + return TA(a: t, b: s) + } + + func testSubstitutedCapture2(s: S, t: String) -> TA { + return TA(a: t, b: s) + } + + func testSubstitutedCapture3(s: Int, t: String) -> TA { + return TA(a: t, b: s) + } + + func testSubstitutedCapture4(s: Int, t: String) -> TA { + return TA(a: t, b: s) + } + + func testSubstitutedCapture5(s: Float, t: Int) -> TAI { + return TAI(a: t, b: s) + } + + func testSubstitutedCapture6(s: Float, t: Int) -> TAI { + return TAI(a: t, b: s) + } +} + +// Qualified lookup of generic typealiases nested inside concrete contexts +struct ConcreteStruct { + typealias O = Optional +} + +func takesUnsugaredType1(m: MyType) {} +func takesSugaredType1(m: ConcreteClass.TA) { + takesUnsugaredType1(m: m) +} + +let _ = ConcreteStruct.O(123) +let _ = ConcreteStruct.O(123) + +let _: ConcreteStruct.O = ConcreteStruct.O(123) +let _: ConcreteStruct.O = ConcreteStruct.O(123) + +let _: ConcreteStruct.O = ConcreteStruct.O(123) +let _: ConcreteStruct.O = ConcreteStruct.O(123) + +// Qualified lookup of generic typealiases nested inside generic contexts +// +// FIXME marks cases which still don't work correctly, and either produce a +// spurious diagnostic, or are actually invalid and do not diagnose. +// +// This occurs because the constraint solver does the wrong thing with an +// UnresolvedSpecializeExpr applied to a generic typealias. +// +// In the other cases, we manage to fold the UnresolvedSpecializeExpr in the +// precheckExpression() phase, which handles generic typealiases correctly. + +let _ = GenericClass.TA(a: 4.0, b: 1) // FIXME +let _ = GenericClass.TA(a: 1, b: 4.0) + +let _ = GenericClass.TA(a: 4.0, b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} +let _ = GenericClass.TA(a: 1, b: 4.0) + +let _ = GenericClass.TA(a: 4.0, b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} +let _ = GenericClass.TA(a: 1, b: 4.0) + +let _: GenericClass.TA = GenericClass.TA(a: 4.0, b: 1) +let _: GenericClass.TA = GenericClass.TA(a: 1, b: 4.0) + +let _: GenericClass.TA = GenericClass.TA(a: 4.0, b: 1) // FIXME +let _: GenericClass.TA = GenericClass.TA(a: 1, b: 4.0) + +let _: GenericClass.TA = GenericClass.TA(a: 4.0, b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} +let _: GenericClass.TA = GenericClass.TA(a: 1, b: 4.0) + +let _: GenericClass.TA = GenericClass.TA(a: 4.0, b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} +let _: GenericClass.TA = GenericClass.TA(a: 1, b: 4.0) + +let _: GenericClass.TA = GenericClass.TA(a: 4.0, b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} +let _: GenericClass.TA = GenericClass.TA(a: 1, b: 4.0) + +let _: GenericClass.TA = GenericClass.TA(a: 4.0, b: 1) // expected-error {{cannot assign value of type 'MyType' to type 'MyType'}} +let _: GenericClass.TA = GenericClass.TA(a: 1, b: 4.0) // expected-error {{cannot assign value of type 'MyType' to type 'MyType'}} + +let _: GenericClass.TA = GenericClass.TA(a: 4.0, b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} +let _: GenericClass.TA = GenericClass.TA(a: 1, b: 4.0) + +let _: GenericClass.TA = GenericClass.TA(a: 4.0, b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} +let _: GenericClass.TA = GenericClass.TA(a: 1, b: 4.0) + +let _: GenericClass.TA = GenericClass.TA(a: 4.0, b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} +let _: GenericClass.TA = GenericClass.TA(a: 1, b: 4.0) + +let _: GenericClass.TA = GenericClass.TA(a: 4.0, b: 1) // expected-error {{cannot assign value of type 'MyType' to type 'GenericClass.TA' (aka 'MyType')}} +let _: GenericClass.TA = GenericClass.TA(a: 1, b: 4.0) // expected-error {{cannot assign value of type 'MyType' to type 'GenericClass.TA' (aka 'MyType')}} + +let _: GenericClass.TA = GenericClass.TA(a: 4.0, b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} +let _: GenericClass.TA = GenericClass.TA(a: 1, b: 4.0) + +let _: GenericClass.TA = GenericClass.TA(a: 4.0, b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} +let _: GenericClass.TA = GenericClass.TA(a: 1, b: 4.0) + +func takesUnsugaredType2(m: MyType) {} +func takesSugaredType2(m: GenericClass.TA) { + takesUnsugaredType2(m: m) +} + + +// +// Error paths +// + +// This works, but in the body of the extension we see the original type +// parameters of A<>'s underlying type MyType<>, rather than the type +// parameters of A<>. +extension A {} + +extension A {} // expected-error {{generic type 'A' specialized with too few type parameters (got 1, but expected 2)}} +extension A {} // expected-error {{constrained extension must be declared on the unspecialized generic type 'MyType' with constraints specified by a 'where' clause}} +extension C {} // expected-error {{cannot find type 'T' in scope}} +extension C {} // expected-error {{constrained extension must be declared on the unspecialized generic type 'MyType' with constraints specified by a 'where' clause}} + + +protocol ErrorQ { + associatedtype Y +} +protocol ErrorP { + associatedtype X: ErrorQ // expected-note {{protocol requires nested type 'X'; do you want to add it?}} +} + +typealias ErrorA = T.X.Y + +struct ErrorB : ErrorP { // expected-error {{type 'ErrorB' does not conform to protocol 'ErrorP'}} + typealias X = ErrorC // expected-note {{possibly intended match 'ErrorB.X' (aka 'ErrorC') does not conform to 'ErrorQ'}} +} + +struct ErrorC { + typealias Y = Int +} + +typealias Y = ErrorA + +typealias Id = T + +extension Id {} // expected-error {{non-nominal type 'Id' cannot be extended}} + +class OuterGeneric { + typealias Alias = AnotherGeneric + // expected-note@-1 {{generic type 'Alias' declared here}} + class InnerNonGeneric : Alias {} + // expected-error@-1 {{reference to generic type 'OuterGeneric.Alias' requires arguments in <...>}} +} + +class AnotherGeneric {} + +// +// Generic typealiases in protocols +// + +protocol P { + associatedtype A + typealias G1 = MyType + typealias G2 = MyType + typealias G3 = () -> () + typealias G4 = (T) -> () + + func firstRequirement(_: G1) + func secondRequirement(_: G2) + func thirdRequirement(_: G3) + func fourthRequirement(_: G4) + + func firstRequirementGeneric(_: G1) + func secondRequirementGeneric(_: G2) + func thirdRequirementGeneric(_: G3, _: T) + func fourthRequirementGeneric(_: G4) +} + +struct S : P { + typealias A = Float + + func shouldFail(fn: (Int) -> ()) { + thirdRequirement(fn) + // expected-error@-1 {{cannot convert value of type '(Int) -> ()' to expected argument type '() -> ()'}} + } + + func firstRequirement(_: G1) {} + func secondRequirement(_: G2) {} + func thirdRequirement(_: G3) {} + func fourthRequirement(_: G4) {} + + func firstRequirementGeneric(_: G1) { + _ = G1.self + } + + func secondRequirementGeneric(_: G2) { + _ = G2.self + } + + func thirdRequirementGeneric(_: G3, _: T) { + _ = G3.self + } + + func fourthRequirementGeneric(_: G4) { + _ = G4.self + } + + func expressionContext() { + let _: G1 = MyType(a: S(), b: 3) + let _: G1 = MyType(a: S(), b: 3) + + let _: S.G1 = MyType(a: S(), b: 3) + let _: S.G1 = MyType(a: S(), b: 3) + + let _: G2 = MyType(a: 3, b: 1.0) + let _: G2 = MyType(a: 3, b: 1.0) + + let _: S.G2 = MyType(a: 3, b: 1.0) + let _: S.G2 = MyType(a: 3, b: 1.0) + } +} + +func takesMyType(x: MyType) {} + +func takesMyType(y: MyType) {} + +func f(x: S.G1, y: S.G2) { + takesMyType(x: x) + takesMyType(y: y) +} + +// +// Generic typealiases with requirements +// + +typealias Element = S.Iterator.Element where S : Sequence + +func takesInt(_: Element<[Int]>) {} + +takesInt(10) + +func failsRequirementCheck(_: Element) {} +// expected-error@-1 {{type 'Int' does not conform to protocol 'Sequence'}} + +// +// Sugar in base types of a typealias. +// +struct X { + typealias GY = [V] +} + +typealias GX = X + +func testSugar(_ gx: GX, _ gy: GX.GY, gz: GX.GY.Element) { + let i: Int = gx // expected-error{{cannot convert value of type 'GX' (aka 'X') to specified type 'Int'}} + let i2: Int = gy // expected-error{{cannot convert value of type 'GX.GY' (aka 'Array') to specified type 'Int'}} + let i3: Int = gz // expected-error{{cannot convert value of type 'GX.GY.Element' (aka 'Double') to specified type 'Int'}} +} diff --git a/test/decl/var/variables.swift b/test/decl/var/variables.swift index f2b5d0190c4b1..b470686ca0819 100644 --- a/test/decl/var/variables.swift +++ b/test/decl/var/variables.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-parser-lookup var t1 : Int var t2 = 10 @@ -14,17 +14,30 @@ var bfx : Int, bfy : Int _ = 10 -var self1 = self1 // expected-error {{variable used within its own initial value}} -var self2 : Int = self2 // expected-error {{variable used within its own initial value}} -var (self3) : Int = self3 // expected-error {{variable used within its own initial value}} -var (self4) : Int = self4 // expected-error {{variable used within its own initial value}} -var self5 = self5 + self5 // expected-error 2 {{variable used within its own initial value}} -var self6 = !self6 // expected-error {{variable used within its own initial value}} -var (self7a, self7b) = (self7b, self7a) // expected-error 2 {{variable used within its own initial value}} +var self1 = self1 +// expected-note@-1 2{{through reference here}} +// expected-error@-2 {{circular reference}} + +var self2 : Int = self2 +var (self3) : Int = self3 +var (self4) : Int = self4 + +var self5 = self5 + self5 +// expected-note@-1 2{{through reference here}} +// expected-error@-2 {{circular reference}} + +var self6 = !self6 +// expected-note@-1 2{{through reference here}} +// expected-error@-2 {{circular reference}} + +var (self7a, self7b) = (self7b, self7a) +// expected-note@-1 2{{through reference here}} +// expected-error@-2 {{circular reference}} var self8 = 0 func testShadowing() { - var self8 = self8 // expected-error {{variable used within its own initial value}} + var self8 = self8 + // expected-warning@-1 {{initialization of variable 'self8' was never used; consider replacing with assignment to '_' or removing it}} } var (paren) = 0 diff --git a/test/decl/var/variables_parser_lookup.swift b/test/decl/var/variables_parser_lookup.swift new file mode 100644 index 0000000000000..8b2fa5c2aa080 --- /dev/null +++ b/test/decl/var/variables_parser_lookup.swift @@ -0,0 +1,121 @@ +// RUN: %target-typecheck-verify-swift -enable-parser-lookup + +var t1 : Int +var t2 = 10 +var t3 = 10, t4 = 20.0 +var (t5, t6) = (10, 20.0) +var t7, t8 : Int +var t9, t10 = 20 // expected-error {{type annotation missing in pattern}} +var t11, t12 : Int = 20 // expected-error {{type annotation missing in pattern}} +var t13 = 2.0, t14 : Int +var (x = 123, // expected-error {{expected ',' separator}} {{7-7=,}} expected-error {{expected pattern}} + y = 456) : (Int,Int) +var bfx : Int, bfy : Int + +_ = 10 + +var self1 = self1 // expected-error {{variable used within its own initial value}} +var self2 : Int = self2 // expected-error {{variable used within its own initial value}} +var (self3) : Int = self3 // expected-error {{variable used within its own initial value}} +var (self4) : Int = self4 // expected-error {{variable used within its own initial value}} +var self5 = self5 + self5 // expected-error 2 {{variable used within its own initial value}} +var self6 = !self6 // expected-error {{variable used within its own initial value}} +var (self7a, self7b) = (self7b, self7a) // expected-error 2 {{variable used within its own initial value}} + +var self8 = 0 +func testShadowing() { + var self8 = self8 // expected-error {{variable used within its own initial value}} +} + +var (paren) = 0 +var paren2: Int = paren + +struct Broken { + var b : Bool = True // expected-error{{cannot find 'True' in scope}} +} + +// rdar://16252090 - Warning when inferring empty tuple type for declarations +var emptyTuple = testShadowing() // expected-warning {{variable 'emptyTuple' inferred to have type '()'}} \ + // expected-note {{add an explicit type annotation to silence this warning}} {{15-15=: ()}} + +// rdar://15263687 - Diagnose variables inferenced to 'AnyObject' +var ao1 : AnyObject +var ao2 = ao1 + +var aot1 : AnyObject.Type +var aot2 = aot1 // expected-warning {{variable 'aot2' inferred to have type 'AnyObject.Type', which may be unexpected}} \ + // expected-note {{add an explicit type annotation to silence this warning}} {{9-9=: AnyObject.Type}} + + +for item in [AnyObject]() { // No warning in for-each loop. + _ = item +} + + +// Type inference of _Nil very coherent but kind of useless +var ptr = nil // expected-error {{'nil' requires a contextual type}} + +func testAnyObjectOptional() -> AnyObject? { + let x = testAnyObjectOptional() + return x +} + +// SR-11511 Warning for inferring an array of empty tuples +var arrayOfEmptyTuples = [""].map { print($0) } // expected-warning {{variable 'arrayOfEmptyTuples' inferred to have type '[()]'}} \ + // expected-note {{add an explicit type annotation to silence this warning}} {{23-23=: [()]}} + +var maybeEmpty = Optional(arrayOfEmptyTuples) // expected-warning {{variable 'maybeEmpty' inferred to have type '[()]?'}} \ + // expected-note {{add an explicit type annotation to silence this warning}} {{15-15=: [()]?}} + +var shouldWarnWithoutSugar = (arrayOfEmptyTuples as Array<()>) // expected-warning {{variable 'shouldWarnWithoutSugar' inferred to have type 'Array<()>'}} \ + // expected-note {{add an explicit type annotation to silence this warning}} {{27-27=: Array<()>}} + +class SomeClass {} + +// weak let's should be rejected +weak let V = SomeClass() // expected-error {{'weak' must be a mutable variable, because it may change at runtime}} + +let a = b ; let b = a +// expected-error@-1 {{circular reference}} +// expected-note@-2 {{through reference here}} +// expected-note@-3 {{through reference here}} +// expected-note@-4 {{through reference here}} +// expected-note@-5 {{through reference here}} +// expected-note@-6 {{through reference here}} + +// Swift should warn about immutable default initialized values +let uselessValue : String? + + +func tuplePatternDestructuring(_ x : Int, y : Int) { + let (b: _, a: h) = (b: x, a: y) + _ = h + + // Destructuring tuple with labels doesn't work + let (i, j) = (b: x, a: y) + _ = i+j + + // QoI: type variable reconstruction failing for tuple types + let (x: g1, a: h1) = (b: x, a: y) // expected-error {{cannot convert value of type '(b: Int, a: Int)' to specified type '(x: Int, a: Int)'}} +} + +// Crash while compiling attached test-app. +func test21057425() -> (Int, Int) { + let x: Int = "not an int!", y = 0 // expected-error{{cannot convert value of type 'String' to specified type 'Int'}} + return (x, y) +} + +// rdar://problem/21081340 +func test21081340() { + func foo() { } + let (x: a, y: b): () = foo() // expected-error{{tuple pattern has the wrong length for tuple type '()'}} +} + +// Swift let late initialization in top level control flow statements +if true { + let s : Int + s = 42 // should be valid. + _ = s +} + + diff --git a/test/diagnostics/Localization/en_localization.swift b/test/diagnostics/Localization/en_localization.swift index 6c9842f1d1df9..605c00dcaccf1 100644 --- a/test/diagnostics/Localization/en_localization.swift +++ b/test/diagnostics/Localization/en_localization.swift @@ -1,8 +1,11 @@ -// RUN: %target-typecheck-verify-swift -localization-path %S/Inputs -locale en +// RUN: %target-typecheck-verify-swift -localization-path %S/Inputs -locale en -disable-parser-lookup _ = "HI! // expected-error@-1{{unterminated string literal}} -var self1 = self1 // expected-error {{variable used within its own initial value}} +var self1 = self1 +// expected-note@-1 2{{through reference here}} +// expected-error@-2 {{circular reference}} + struct Broken { var b : Bool = True // expected-error{{cannot find 'True' in scope}} } diff --git a/test/diagnostics/Localization/en_localization_parser_lookup.swift b/test/diagnostics/Localization/en_localization_parser_lookup.swift new file mode 100644 index 0000000000000..dd5ed8f861127 --- /dev/null +++ b/test/diagnostics/Localization/en_localization_parser_lookup.swift @@ -0,0 +1,9 @@ +// RUN: %target-typecheck-verify-swift -localization-path %S/Inputs -locale en -enable-parser-lookup + +_ = "HI! +// expected-error@-1{{unterminated string literal}} +var self1 = self1 // expected-error {{variable used within its own initial value}} +struct Broken { + var b : Bool = True // expected-error{{cannot find 'True' in scope}} +} +var v1 : Int[1 // expected-error {{expected ']' in array type}} expected-note {{to match this opening '['}} diff --git a/test/diagnostics/Localization/fr_localization.swift b/test/diagnostics/Localization/fr_localization.swift index 9caf88a529e37..c2acde077a92a 100644 --- a/test/diagnostics/Localization/fr_localization.swift +++ b/test/diagnostics/Localization/fr_localization.swift @@ -1,12 +1,17 @@ // RUN: %empty-directory(%t) // RUN: swift-serialize-diagnostics --input-file-path=%S/Inputs/fr.yaml --output-directory=%t/ // RUN: swift-serialize-diagnostics --input-file-path=%S/Inputs/en.yaml --output-directory=%t/ 2>&1 | %FileCheck %s -// RUN: %target-typecheck-verify-swift -localization-path %t -locale fr +// RUN: %target-typecheck-verify-swift -localization-path %t -locale fr -disable-parser-lookup // CHECK: These diagnostic IDs are no longer availiable: 'not_available_in_def, not_available_in_def_2, not_available_in_def_3, not_available_in_def_4, not_available_in_def_5' _ = "HI! // expected-error@-1{{chaîne non terminée littérale}} -var self1 = self1 // expected-error {{variable utilisée dans sa propre valeur initiale}} + +// FIXME: This used to produce a localized diagnostic. + +var self1 = self1 // expected-note 2{{through reference here}} +// expected-error@-1 {{circular reference}} + struct Broken { var b : Bool = True // expected-error{{impossible de trouver 'True' portée}} } diff --git a/test/diagnostics/Localization/fr_localization_parser_lookup.swift b/test/diagnostics/Localization/fr_localization_parser_lookup.swift new file mode 100644 index 0000000000000..b46efe97f06c2 --- /dev/null +++ b/test/diagnostics/Localization/fr_localization_parser_lookup.swift @@ -0,0 +1,13 @@ +// RUN: %empty-directory(%t) +// RUN: swift-serialize-diagnostics --input-file-path=%S/Inputs/fr.yaml --output-directory=%t/ +// RUN: swift-serialize-diagnostics --input-file-path=%S/Inputs/en.yaml --output-directory=%t/ 2>&1 | %FileCheck %s +// RUN: %target-typecheck-verify-swift -localization-path %t -locale fr -enable-parser-lookup + +// CHECK: These diagnostic IDs are no longer availiable: 'not_available_in_def, not_available_in_def_2, not_available_in_def_3, not_available_in_def_4, not_available_in_def_5' +_ = "HI! +// expected-error@-1{{chaîne non terminée littérale}} +var self1 = self1 // expected-error {{variable utilisée dans sa propre valeur initiale}} +struct Broken { + var b : Bool = True // expected-error{{impossible de trouver 'True' portée}} +} +var v1 : Int[1 // expected-error {{expected ']' in array type}} expected-note {{to match this opening '['}} diff --git a/test/diagnostics/Localization/no_localization_files_and_wrong_path.swift b/test/diagnostics/Localization/no_localization_files_and_wrong_path.swift index f2a27dcf7b3f5..2184a5d762fc5 100644 --- a/test/diagnostics/Localization/no_localization_files_and_wrong_path.swift +++ b/test/diagnostics/Localization/no_localization_files_and_wrong_path.swift @@ -1,11 +1,14 @@ -// RUN: %target-typecheck-verify-swift -localization-path /Not_exsisting_path -locale en +// RUN: %target-typecheck-verify-swift -localization-path /Not_exsisting_path -locale en -disable-parser-lookup // :0: warning: cannot find translations for 'en' at '/Not_exsisting_path/en.yaml': no such file // :0: warning: specified localization directory '/Not_exsisting_path' does not exist, translation is disabled _ = "HI! // expected-error@-1{{unterminated string literal}} -var self1 = self1 // expected-error {{variable used within its own initial value}} +var self1 = self1 +// expected-note@-1 2{{through reference here}} +// expected-error@-2 {{circular reference}} + struct Broken { var b : Bool = True // expected-error{{cannot find 'True' in scope}} } diff --git a/test/diagnostics/Localization/no_localization_files_and_wrong_path_parser_lookup.swift b/test/diagnostics/Localization/no_localization_files_and_wrong_path_parser_lookup.swift new file mode 100644 index 0000000000000..88633ab4f03b3 --- /dev/null +++ b/test/diagnostics/Localization/no_localization_files_and_wrong_path_parser_lookup.swift @@ -0,0 +1,11 @@ +// RUN: %target-typecheck-verify-swift -localization-path /Not_exsisting_path -locale en -enable-parser-lookup + +// :0: warning: cannot find translations for 'en' at '/Not_exsisting_path/en.yaml': no such file +// :0: warning: specified localization directory '/Not_exsisting_path' does not exist, translation is disabled + +_ = "HI! +// expected-error@-1{{unterminated string literal}} +var self1 = self1 // expected-error {{variable used within its own initial value}} +struct Broken { + var b : Bool = True // expected-error{{cannot find 'True' in scope}} +} diff --git a/test/expr/closure/closures.swift b/test/expr/closure/closures.swift index 90514b405d185..ad884058d150f 100644 --- a/test/expr/closure/closures.swift +++ b/test/expr/closure/closures.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-parser-lookup var func6 : (_ fn : (Int,Int) -> Int) -> () var func6a : ((Int, Int) -> Int) -> () @@ -116,9 +116,14 @@ func f0(_ a: Any) -> Int { return 1 } assert(f0(1) == 1) -var selfRef = { selfRef() } // expected-error {{variable used within its own initial value}} +var selfRef = { selfRef() } +// expected-note@-1 2{{through reference here}} +// expected-error@-2 {{circular reference}} +// expected-error@-3 {{unable to infer closure type in the current context}} + var nestedSelfRef = { - var recursive = { nestedSelfRef() } // expected-error {{variable used within its own initial value}} + var recursive = { nestedSelfRef() } + // expected-warning@-1 {{variable 'recursive' was never mutated; consider changing to 'let' constant}} recursive() } @@ -319,7 +324,7 @@ func testCaptureBehavior(_ ptr : SomeClass) { doStuff { [weak v1] in v1!.foo() } // expected-warning @+2 {{variable 'v1' was written to, but never read}} doStuff { [weak v1, // expected-note {{previous}} - weak v1] in v1!.foo() } // expected-error {{definition conflicts with previous value}} + weak v1] in v1!.foo() } // expected-error {{invalid redeclaration of 'v1'}} doStuff { [unowned v2] in v2.foo() } doStuff { [unowned(unsafe) v2] in v2.foo() } doStuff { [unowned(safe) v2] in v2.foo() } diff --git a/test/expr/closure/closures_parser_lookup.swift b/test/expr/closure/closures_parser_lookup.swift new file mode 100644 index 0000000000000..1ec9182c62d59 --- /dev/null +++ b/test/expr/closure/closures_parser_lookup.swift @@ -0,0 +1,526 @@ +// RUN: %target-typecheck-verify-swift -enable-parser-lookup + +var func6 : (_ fn : (Int,Int) -> Int) -> () +var func6a : ((Int, Int) -> Int) -> () +var func6b : (Int, (Int, Int) -> Int) -> () +func func6c(_ f: (Int, Int) -> Int, _ n: Int = 0) {} + + +// Expressions can be auto-closurified, so that they can be evaluated separately +// from their definition. +var closure1 : () -> Int = {4} // Function producing 4 whenever it is called. +var closure2 : (Int,Int) -> Int = { 4 } // expected-error{{contextual type for closure argument list expects 2 arguments, which cannot be implicitly ignored}} {{36-36= _,_ in}} +var closure3a : () -> () -> (Int,Int) = {{ (4, 2) }} // multi-level closing. +var closure3b : (Int,Int) -> (Int) -> (Int,Int) = {{ (4, 2) }} // expected-error{{contextual type for closure argument list expects 2 arguments, which cannot be implicitly ignored}} {{52-52=_,_ in }} +// expected-error@-1 {{contextual type for closure argument list expects 1 argument, which cannot be implicitly ignored}} {{53-53= _ in}} +var closure4 : (Int,Int) -> Int = { $0 + $1 } +var closure5 : (Double) -> Int = { + $0 + 1.0 + // expected-error@-1 {{cannot convert value of type 'Double' to closure result type 'Int'}} +} + +var closure6 = $0 // expected-error {{anonymous closure argument not contained in a closure}} + +var closure7 : Int = { 4 } // expected-error {{function produces expected type 'Int'; did you mean to call it with '()'?}} {{27-27=()}} // expected-note {{Remove '=' to make 'closure7' a computed property}}{{20-22=}} + +var capturedVariable = 1 +var closure8 = { [capturedVariable] in + capturedVariable += 1 // expected-error {{left side of mutating operator isn't mutable: 'capturedVariable' is an immutable capture}} +} + +func funcdecl1(_ a: Int, _ y: Int) {} +func funcdecl3() -> Int {} +func funcdecl4(_ a: ((Int) -> Int), _ b: Int) {} + +func funcdecl5(_ a: Int, _ y: Int) { + // Pass in a closure containing the call to funcdecl3. + funcdecl4({ funcdecl3() }, 12) // expected-error {{contextual type for closure argument list expects 1 argument, which cannot be implicitly ignored}} {{14-14= _ in}} + + + func6({$0 + $1}) // Closure with two named anonymous arguments + func6({($0) + $1}) // Closure with sequence expr inferred type + func6({($0) + $0}) // // expected-error {{contextual closure type '(Int, Int) -> Int' expects 2 arguments, but 1 was used in closure body}} + + + var testfunc : ((), Int) -> Int // expected-note {{'testfunc' declared here}} + testfunc({$0+1}) // expected-error {{missing argument for parameter #2 in call}} + // expected-error@-1 {{cannot convert value of type '(Int) -> Int' to expected argument type '()'}} + + funcdecl5(1, 2) // recursion. + + // Element access from a tuple. + var a : (Int, f : Int, Int) + var b = a.1+a.f + + // Tuple expressions with named elements. + var i : (y : Int, x : Int) = (x : 42, y : 11) // expected-warning {{expression shuffles the elements of this tuple; this behavior is deprecated}} + funcdecl1(123, 444) + + // Calls. + 4() // expected-error {{cannot call value of non-function type 'Int'}}{{4-6=}} + + + // rdar://12017658 - Infer some argument types from func6. + func6({ a, b -> Int in a+b}) + // Return type inference. + func6({ a,b in a+b }) + + // Infer incompatible type. + func6({a,b -> Float in 4.0 }) // expected-error {{declared closure result 'Float' is incompatible with contextual type 'Int'}} {{17-22=Int}} // Pattern doesn't need to name arguments. + func6({ _,_ in 4 }) + + func6({a,b in 4.0 }) // expected-error {{cannot convert value of type 'Double' to closure result type 'Int'}} + + // TODO: This diagnostic can be improved: rdar://22128205 + func6({(a : Float, b) in 4 }) // expected-error {{cannot convert value of type '(Float, Int) -> Int' to expected argument type '(Int, Int) -> Int'}} + + + + var fn = {} + var fn2 = { 4 } + + + var c : Int = { a,b -> Int in a+b} // expected-error{{cannot convert value of type '(Int, Int) -> Int' to specified type 'Int'}} + + +} + +func unlabeledClosureArgument() { + + func add(_ x: Int, y: Int) -> Int { return x + y } + func6a({$0 + $1}) // single closure argument + func6a(add) + func6b(1, {$0 + $1}) // second arg is closure + func6b(1, add) + func6c({$0 + $1}) // second arg is default int + func6c(add) +} + +// rdar://11935352 - closure with no body. +func closure_no_body(_ p: () -> ()) { + return closure_no_body({}) +} + + +// rdar://12019415 +func t() { + let u8 : UInt8 = 1 + let x : Bool = true + + if 0xA0..<0xBF ~= Int(u8) && x { + } +} + +// +func f0(_ a: Any) -> Int { return 1 } +assert(f0(1) == 1) + + +var selfRef = { selfRef() } // expected-error {{variable used within its own initial value}} +var nestedSelfRef = { + var recursive = { nestedSelfRef() } // expected-error {{variable used within its own initial value}} + recursive() +} + +var shadowed = { (shadowed: Int) -> Int in + let x = shadowed + return x +} // no-warning +var shadowedShort = { (shadowedShort: Int) -> Int in shadowedShort+1 } // no-warning + + +func anonymousClosureArgsInClosureWithArgs() { + func f(_: String) {} + var a1 = { () in $0 } // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments}} + var a2 = { () -> Int in $0 } // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments}} + var a3 = { (z: Int) in $0 } // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'z'?}} {{26-28=z}} + var a4 = { (z: [Int], w: [Int]) in + f($0.count) // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'z'?}} {{7-9=z}} expected-error {{cannot convert value of type 'Int' to expected argument type 'String'}} + f($1.count) // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'w'?}} {{7-9=w}} expected-error {{cannot convert value of type 'Int' to expected argument type 'String'}} + } + var a5 = { (_: [Int], w: [Int]) in + f($0.count) // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments}} + f($1.count) // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'w'?}} {{7-9=w}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'String'}} + } +} + +func doStuff(_ fn : @escaping () -> Int) {} +func doVoidStuff(_ fn : @escaping () -> ()) {} + +// Require specifying self for locations in code where strong reference cycles are likely +class ExplicitSelfRequiredTest { + var x = 42 + func method() -> Int { + // explicit closure requires an explicit "self." base or an explicit capture. + doVoidStuff({ self.x += 1 }) + doVoidStuff({ [self] in x += 1 }) + doVoidStuff({ [self = self] in x += 1 }) + doVoidStuff({ [unowned self] in x += 1 }) + doVoidStuff({ [unowned(unsafe) self] in x += 1 }) + doVoidStuff({ [unowned self = self] in x += 1 }) + + doStuff({ [self] in x+1 }) + doStuff({ [self = self] in x+1 }) + doStuff({ self.x+1 }) + doStuff({ [unowned self] in x+1 }) + doStuff({ [unowned(unsafe) self] in x+1 }) + doStuff({ [unowned self = self] in x+1 }) + doStuff({ x+1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} expected-note{{reference 'self.' explicitly}} {{15-15=self.}} + doVoidStuff({ doStuff({ x+1 })}) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{28-28= [self] in}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + doVoidStuff({ x += 1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{19-19=self.}} + doVoidStuff({ _ = "\(x)"}) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} + doVoidStuff({ [y = self] in x += 1 }) // expected-warning {{capture 'y' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{20-20=self, }} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} + doStuff({ [y = self] in x+1 }) // expected-warning {{capture 'y' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + doVoidStuff({ [weak self] in x += 1 }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + doStuff({ [weak self] in x+1 }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + doVoidStuff({ [self = self.x] in x += 1 }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + doStuff({ [self = self.x] in x+1 }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + + // Methods follow the same rules as properties, uses of 'self' without capturing must be marked with "self." + doStuff { method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} expected-note{{reference 'self.' explicitly}} {{15-15=self.}} + doVoidStuff { _ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{23-23=self.}} + doVoidStuff { _ = "\(method())" } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} + doVoidStuff { () -> () in _ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self]}} expected-note{{reference 'self.' explicitly}} {{35-35=self.}} + doVoidStuff { [y = self] in _ = method() } // expected-warning {{capture 'y' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{20-20=self, }} expected-note{{reference 'self.' explicitly}} {{37-37=self.}} + doStuff({ [y = self] in method() }) // expected-warning {{capture 'y' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + doVoidStuff({ [weak self] in _ = method() }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doStuff({ [weak self] in method() }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doVoidStuff({ [self = self.x] in _ = method() }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doStuff({ [self = self.x] in method() }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doVoidStuff { _ = self.method() } + doVoidStuff { [self] in _ = method() } + doVoidStuff { [self = self] in _ = method() } + doVoidStuff({ [unowned self] in _ = method() }) + doVoidStuff({ [unowned(unsafe) self] in _ = method() }) + doVoidStuff({ [unowned self = self] in _ = method() }) + + doStuff { self.method() } + doStuff { [self] in method() } + doStuff({ [self = self] in method() }) + doStuff({ [unowned self] in method() }) + doStuff({ [unowned(unsafe) self] in method() }) + doStuff({ [unowned self = self] in method() }) + + // When there's no space between the opening brace and the first expression, insert it + doStuff {method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in }} expected-note{{reference 'self.' explicitly}} {{14-14=self.}} + doVoidStuff {_ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in }} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + doVoidStuff {() -> () in _ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self]}} expected-note{{reference 'self.' explicitly}} {{34-34=self.}} + // With an empty capture list, insertion should should be suggested without a comma + doStuff { [] in method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self}} expected-note{{reference 'self.' explicitly}} {{21-21=self.}} + doStuff { [ ] in method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self}} expected-note{{reference 'self.' explicitly}} {{23-23=self.}} + doStuff { [ /* This space intentionally left blank. */ ] in method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self}} expected-note{{reference 'self.' explicitly}} {{65-65=self.}} + // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self}} + doStuff { [ // Nothing in this capture list! + ] + in + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{9-9=self.}} + } + // An inserted capture list should be on the same line as the opening brace, immediately following it. + // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} + doStuff { + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} + } + // expected-note@+2 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} + // Note: Trailing whitespace on the following line is intentional and should not be removed! + doStuff { + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} + } + // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} + doStuff { // We have stuff to do. + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} + } + // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} + doStuff {// We have stuff to do. + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} + } + + // String interpolation should offer the diagnosis and fix-its at the expected locations + doVoidStuff { _ = "\(method())" } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} + doVoidStuff { _ = "\(x+1)" } // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} + + // If we already have a capture list, self should be added to the list + let y = 1 + doStuff { [y] in method() } // expected-warning {{capture 'y' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + doStuff { [ // expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} + y // expected-warning {{capture 'y' was never used}} + ] in method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{14-14=self.}} + + // "self." shouldn't be required in the initializer expression in a capture list + // This should not produce an error, "x" isn't being captured by the closure. + doStuff({ [myX = x] in myX }) + + // This should produce an error, since x is used within the inner closure. + doStuff({ [myX = {x}] in 4 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{23-23= [self] in }} expected-note{{reference 'self.' explicitly}} {{23-23=self.}} + // expected-warning @-1 {{capture 'myX' was never used}} + + return 42 + } +} + +// If the implicit self is of value type, no diagnostic should be produced. +struct ImplicitSelfAllowedInStruct { + var x = 42 + mutating func method() -> Int { + doStuff({ x+1 }) + doVoidStuff({ x += 1 }) + doStuff({ method() }) + doVoidStuff({ _ = method() }) + } + + func method2() -> Int { + doStuff({ x+1 }) + doVoidStuff({ _ = x+1 }) + doStuff({ method2() }) + doVoidStuff({ _ = method2() }) + } +} + +enum ImplicitSelfAllowedInEnum { + case foo + var x: Int { 42 } + mutating func method() -> Int { + doStuff({ x+1 }) + doVoidStuff({ _ = x+1 }) + doStuff({ method() }) + doVoidStuff({ _ = method() }) + } + + func method2() -> Int { + doStuff({ x+1 }) + doVoidStuff({ _ = x+1 }) + doStuff({ method2() }) + doVoidStuff({ _ = method2() }) + } +} + + +class SomeClass { + var field : SomeClass? + func foo() -> Int {} +} + +func testCaptureBehavior(_ ptr : SomeClass) { + // Test normal captures. + weak var wv : SomeClass? = ptr + unowned let uv : SomeClass = ptr + unowned(unsafe) let uv1 : SomeClass = ptr + unowned(safe) let uv2 : SomeClass = ptr + doStuff { wv!.foo() } + doStuff { uv.foo() } + doStuff { uv1.foo() } + doStuff { uv2.foo() } + + + // Capture list tests + let v1 : SomeClass? = ptr + let v2 : SomeClass = ptr + + doStuff { [weak v1] in v1!.foo() } + // expected-warning @+2 {{variable 'v1' was written to, but never read}} + doStuff { [weak v1, // expected-note {{previous}} + weak v1] in v1!.foo() } // expected-error {{definition conflicts with previous value}} + doStuff { [unowned v2] in v2.foo() } + doStuff { [unowned(unsafe) v2] in v2.foo() } + doStuff { [unowned(safe) v2] in v2.foo() } + doStuff { [weak v1, weak v2] in v1!.foo() + v2!.foo() } + + let i = 42 + // expected-warning @+1 {{variable 'i' was never mutated}} + doStuff { [weak i] in i! } // expected-error {{'weak' may only be applied to class and class-bound protocol types, not 'Int'}} +} + +extension SomeClass { + func bar() { + doStuff { [unowned self] in self.foo() } + doStuff { [unowned xyz = self.field!] in xyz.foo() } + doStuff { [weak xyz = self.field] in xyz!.foo() } + + // rdar://16889886 - Assert when trying to weak capture a property of self in a lazy closure + // FIXME: We should probably offer a fix-it to the field capture error and suppress the 'implicit self' error. https://bugs.swift.org/browse/SR-11634 + doStuff { [weak self.field] in field!.foo() } // expected-error {{fields may only be captured by assigning to a specific name}} expected-error {{reference to property 'field' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}} {{36-36=self.}} expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} + // expected-warning @+1 {{variable 'self' was written to, but never read}} + doStuff { [weak self&field] in 42 } // expected-error {{expected ']' at end of capture list}} + + } + + func strong_in_capture_list() { + // QOI: "[strong self]" in capture list generates unhelpful error message + _ = {[strong self] () -> () in return } // expected-error {{expected 'weak', 'unowned', or no specifier in capture list}} + } +} + + +// Observed variable in a closure triggers an assertion +var closureWithObservedProperty: () -> () = { + var a: Int = 42 { // expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} + willSet { + _ = "Will set a to \(newValue)" + } + didSet { + _ = "Did set a with old value of \(oldValue)" + } + } +} + +; + +{}() // expected-error{{top-level statement cannot begin with a closure expression}} + + + +// rdar://19179412 - Crash on valid code. +func rdar19179412() -> (Int) -> Int { + return { x in + class A { + let d : Int = 0 + } + return 0 + } +} + +// Test coercion of single-expression closure return types to void. +func takesVoidFunc(_ f: () -> ()) {} +var i: Int = 1 + +// expected-warning @+1 {{expression of type 'Int' is unused}} +takesVoidFunc({i}) +// expected-warning @+1 {{expression of type 'Int' is unused}} +var f1: () -> () = {i} +var x = {return $0}(1) + +func returnsInt() -> Int { return 0 } +takesVoidFunc(returnsInt) // expected-error {{cannot convert value of type '() -> Int' to expected argument type '() -> ()'}} +takesVoidFunc({() -> Int in 0}) // expected-error {{declared closure result 'Int' is incompatible with contextual type '()'}} {{22-25=()}} + +// These used to crash the compiler, but were fixed to support the implementation of rdar://problem/17228969 +Void(0) // expected-error{{argument passed to call that takes no arguments}} +_ = {0} + +// "multi-statement closures require an explicit return type" should be an error not a note +let samples = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{16-16= () -> <#Result#> in }} + if (i > 10) { return true } + else { return false } + }() + +// Swift error: cannot capture '$0' before it is declared +func f(_ fp : (Bool, Bool) -> Bool) {} +f { $0 && !$1 } + + +// unexpected error on self. capture inside class method +func TakesIntReturnsVoid(_ fp : ((Int) -> ())) {} + +struct TestStructWithStaticMethod { + static func myClassMethod(_ count: Int) { + // Shouldn't require "self." + TakesIntReturnsVoid { _ in myClassMethod(0) } + } +} + +class TestClassWithStaticMethod { + class func myClassMethod(_ count: Int) { + // Shouldn't require "self." + TakesIntReturnsVoid { _ in myClassMethod(0) } + } +} + +// Test that we can infer () as the result type of these closures. +func genericOne(_ a: () -> T) {} +func genericTwo(_ a: () -> T, _ b: () -> T) {} +genericOne {} +genericTwo({}, {}) + + +// QoI: Warning for unused capture list variable should be customized +class r22344208 { + func f() { + let q = 42 + let _: () -> Int = { + [unowned self, // expected-warning {{capture 'self' was never used}} + q] in // expected-warning {{capture 'q' was never used}} + 1 } + } +} + +var f = { (s: Undeclared) -> Int in 0 } // expected-error {{cannot find type 'Undeclared' in scope}} + +// Swift compiler crashes when using closure, declared to return illegal type. +func r21375863() { + var width = 0 // expected-warning {{variable 'width' was never mutated}} + var height = 0 // expected-warning {{variable 'height' was never mutated}} + var bufs: [[UInt8]] = (0..<4).map { _ -> [asdf] in // expected-error {{cannot find type 'asdf' in scope}} expected-warning {{variable 'bufs' was never used}} + [UInt8](repeating: 0, count: width*height) + } +} + +// +// Don't crash if we infer a closure argument to have a tuple type containing inouts. +func r25993258_helper(_ fn: (inout Int, Int) -> ()) {} +func r25993258a() { + r25993258_helper { x in () } // expected-error {{contextual closure type '(inout Int, Int) -> ()' expects 2 arguments, but 1 was used in closure body}} +} +func r25993258b() { + r25993258_helper { _ in () } // expected-error {{contextual closure type '(inout Int, Int) -> ()' expects 2 arguments, but 1 was used in closure body}} +} + +// We have to map the captured var type into the right generic environment. +class GenericClass {} + +func lvalueCapture(c: GenericClass) { + var cc = c + weak var wc = c + + func innerGeneric(_: U) { + _ = cc + _ = wc + + cc = wc! + } +} + +// Don't expose @lvalue-ness in diagnostics. +let closure = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{16-16= () -> <#Result#> in }} + var helper = true + return helper +} + +// SR-9839 +func SR9839(_ x: @escaping @convention(block) () -> Void) {} + +func id(_ x: T) -> T { + return x +} + +var qux: () -> Void = {} + +SR9839(qux) +SR9839(id(qux)) // expected-error {{conflicting arguments to generic parameter 'T' ('() -> Void' vs. '@convention(block) () -> Void')}} + +func forceUnwrap(_ x: T?) -> T { + return x! +} + +var qux1: (() -> Void)? = {} + +SR9839(qux1!) +SR9839(forceUnwrap(qux1)) + +// rdar://problem/65155671 - crash referencing parameter of outer closure +func rdar65155671(x: Int) { + { a in + _ = { [a] in a } + }(x) +} + +func sr3186(_ f: (@escaping (@escaping (T) -> U) -> ((T) -> U))) -> ((T) -> U) { + return { x in return f(sr3186(f))(x) } +} + +class SR3186 { + init() { + // expected-warning@+1{{capture 'self' was never used}} + let v = sr3186 { f in { [unowned self, f] x in x != 1000 ? f(x + 1) : "success" } }(0) + print("\(v)") + } +} diff --git a/test/expr/expressions.swift b/test/expr/expressions.swift index 4479f9a74fc15..d4dcbe24a27d1 100644 --- a/test/expr/expressions.swift +++ b/test/expr/expressions.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-parser-lookup //===----------------------------------------------------------------------===// // Tests and samples. @@ -246,15 +246,16 @@ func test_as_2() { func test_lambda() { // A simple closure. var a = { (value: Int) -> () in markUsed(value+1) } + // expected-warning@-1 {{initialization of variable 'a' was never used; consider replacing with assignment to '_' or removing it}} // A recursive lambda. - // FIXME: This should definitely be accepted. var fib = { (n: Int) -> Int in + // expected-warning@-1 {{variable 'fib' was never mutated; consider changing to 'let' constant}} if (n < 2) { return n } - return fib(n-1)+fib(n-2) // expected-error 2 {{variable used within its own initial value}} + return fib(n-1)+fib(n-2) } } diff --git a/test/expr/expressions_parser_lookup.swift b/test/expr/expressions_parser_lookup.swift new file mode 100644 index 0000000000000..582d59d7d5d12 --- /dev/null +++ b/test/expr/expressions_parser_lookup.swift @@ -0,0 +1,947 @@ +// RUN: %target-typecheck-verify-swift -enable-parser-lookup + +//===----------------------------------------------------------------------===// +// Tests and samples. +//===----------------------------------------------------------------------===// + +// Comment. With unicode characters: ¡ç®åz¥! + +func markUsed(_: T) {} + +// Various function types. +var func1 : () -> () // No input, no output. +var func2 : (Int) -> Int +var func3 : () -> () -> () // Takes nothing, returns a fn. +var func3a : () -> (() -> ()) // same as func3 +var func6 : (_ fn : (Int,Int) -> Int) -> () // Takes a fn, returns nothing. +var func7 : () -> (Int,Int,Int) // Takes nothing, returns tuple. + +// Top-Level expressions. These are 'main' content. +func1() +_ = 4+7 + +var bind_test1 : () -> () = func1 +var bind_test2 : Int = 4; func1 // expected-error {{expression resolves to an unused variable}} + +(func1, func2) // expected-error {{expression resolves to an unused variable}} + +func basictest() { + // Simple integer variables. + var x : Int + var x2 = 4 // Simple Type inference. + var x3 = 4+x*(4+x2)/97 // Basic Expressions. + + // Declaring a variable Void, aka (), is fine too. + var v : Void + + var x4 : Bool = true + var x5 : Bool = + 4 // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} + + //var x6 : Float = 4+5 + + var x7 = 4; 5 // expected-warning {{integer literal is unused}} + + // Test implicit conversion of integer literal to non-Int64 type. + var x8 : Int8 = 4 + x8 = x8 + 1 + _ = x8 + 1 + _ = 0 + x8 + 1.0 + x8 // expected-error{{binary operator '+' cannot be applied to operands of type 'Double' and 'Int8'}} + // expected-note @-1 {{overloads for '+' exist with these partially matching parameter lists:}} + + + var x9 : Int16 = x8 + 1 // expected-error {{cannot convert value of type 'Int8' to specified type 'Int16'}} + + // Various tuple types. + var tuple1 : () + var tuple2 : (Int) + var tuple3 : (Int, Int, ()) + var tuple2a : (a : Int) // expected-error{{cannot create a single-element tuple with an element label}}{{18-22=}} + var tuple3a : (a : Int, b : Int, c : ()) + + var tuple4 = (1, 2) // Tuple literal. + var tuple5 = (1, 2, 3, 4) // Tuple literal. + var tuple6 = (1 2) // expected-error {{expected ',' separator}} {{18-18=,}} + + // Brace expressions. + var brace3 = { + var brace2 = 42 // variable shadowing. + _ = brace2+7 + } + + // Function calls. + var call1 : () = func1() + var call2 = func2(1) + var call3 : () = func3()() + + // Cannot call an integer. + bind_test2() // expected-error {{cannot call value of non-function type 'Int'}}{{13-15=}} +} + +// +func testUnusedLiterals_SR3522() { + 42 // expected-warning {{integer literal is unused}} + 2.71828 // expected-warning {{floating-point literal is unused}} + true // expected-warning {{boolean literal is unused}} + false // expected-warning {{boolean literal is unused}} + "Hello" // expected-warning {{string literal is unused}} + "Hello \(42)" // expected-warning {{string literal is unused}} + #file // expected-warning {{#file literal is unused}} + (#line) // expected-warning {{#line literal is unused}} + #column // expected-warning {{#column literal is unused}} + #function // expected-warning {{#function literal is unused}} + #dsohandle // expected-warning {{#dsohandle literal is unused}} + __FILE__ // expected-error {{__FILE__ has been replaced with #file in Swift 3}} expected-warning {{#file literal is unused}} + __LINE__ // expected-error {{__LINE__ has been replaced with #line in Swift 3}} expected-warning {{#line literal is unused}} + __COLUMN__ // expected-error {{__COLUMN__ has been replaced with #column in Swift 3}} expected-warning {{#column literal is unused}} + __FUNCTION__ // expected-error {{__FUNCTION__ has been replaced with #function in Swift 3}} expected-warning {{#function literal is unused}} + __DSO_HANDLE__ // expected-error {{__DSO_HANDLE__ has been replaced with #dsohandle in Swift 3}} expected-warning {{#dsohandle literal is unused}} + + nil // expected-error {{'nil' requires a contextual type}} + #fileLiteral(resourceName: "what.txt") // expected-error {{could not infer type of file reference literal}} expected-note * {{}} + #imageLiteral(resourceName: "hello.png") // expected-error {{could not infer type of image literal}} expected-note * {{}} + #colorLiteral(red: 1, green: 0, blue: 0, alpha: 1) // expected-error {{could not infer type of color literal}} expected-note * {{}} +} + +// Infix operators and attribute lists. +infix operator %% : MinPrecedence +precedencegroup MinPrecedence { + associativity: left + lowerThan: AssignmentPrecedence +} + +func %%(a: Int, b: Int) -> () {} +var infixtest : () = 4 % 2 + 27 %% 123 + + + +// The 'func' keyword gives a nice simplification for function definitions. +func funcdecl1(_ a: Int, _ y: Int) {} +func funcdecl2() { + return funcdecl1(4, 2) +} +func funcdecl3() -> Int { + return 12 +} +func funcdecl4(_ a: ((Int) -> Int), b: Int) {} +func signal(_ sig: Int, f: (Int) -> Void) -> (Int) -> Void {} + +// Doing fun things with named arguments. Basic stuff first. +func funcdecl6(_ a: Int, b: Int) -> Int { return a+b } + +// Can dive into tuples, 'b' is a reference to a whole tuple, c and d are +// fields in one. Cannot dive into functions or through aliases. +func funcdecl7(_ a: Int, b: (c: Int, d: Int), third: (c: Int, d: Int)) -> Int { + _ = a + b.0 + b.c + third.0 + third.1 + b.foo // expected-error {{value of tuple type '(c: Int, d: Int)' has no member 'foo'}} +} + +// Error recovery. +func testfunc2 (_: ((), Int) -> Int) -> Int {} +func makeTuple() -> (String, Int) { return ("foo", 42) } +func errorRecovery() { + testfunc2({ $0 + 1 }) // expected-error {{contextual closure type '((), Int) -> Int' expects 2 arguments, but 1 was used in closure body}} + // expected-error@-1 {{cannot convert value of type '()' to expected argument type 'Int'}} + + enum union1 { + case bar + case baz + } + var a: Int = .hello // expected-error {{type 'Int' has no member 'hello'}} + var b: union1 = .bar // ok + var c: union1 = .xyz // expected-error {{type 'union1' has no member 'xyz'}} + var d: (Int,Int,Int) = (1,2) // expected-error {{'(Int, Int)' is not convertible to '(Int, Int, Int)', tuples have a different number of elements}} + var e: (Int,Int) = (1, 2, 3) // expected-error {{'(Int, Int, Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}} + var f: (Int,Int) = (1, 2, f : 3) // expected-error {{'(Int, Int, f: Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}} + + // CrashTracer: [USER] swift at …mous_namespace::ConstraintGenerator::getTypeForPattern + 698 + var (g1, g2, g3) = (1, 2) // expected-error {{'(Int, Int)' is not convertible to '(Int, Int, _)', tuples have a different number of elements}} + var (h1, h2) = (1, 2, 3) // expected-error {{'(Int, Int, Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}} + var i: (Bool, Bool) = makeTuple() // expected-error {{cannot convert value of type '(String, Int)' to specified type '(Bool, Bool)'}} +} + +func acceptsInt(_ x: Int) {} +acceptsInt(unknown_var) // expected-error {{cannot find 'unknown_var' in scope}} + + + +var test1a: (Int) -> (Int) -> Int = { { $0 } } // expected-error{{contextual type for closure argument list expects 1 argument, which cannot be implicitly ignored}} {{38-38= _ in}} +var test1b = { 42 } +var test1c = { { 42 } } +var test1d = { { { 42 } } } + +func test2(_ a: Int, b: Int) -> (c: Int) { // expected-error{{cannot create a single-element tuple with an element label}} {{34-37=}} expected-note {{did you mean 'a'?}} expected-note {{did you mean 'b'?}} + _ = a+b + a+b+c // expected-error{{cannot find 'c' in scope}} + return a+b +} + + +func test3(_ arg1: Int, arg2: Int) -> Int { + return 4 +} + +func test4() -> ((_ arg1: Int, _ arg2: Int) -> Int) { + return test3 +} + +func test5() { + let a: (Int, Int) = (1,2) + var + _: ((Int) -> Int, Int) = a // expected-error {{cannot convert value of type '(Int, Int)' to specified type '((Int) -> Int, Int)'}} + + + let c: (a: Int, b: Int) = (1,2) + let _: (b: Int, a: Int) = c // expected-warning {{expression shuffles the elements of this tuple; this behavior is deprecated}} +} + + +// Functions can obviously take and return values. +func w3(_ a: Int) -> Int { return a } +func w4(_: Int) -> Int { return 4 } + + + +func b1() {} + +func foo1(_ a: Int, b: Int) -> Int {} +func foo2(_ a: Int) -> (_ b: Int) -> Int {} +func foo3(_ a: Int = 2, b: Int = 3) {} + +prefix operator ^^ + +prefix func ^^(a: Int) -> Int { + return a + 1 +} + +func test_unary1() { + var x: Int + + x = ^^(^^x) + x = *x // expected-error {{'*' is not a prefix unary operator}} + x = x* // expected-error {{'*' is not a postfix unary operator}} + x = +(-x) + x = + -x // expected-error {{unary operator cannot be separated from its operand}} {{8-9=}} +} +func test_unary2() { + var x: Int + // FIXME: second diagnostic is redundant. + x = &; // expected-error {{expected expression after unary operator}} expected-error {{expected expression in assignment}} +} +func test_unary3() { + var x: Int + // FIXME: second diagnostic is redundant. + x = &, // expected-error {{expected expression after unary operator}} expected-error {{expected expression in assignment}} +} + +func test_as_1() { + var _: Int +} +func test_as_2() { + let x: Int = 1 + x as [] // expected-error {{expected element type}} {{9-9= <#type#>}} +} + +func test_lambda() { + // A simple closure. + var a = { (value: Int) -> () in markUsed(value+1) } + + // A recursive lambda. + // FIXME: This should definitely be accepted. + var fib = { (n: Int) -> Int in + if (n < 2) { + return n + } + + return fib(n-1)+fib(n-2) // expected-error 2 {{variable used within its own initial value}} + } +} + +func test_lambda2() { + { () -> protocol in + // expected-error @-1 {{'protocol<...>' composition syntax has been removed and is not needed here}} {{11-24=Int}} + // expected-error @-2 {{non-protocol, non-class type 'Int' cannot be used within a protocol-constrained type}} + // expected-warning @-3 {{result of call to closure returning 'Any' is unused}} + return 1 + }() +} + +func test_floating_point() { + _ = 0.0 + _ = 100.1 + var _: Float = 0.0 + var _: Double = 0.0 +} + +func test_nonassoc(_ x: Int, y: Int) -> Bool { + // FIXME: the second error and note here should arguably disappear + return x == y == x // expected-error {{adjacent operators are in non-associative precedence group 'ComparisonPrecedence'}} expected-error {{binary operator '==' cannot be applied to operands of type 'Bool' and 'Int'}} + // expected-note@-1 {{overloads for '==' exist with these partially matching parameter lists: (Bool, Bool), (Int, Int)}} +} + +// More realistic examples. + +func fib(_ n: Int) -> Int { + if (n < 2) { + return n + } + + return fib(n-2) + fib(n-1) +} + +//===----------------------------------------------------------------------===// +// Integer Literals +//===----------------------------------------------------------------------===// + +// FIXME: Should warn about integer constants being too large +var + il_a: Bool = 4 // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} +var il_b: Int8 + = 123123 +var il_c: Int8 = 4 // ok + +struct int_test4 : ExpressibleByIntegerLiteral { + typealias IntegerLiteralType = Int + init(integerLiteral value: Int) {} // user type. +} + +var il_g: int_test4 = 4 + + + +// This just barely fits in Int64. +var il_i: Int64 = 18446744073709551615 + +// This constant is too large to fit in an Int64, but it is fine for Int128. +// FIXME: Should warn about the first. +var il_j: Int64 = 18446744073709551616 +// var il_k: Int128 = 18446744073709551616 + +var bin_literal: Int64 = 0b100101 +var hex_literal: Int64 = 0x100101 +var oct_literal: Int64 = 0o100101 + +// verify that we're not using C rules +var oct_literal_test: Int64 = 0123 +assert(oct_literal_test == 123) + +// ensure that we swallow random invalid chars after the first invalid char +var invalid_num_literal: Int64 = 0QWERTY // expected-error{{'Q' is not a valid digit in integer literal}} +var invalid_bin_literal: Int64 = 0bQWERTY // expected-error{{'Q' is not a valid binary digit (0 or 1) in integer literal}} +var invalid_hex_literal: Int64 = 0xQWERTY // expected-error{{'Q' is not a valid hexadecimal digit (0-9, A-F) in integer literal}} +var invalid_oct_literal: Int64 = 0oQWERTY // expected-error{{'Q' is not a valid octal digit (0-7) in integer literal}} +var invalid_exp_literal: Double = 1.0e+QWERTY // expected-error{{'Q' is not a valid digit in floating point exponent}} +var invalid_fp_exp_literal: Double = 0x1p+QWERTY // expected-error{{'Q' is not a valid digit in floating point exponent}} + +// don't emit a partial integer literal if the invalid char is valid for identifiers. +var invalid_num_literal_prefix: Int64 = 0a1234567 // expected-error{{'a' is not a valid digit in integer literal}} +var invalid_num_literal_middle: Int64 = 0123A5678 // expected-error{{'A' is not a valid digit in integer literal}} +var invalid_bin_literal_middle: Int64 = 0b1020101 // expected-error{{'2' is not a valid binary digit (0 or 1) in integer literal}} +var invalid_oct_literal_middle: Int64 = 0o1357864 // expected-error{{'8' is not a valid octal digit (0-7) in integer literal}} +var invalid_hex_literal_middle: Int64 = 0x147ADG0 // expected-error{{'G' is not a valid hexadecimal digit (0-9, A-F) in integer literal}} + +var invalid_hex_literal_exponent_ = 0xffp+12abc // expected-error{{'a' is not a valid digit in floating point exponent}} +var invalid_float_literal_exponent = 12e1abc // expected-error{{'a' is not a valid digit in floating point exponent}} + +// rdar://11088443 +var negative_int32: Int32 = -1 + +// +var tupleelemvar = 1 +markUsed((tupleelemvar, tupleelemvar).1) + +func int_literals() { + // Fits exactly in 64-bits - rdar://11297273 + _ = 1239123123123123 + // Overly large integer. + // FIXME: Should warn about it. + _ = 123912312312312312312 + +} + +// +func tuple_of_rvalues(_ a:Int, b:Int) -> Int { + return (a, b).1 +} + +extension Int { + func testLexingMethodAfterIntLiteral() {} + func _0() {} + // Hex letters + func ffa() {} + // Hex letters + non hex. + func describe() {} + // Hex letters + 'p'. + func eap() {} + // Hex letters + 'p' + non hex. + func fpValue() {} +} + +123.testLexingMethodAfterIntLiteral() +0b101.testLexingMethodAfterIntLiteral() +0o123.testLexingMethodAfterIntLiteral() +0x1FFF.testLexingMethodAfterIntLiteral() + +123._0() +0b101._0() +0o123._0() +0x1FFF._0() + +0x1fff.ffa() +0x1FFF.describe() +0x1FFF.eap() +0x1FFF.fpValue() + +var separator1: Int = 1_ +var separator2: Int = 1_000 +var separator4: Int = 0b1111_0000_ +var separator5: Int = 0b1111_0000 +var separator6: Int = 0o127_777_ +var separator7: Int = 0o127_777 +var separator8: Int = 0x12FF_FFFF +var separator9: Int = 0x12FF_FFFF_ + +//===----------------------------------------------------------------------===// +// Float Literals +//===----------------------------------------------------------------------===// + +var fl_a = 0.0 +var fl_b: Double = 1.0 +var fl_c: Float = 2.0 +// FIXME: crummy diagnostic +var fl_d: Float = 2.0.0 // expected-error {{expected named member of numeric literal}} +var fl_e: Float = 1.0e42 +var fl_f: Float = 1.0e+ // expected-error {{expected a digit in floating point exponent}} +var fl_g: Float = 1.0E+42 +var fl_h: Float = 2e-42 +var vl_i: Float = -.45 // expected-error {{'.45' is not a valid floating point literal; it must be written '0.45'}} {{20-20=0}} +var fl_j: Float = 0x1p0 +var fl_k: Float = 0x1.0p0 +var fl_l: Float = 0x1.0 // expected-error {{hexadecimal floating point literal must end with an exponent}} +var fl_m: Float = 0x1.FFFFFEP-2 +var fl_n: Float = 0x1.fffffep+2 +var fl_o: Float = 0x1.fffffep+ // expected-error {{expected a digit in floating point exponent}} +var fl_p: Float = 0x1p // expected-error {{expected a digit in floating point exponent}} +var fl_q: Float = 0x1p+ // expected-error {{expected a digit in floating point exponent}} +var fl_r: Float = 0x1.0fp // expected-error {{expected a digit in floating point exponent}} +var fl_s: Float = 0x1.0fp+ // expected-error {{expected a digit in floating point exponent}} +var fl_t: Float = 0x1.p // expected-error {{value of type 'Int' has no member 'p'}} +var fl_u: Float = 0x1.p2 // expected-error {{value of type 'Int' has no member 'p2'}} +var fl_v: Float = 0x1.p+ // expected-error {{'+' is not a postfix unary operator}} +var fl_w: Float = 0x1.p+2 // expected-error {{value of type 'Int' has no member 'p'}} + +var if1: Double = 1.0 + 4 // integer literal ok as double. +var if2: Float = 1.0 + 4 // integer literal ok as float. + +var fl_separator1: Double = 1_.2_ +var fl_separator2: Double = 1_000.2_ +var fl_separator3: Double = 1_000.200_001 +var fl_separator4: Double = 1_000.200_001e1_ +var fl_separator5: Double = 1_000.200_001e1_000 +var fl_separator6: Double = 1_000.200_001e1_000 +var fl_separator7: Double = 0x1_.0FFF_p1_ +var fl_separator8: Double = 0x1_0000.0FFF_ABCDp10_001 + +var fl_bad_separator1: Double = 1e_ // expected-error {{'_' is not a valid first character in floating point exponent}} +var fl_bad_separator2: Double = 0x1p_ // expected-error {{'_' is not a valid first character in floating point exponent}} + +//===----------------------------------------------------------------------===// +// String Literals +//===----------------------------------------------------------------------===// + +var st_a = "" +var st_b: String = "" +var st_c = "asdfasd // expected-error {{unterminated string literal}} + +var st_d = " \t\n\r\"\'\\ " // Valid simple escapes +var st_e = " \u{12}\u{0012}\u{00000078} " // Valid unicode escapes +var st_u1 = " \u{1} " +var st_u2 = " \u{123} " +var st_u3 = " \u{1234567} " // expected-error {{invalid unicode scalar}} +var st_u4 = " \q " // expected-error {{invalid escape sequence in literal}} + +var st_u5 = " \u{FFFFFFFF} " // expected-error {{invalid unicode scalar}} +var st_u6 = " \u{D7FF} \u{E000} " // Fencepost UTF-16 surrogate pairs. +var st_u7 = " \u{D800} " // expected-error {{invalid unicode scalar}} +var st_u8 = " \u{DFFF} " // expected-error {{invalid unicode scalar}} +var st_u10 = " \u{0010FFFD} " // Last valid codepoint, 0xFFFE and 0xFFFF are reserved in each plane +var st_u11 = " \u{00110000} " // expected-error {{invalid unicode scalar}} + +func stringliterals(_ d: [String: Int]) { + + // rdar://11385385 + let x = 4 + "Hello \(x+1) world" // expected-warning {{string literal is unused}} + + "Error: \(x+1"; // expected-error {{unterminated string literal}} + + "Error: \(x+1 // expected-error {{unterminated string literal}} + ; // expected-error {{';' statements are not allowed}} + + // rdar://14050788 [DF] String Interpolations can't contain quotes + "test \("nested")" + "test \("\("doubly nested")")" + "test \(d["hi"])" + "test \("quoted-paren )")" + "test \("quoted-paren (")" + "test \("\\")" + "test \("\n")" + "test \("\")" // expected-error {{unterminated string literal}} + + "test \ + // expected-error @-1 {{unterminated string literal}} expected-error @-1 {{invalid escape sequence in literal}} + "test \("\ + // expected-error @-1 {{unterminated string literal}} + "test newline \("something" + + "something else")" + // expected-error @-2 {{unterminated string literal}} expected-error @-1 {{unterminated string literal}} + + // expected-warning @+2 {{variable 'x2' was never used; consider replacing with '_' or removing it}} + // expected-error @+1 {{unterminated string literal}} + var x2 : () = ("hello" + " + ; +} + +func testSingleQuoteStringLiterals() { + _ = 'abc' // expected-error{{single-quoted string literal found, use '"'}}{{7-12="abc"}} + _ = 'abc' + "def" // expected-error{{single-quoted string literal found, use '"'}}{{7-12="abc"}} + + _ = 'ab\nc' // expected-error{{single-quoted string literal found, use '"'}}{{7-14="ab\\nc"}} + + _ = "abc\('def')" // expected-error{{single-quoted string literal found, use '"'}}{{13-18="def"}} + _ = 'ab\("c")' // expected-error{{single-quoted string literal found, use '"'}}{{7-17="ab\\("c")"}} + _ = 'a\('b')c' // expected-error{{single-quoted string literal found, use '"'}}{{7-17="a\\('b')c"}} + // expected-error@-1{{single-quoted string literal found, use '"'}}{{11-14="b"}} + + _ = "abc' // expected-error{{unterminated string literal}} + _ = 'abc" // expected-error{{unterminated string literal}} + _ = "a'c" + + _ = 'ab\'c' // expected-error{{single-quoted string literal found, use '"'}}{{7-14="ab'c"}} + + _ = 'ab"c' // expected-error{{single-quoted string literal found, use '"'}}{{7-13="ab\\"c"}} + _ = 'ab\"c' // expected-error{{single-quoted string literal found, use '"'}}{{7-14="ab\\"c"}} + _ = 'ab\\"c' // expected-error{{single-quoted string literal found, use '"'}}{{7-15="ab\\\\\\"c"}} +} + +// +var s = "" // expected-note {{did you mean 's'?}} +s.append(contentsOf: ["x"]) + +//===----------------------------------------------------------------------===// +// InOut arguments +//===----------------------------------------------------------------------===// + +func takesInt(_ x: Int) {} +func takesExplicitInt(_ x: inout Int) { } + +func testInOut(_ arg: inout Int) { + var x: Int + takesExplicitInt(x) // expected-error{{passing value of type 'Int' to an inout parameter requires explicit '&'}} {{20-20=&}} + takesExplicitInt(&x) + takesInt(&x) // expected-error{{'&' used with non-inout argument of type 'Int'}} + var y = &x //expected-error {{use of extraneous '&'}} + var z = &arg //expected-error {{use of extraneous '&'}} + + takesExplicitInt(5) // expected-error {{cannot pass immutable value as inout argument: literals are not mutable}} +} + +//===----------------------------------------------------------------------===// +// Conversions +//===----------------------------------------------------------------------===// + +var pi_f: Float +var pi_d: Double + +struct SpecialPi {} // Type with no implicit construction. + +var pi_s: SpecialPi + +func getPi() -> Float {} +func getPi() -> Double {} +func getPi() -> SpecialPi {} + +enum Empty { } + +extension Empty { + init(_ f: Float) { } +} + +func conversionTest(_ a: inout Double, b: inout Int) { + var f: Float + var d: Double + a = Double(b) + a = Double(f) + a = Double(d) // no-warning + b = Int(a) + f = Float(b) + + var pi_f1 = Float(pi_f) + var pi_d1 = Double(pi_d) + var pi_s1 = SpecialPi(pi_s) // expected-error {{argument passed to call that takes no arguments}} + + var pi_f2 = Float(getPi()) // expected-error {{ambiguous use of 'init(_:)'}} + var pi_d2 = Double(getPi()) // expected-error {{ambiguous use of 'init(_:)'}} + var pi_s2: SpecialPi = getPi() // no-warning + + var float = Float.self + var pi_f3 = float.init(getPi()) // expected-error {{ambiguous use of 'init(_:)'}} + var pi_f4 = float.init(pi_f) + + var e = Empty(f) // expected-warning {{variable 'e' inferred to have type 'Empty', which is an enum with no cases}} expected-note {{add an explicit type annotation to silence this warning}} {{8-8=: Empty}} + var e2 = Empty(d) // expected-error{{cannot convert value of type 'Double' to expected argument type 'Float'}} + var e3 = Empty(Float(d)) // expected-warning {{variable 'e3' inferred to have type 'Empty', which is an enum with no cases}} expected-note {{add an explicit type annotation to silence this warning}} {{9-9=: Empty}} +} + +// FIXME(diagnostics): This note is pointing to a synthesized init +struct Rule { // expected-note {{'init(target:dependencies:)' declared here}} + var target: String + var dependencies: String +} + +var ruleVar: Rule +// FIXME(diagnostics): To be able to suggest different candidates here we need to teach the solver how to figure out to which parameter +// does argument belong to in this case. If the `target` was of a different type, we currently suggest to add an argument for `dependencies:` +// which is incorrect. +ruleVar = Rule("a") // expected-error {{missing argument label 'target:' in call}} +// expected-error@-1 {{missing argument for parameter 'dependencies' in call}} + +class C { // expected-note {{did you mean 'C'?}} + var x: C? + init(other: C?) { x = other } + + func method() {} +} + +_ = C(3) // expected-error {{missing argument label 'other:' in call}} +// expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'C?'}} +_ = C(other: 3) // expected-error {{cannot convert value of type 'Int' to expected argument type 'C?'}} + +//===----------------------------------------------------------------------===// +// Unary Operators +//===----------------------------------------------------------------------===// + +func unaryOps(_ i8: inout Int8, i64: inout Int64) { + i8 = ~i8 + i64 += 1 + i8 -= 1 + + Int64(5) += 1 // expected-error{{left side of mutating operator has immutable type 'Int64'}} + + // attempt to modify a 'let' variable with ++ results in typecheck error not being able to apply ++ to Float + let a = i8 // expected-note {{change 'let' to 'var' to make it mutable}} {{3-6=var}} + a += 1 // expected-error {{left side of mutating operator isn't mutable: 'a' is a 'let' constant}} + + var b : Int { get { }} + b += 1 // expected-error {{left side of mutating operator isn't mutable: 'b' is a get-only property}} +} + +//===----------------------------------------------------------------------===// +// Iteration +//===----------------------------------------------------------------------===// + +func..<(x: Double, y: Double) -> Double { + return x + y +} + +func iterators() { + _ = 0..<42 + _ = 0.0..<42.0 +} + +//===----------------------------------------------------------------------===// +// Magic literal expressions +//===----------------------------------------------------------------------===// + +func magic_literals() { + _ = __FILE__ // expected-error {{__FILE__ has been replaced with #file in Swift 3}} + _ = __LINE__ // expected-error {{__LINE__ has been replaced with #line in Swift 3}} + _ = __COLUMN__ // expected-error {{__COLUMN__ has been replaced with #column in Swift 3}} + _ = __DSO_HANDLE__ // expected-error {{__DSO_HANDLE__ has been replaced with #dsohandle in Swift 3}} + + _ = #file + _ = #line + #column + var _: UInt8 = #line + #column +} + +//===----------------------------------------------------------------------===// +// lvalue processing +//===----------------------------------------------------------------------===// + + +infix operator +-+= +@discardableResult +func +-+= (x: inout Int, y: Int) -> Int { return 0} + +func lvalue_processing() { + var i = 0 + i += 1 // obviously ok + + var fn = (+-+=) + + var n = 42 + fn(n, 12) // expected-error {{passing value of type 'Int' to an inout parameter requires explicit '&'}} {{6-6=&}} + fn(&n, 12) // expected-warning {{result of call to function returning 'Int' is unused}} + + n +-+= 12 + + (+-+=)(&n, 12) // ok. + (+-+=)(n, 12) // expected-error {{passing value of type 'Int' to an inout parameter requires explicit '&'}} {{10-10=&}} +} + +struct Foo { + func method() {} + mutating func mutatingMethod() {} +} + +func test() { + var x = Foo() + let y = Foo() + + // rdar://15708430 + (&x).method() // expected-error {{use of extraneous '&'}} + (&x).mutatingMethod() // expected-error {{use of extraneous '&'}} +} + + +// Unused results. +func unusedExpressionResults() { + // Unused l-value + _ // expected-error{{'_' can only appear in a pattern or on the left side of an assignment}} + + // Conditional Optional binding hides compiler error + let optionalc:C? = nil + optionalc?.method() // ok + optionalc?.method // expected-error {{expression resolves to an unused function}} +} + + + + +//===----------------------------------------------------------------------===// +// Collection Literals +//===----------------------------------------------------------------------===// + +func arrayLiterals() { + let _ = [1,2,3] + let _ : [Int] = [] + let _ = [] // expected-error {{empty collection literal requires an explicit type}} +} + +func dictionaryLiterals() { + let _ = [1 : "foo",2 : "bar",3 : "baz"] + let _: Dictionary = [:] + let _ = [:] // expected-error {{empty collection literal requires an explicit type}} +} + +func invalidDictionaryLiteral() { + // FIXME: lots of unnecessary diagnostics. + + var a = [1: ; // expected-error {{expected value in dictionary literal}} + var b = [1: ;] // expected-error {{expected value in dictionary literal}} + var c = [1: "one" ;] // expected-error {{expected key expression in dictionary literal}} expected-error {{expected ',' separator}} {{20-20=,}} + var d = [1: "one", ;] // expected-error {{expected key expression in dictionary literal}} + var e = [1: "one", 2] // expected-error {{expected ':' in dictionary literal}} + var f = [1: "one", 2 ;] // expected-error {{expected ':' in dictionary literal}} + var g = [1: "one", 2: ;] // expected-error {{expected value in dictionary literal}} +} + + +[4].joined(separator: [1]) +// expected-error@-1 {{cannot convert value of type 'Int' to expected element type 'String'}} +// expected-error@-2 {{cannot convert value of type '[Int]' to expected argument type 'String'}} + +[4].joined(separator: [[[1]]]) +// expected-error@-1 {{cannot convert value of type 'Int' to expected element type 'String'}} +// expected-error@-2 {{cannot convert value of type '[[[Int]]]' to expected argument type 'String'}} + +//===----------------------------------------------------------------------===// +// nil/metatype comparisons +//===----------------------------------------------------------------------===// +_ = Int.self == nil // expected-warning {{comparing non-optional value of type 'Any.Type' to 'nil' always returns false}} +_ = nil == Int.self // expected-warning {{comparing non-optional value of type 'Any.Type' to 'nil' always returns false}} +_ = Int.self != nil // expected-warning {{comparing non-optional value of type 'Any.Type' to 'nil' always returns true}} +_ = nil != Int.self // expected-warning {{comparing non-optional value of type 'Any.Type' to 'nil' always returns true}} + +// Disallow postfix ? when not chaining +func testOptionalChaining(_ a : Int?, b : Int!, c : Int??) { + _ = a? // expected-error {{optional chain has no effect, expression already produces 'Int?'}} {{8-9=}} + _ = a?.customMirror + + _ = b? // expected-error {{optional chain has no effect, expression already produces 'Int?'}} + _ = b?.customMirror + + var _: Int? = c? // expected-error {{'?' must be followed by a call, member lookup, or subscript}} +} + + +// Nil Coalescing operator (??) should have a higher precedence +func testNilCoalescePrecedence(cond: Bool, a: Int?, r: ClosedRange?) { + // ?? should have higher precedence than logical operators like || and comparisons. + if cond || (a ?? 42 > 0) {} // Ok. + if (cond || a) ?? 42 > 0 {} // expected-error {{cannot be used as a boolean}} {{15-15=(}} {{16-16= != nil)}} + // expected-error@-1 {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} + // expected-error@-2 {{cannot convert value of type 'Bool' to expected argument type 'Int'}} + if (cond || a) ?? (42 > 0) {} // expected-error {{cannot be used as a boolean}} {{15-15=(}} {{16-16= != nil)}} + + if cond || a ?? 42 > 0 {} // Parses as the first one, not the others. + + + // ?? should have lower precedence than range and arithmetic operators. + let r1 = r ?? (0...42) // ok + let r2 = (r ?? 0)...42 // not ok: expected-error {{binary operator '??' cannot be applied to operands of type 'ClosedRange?' and 'Int'}} + let r3 = r ?? 0...42 // parses as the first one, not the second. + + + // [Type checker] Diagnose unsavory optional injections + // Accidental optional injection for ??. + let i = 42 + _ = i ?? 17 // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'Int', so the right side is never used}} {{9-15=}} +} + +// Parsing of as and ?? regressed +func testOptionalTypeParsing(_ a : AnyObject) -> String { + return a as? String ?? "default name string here" +} + +func testParenExprInTheWay() { + let x = 42 + + if x & 4.0 {} // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} + // expected-error@-1 {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} + if (x & 4.0) {} // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} + // expected-error@-1 {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} + if !(x & 4.0) {} // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} + // expected-error@-1 {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} + + if x & x {} // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} +} + +// Mixed method/property overload groups can cause a crash during constraint optimization +public struct TestPropMethodOverloadGroup { + public typealias Hello = String + public let apply:(Hello) -> Int + public func apply(_ input:Hello) -> Int { + return apply(input) + } +} + + +// Passing ternary operator expression as inout crashes Swift compiler +func inoutTests(_ arr: inout Int) { + var x = 1, y = 2 + (true ? &x : &y) // expected-error {{use of extraneous '&'}} + let a = (true ? &x : &y) // expected-error {{use of extraneous '&'}} + + inoutTests(true ? &x : &y) // expected-error {{use of extraneous '&'}} + + &_ // expected-error {{use of extraneous '&'}} + + inoutTests((&x, 24).0) // expected-error {{use of extraneous '&'}} + + inoutTests((&x)) // expected-error {{use of extraneous '&'}} + inoutTests(&x) + + // inout not rejected as operand to assignment operator + &x += y // expected-error {{use of extraneous '&'}} + + // + func takeAny(_ x: Any) {} + takeAny(&x) // expected-error{{'&' used with non-inout argument of type 'Any'}} + func takeManyAny(_ x: Any...) {} + takeManyAny(&x) // expected-error{{'&' used with non-inout argument of type 'Any'}} + takeManyAny(1, &x) // expected-error{{'&' used with non-inout argument of type 'Any'}} + func takeIntAndAny(_ x: Int, _ y: Any) {} + takeIntAndAny(1, &x) // expected-error{{'&' used with non-inout argument of type 'Any'}} +} + + +// Compiler crash in default argument & inout expr +var g20802757 = 2 +func r20802757(_ z: inout Int = &g20802757) { // expected-error {{cannot provide default value to inout parameter 'z'}} + // expected-error@-1 {{use of extraneous '&'}} + print(z) +} + +_ = _.foo // expected-error {{'_' can only appear in a pattern or on the left side of an assignment}} + +// wrong arg list crashing sourcekit +func r22211854() { + func f(_ x: Bool, _ y: Int, _ z: String = "") {} // expected-note 2 {{'f' declared here}} + func g(_ x: T, _ y: T, _ z: String = "") {} // expected-note 2 {{'g' declared here}} + + f(false) // expected-error{{missing argument for parameter #2 in call}} + g(1) // expected-error{{missing argument for parameter #2 in call}} + func h() -> Int { return 1 } + f(h() == 1) // expected-error{{missing argument for parameter #2 in call}} + g(h() == 1) // expected-error{{missing argument for parameter #2 in call}} +} + +// Compiler crash on invoking function with labeled defaulted param with non-labeled argument +func r22348394() { + func f(x: Int = 0) { } + f(Int(3)) // expected-error{{missing argument label 'x:' in call}} +} + +// Compiler crashes in Assertion failed: ((AllowOverwrite || !E->hasLValueAccessKind()) && "l-value access kind has already been set"), function visit +protocol Proto { var y: String? { get } } +func r23185177(_ x: Proto?) -> [String] { + return x?.y // expected-error{{cannot convert return expression of type 'String?' to return type '[String]'}} +} + +// Miscompile: wrong argument parsing when calling a function in swift2.0 +func r22913570() { + func f(_ from: Int = 0, to: Int) {} // expected-note {{'f(_:to:)' declared here}} + f(1 + 1) // expected-error{{missing argument for parameter 'to' in call}} +} + +// SR-628 mixing lvalues and rvalues in tuple expression +do { + var x = 0 + var y = 1 + let _ = (x, x + 1).0 + let _ = (x, 3).1 + (x,y) = (2,3) + (x,4) = (1,2) // expected-error {{cannot assign to value: literals are not mutable}} + (x,y).1 = 7 // expected-error {{cannot assign to immutable expression of type 'Int'}} + x = (x,(3,y)).1.1 +} + +// SR-3439 subscript with pound exprssions. +Sr3439: do { + class B { + init() {} + subscript(x: Int) -> Int { return x } + subscript(x: String) -> String { return x } + + func foo() { + _ = self[#line] // Ok. + } + } + class C : B { + func bar() { + _ = super[#file] // Ok. + } + } + + let obj = C(); + _ = obj[#column] // Ok. +} + +// rdar://problem/23672697 - No way to express literal integers larger than Int without using type ascription +let _: Int64 = 0xFFF_FFFF_FFFF_FFFF +let _: Int64 = Int64(0xFFF_FFFF_FFFF_FFFF) +let _: Int64 = 0xFFF_FFFF_FFFF_FFFF as Int64 +let _ = Int64(0xFFF_FFFF_FFFF_FFFF) +let _ = 0xFFF_FFFF_FFFF_FFFF as Int64 + +// rdar://problem/20289969 - string interpolation with comment containing ')' or '"' +let _ = "foo \(42 /* ) " ) */)" +let _ = "foo \(foo // ) " // expected-error {{unterminated string literal}} +let _ = "foo \(42 /* + * multiline comment + */)end" +// expected-error @-3 {{unterminated string literal}} +// expected-error @-2 {{expected expression}} +// expected-error @-3 {{unterminated string literal}} diff --git a/test/stmt/statements.swift b/test/stmt/statements.swift index 93de7f6efabc8..2c1e4e5085bdd 100644 --- a/test/stmt/statements.swift +++ b/test/stmt/statements.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-parser-lookup /* block comments */ /* /* nested too */ */ @@ -501,8 +501,9 @@ func test_guard(_ x : Int, y : Int??, cond : Bool) { guard let e, cond else {} // expected-error {{variable binding in a condition requires an initializer}} guard case let f? : Int?, cond else {} // expected-error {{variable binding in a condition requires an initializer}} + // FIXME: Bring back the tailored diagnostic guard let g = y else { - markUsed(g) // expected-error {{variable declared in 'guard' condition is not usable in its body}} + markUsed(g) // expected-error {{cannot find 'g' in scope}} } guard let h = y, cond {} // expected-error {{expected 'else' after 'guard' condition}} {{25-25=else }} @@ -512,8 +513,9 @@ func test_guard(_ x : Int, y : Int??, cond : Bool) { // SR-7567 guard let outer = y else { + // FIXME: Bring back the tailored diagnostic guard true else { - print(outer) // expected-error {{variable declared in 'guard' condition is not usable in its body}} + print(outer) // expected-error {{cannot find 'outer' in scope}} } } } diff --git a/test/stmt/statements_parser_lookup.swift b/test/stmt/statements_parser_lookup.swift new file mode 100644 index 0000000000000..caeb47ef5097a --- /dev/null +++ b/test/stmt/statements_parser_lookup.swift @@ -0,0 +1,723 @@ +// RUN: %target-typecheck-verify-swift -enable-parser-lookup + +/* block comments */ +/* /* nested too */ */ + +func markUsed(_ t: T) {} + +func f1(_ a: Int, _ y: Int) {} +func f2() {} +func f3() -> Int {} + +func invalid_semi() { + ; // expected-error {{';' statements are not allowed}} {{3-5=}} +} + +func nested1(_ x: Int) { + var y : Int + // expected-warning@-1 {{variable 'y' was never mutated; consider changing to 'let' constant}} + + func nested2(_ z: Int) -> Int { + return x+y+z + } + + _ = nested2(1) +} + +func funcdecl5(_ a: Int, y: Int) { + var x : Int + + // a few statements + if (x != 0) { + if (x != 0 || f3() != 0) { + // while with and without a space after it. + while(true) { 4; 2; 1 } // expected-warning 3 {{integer literal is unused}} + while (true) { 4; 2; 1 } // expected-warning 3 {{integer literal is unused}} + } + } + + // Assignment statement. + x = y + (x) = y + + 1 = x // expected-error {{cannot assign to value: literals are not mutable}} + (1) = x // expected-error {{cannot assign to value: literals are not mutable}} + "string" = "other" // expected-error {{cannot assign to value: literals are not mutable}} + [1, 1, 1, 1] = [1, 1] // expected-error {{cannot assign to immutable expression of type '[Int]}} + 1.0 = x // expected-error {{cannot assign to value: literals are not mutable}} + nil = 1 // expected-error {{cannot assign to value: literals are not mutable}} + + (x:1).x = 1 // expected-error {{cannot assign to immutable expression of type 'Int'}} + var tup : (x:Int, y:Int) + tup.x = 1 + _ = tup + + let B : Bool + + // if/then/else. + if (B) { + } else if (y == 2) { + } + + // This diagnostic is terrible - rdar://12939553 + if x {} // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} + + if true { + if (B) { + } else { + } + } + + if (B) { + f1(1,2) + } else { + f2() + } + + if (B) { + if (B) { + f1(1,2) + } else { + f2() + } + } else { + f2() + } + + // while statement. + while (B) { + } + + // It's okay to leave out the spaces in these. + while(B) {} + if(B) {} +} + +struct infloopbool { + var boolValue: infloopbool { + return self + } +} + +func infloopbooltest() { + if (infloopbool()) {} // expected-error {{cannot convert value of type 'infloopbool' to expected condition type 'Bool'}} +} + +// test "builder" API style +extension Int { + static func builder() -> Int { } + var builderProp: Int { return 0 } + func builder2() {} +} +Int + .builder() + .builderProp + .builder2() + +struct SomeGeneric { + static func builder() -> SomeGeneric { } + var builderProp: SomeGeneric { return .builder() } + func builder2() {} +} +SomeGeneric + .builder() + .builderProp + .builder2() + + +break // expected-error {{'break' is only allowed inside a loop, if, do, or switch}} +continue // expected-error {{'continue' is only allowed inside a loop}} +while true { + func f() { + break // expected-error {{'break' is only allowed inside a loop}} + continue // expected-error {{'continue' is only allowed inside a loop}} + } + + // Labeled if + MyIf: if 1 != 2 { + break MyIf + continue MyIf // expected-error {{'continue' cannot be used with if statements}} + break // break the while + continue // continue the while. + } +} + +// Labeled if +MyOtherIf: if 1 != 2 { + break MyOtherIf + continue MyOtherIf // expected-error {{'continue' cannot be used with if statements}} + break // expected-error {{unlabeled 'break' is only allowed inside a loop or switch, a labeled break is required to exit an if}} + continue // expected-error {{'continue' is only allowed inside a loop}} +} + +do { + break // expected-error {{unlabeled 'break' is only allowed inside a loop or switch, a labeled break is required to exit an if or do}} +} + +func tuple_assign() { + var a,b,c,d : Int + (a,b) = (1,2) + func f() -> (Int,Int) { return (1,2) } + ((a,b), (c,d)) = (f(), f()) + _ = (a,b,c,d) +} + +func missing_semicolons() { + var w = 321 + func g() {} + g() w += 1 // expected-error{{consecutive statements}} {{6-6=;}} + var z = w"hello" // expected-error{{consecutive statements}} {{12-12=;}} expected-warning {{string literal is unused}} + // expected-warning@-1 {{initialization of variable 'z' was never used; consider replacing with assignment to '_' or removing it}} + class C {}class C2 {} // expected-error{{consecutive statements}} {{14-14=;}} + struct S {}struct S2 {} // expected-error{{consecutive statements}} {{14-14=;}} + func j() {}func k() {} // expected-error{{consecutive statements}} {{14-14=;}} +} + +//===--- Return statement. + +return 42 // expected-error {{return invalid outside of a func}} + +return // expected-error {{return invalid outside of a func}} + +func NonVoidReturn1() -> Int { + _ = 0 + return // expected-error {{non-void function should return a value}} +} + +func NonVoidReturn2() -> Int { + return + // expected-error {{unary operator cannot be separated from its operand}} {{11-1=}} expected-error {{expected expression in 'return' statement}} +} + +func VoidReturn1() { + if true { return } + // Semicolon should be accepted -- rdar://11344875 + return; // no-error +} + +func VoidReturn2() { + return () // no-error +} + +func VoidReturn3() { + return VoidReturn2() // no-error +} + +//===--- If statement. + +func IfStmt1() { + if 1 > 0 // expected-error {{expected '{' after 'if' condition}} + _ = 42 +} + +func IfStmt2() { + if 1 > 0 { + } else // expected-error {{expected '{' or 'if' after 'else'}} + _ = 42 +} +func IfStmt3() { + if 1 > 0 { + } else 1 < 0 { // expected-error {{expected '{' or 'if' after 'else'; did you mean to write 'if'?}} {{9-9= if}} + _ = 42 + } else { + } +} + +//===--- While statement. + +func WhileStmt1() { + while 1 > 0 // expected-error {{expected '{' after 'while' condition}} + _ = 42 +} + +//===-- Do statement. +func DoStmt() { + // This is just a 'do' statement now. + do { + } +} + + +func DoWhileStmt1() { + do { // expected-error {{'do-while' statement is not allowed}} + // expected-note@-1 {{did you mean 'repeat-while' statement?}} {{3-5=repeat}} + // expected-note@-2 {{did you mean separate 'do' and 'while' statements?}} {{5-5=\n}} + } while true +} + +func DoWhileStmt2() { + do { + + } + while true { + + } +} + +func LabeledDoStmt() { + LABEL: { // expected-error {{labeled block needs 'do'}} {{10-10=do }} + } +} + +//===--- Repeat-while statement. + +func RepeatWhileStmt1() { + repeat {} while true + + repeat {} while false + + repeat { break } while true + repeat { continue } while true +} + +func RepeatWhileStmt2() { + repeat // expected-error {{expected '{' after 'repeat'}} expected-error {{expected 'while' after body of 'repeat' statement}} +} + +func RepeatWhileStmt4() { + repeat { + } while + // expected-error {{unary operator cannot be separated from its operand}} {{12-1=}} expected-error {{expected expression in 'repeat-while' condition}} +} + +func brokenSwitch(_ x: Int) -> Int { + switch x { + case .Blah(var rep): // expected-error{{type 'Int' has no member 'Blah'}} + return rep + } +} + +func switchWithVarsNotMatchingTypes(_ x: Int, y: Int, z: String) -> Int { + switch (x,y,z) { + case (let a, 0, _), (0, let a, _): // OK + return a + case (let a, _, _), (_, _, let a): // expected-error {{pattern variable bound to type 'String', expected type 'Int'}} + // expected-warning@-1 {{case is already handled by previous patterns; consider removing it}} + return a + } +} + +func breakContinue(_ x : Int) -> Int { + +Outer: + for _ in 0...1000 { + + Switch: // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} + switch x { + case 42: break Outer + case 97: continue Outer + case 102: break Switch + case 13: continue + case 139: break // 'break' should be able to break out of switch statements + } + } + + // shadowing loop labels should be an error +Loop: // expected-note {{previously declared here}} + for _ in 0...2 { + Loop: // expected-error {{label 'Loop' cannot be reused on an inner statement}} + for _ in 0...2 { + } + } + + + // Following a 'break' statement by another statement on a new line result in an error/fit-it + switch 5 { + case 5: + markUsed("before the break") + break + markUsed("after the break") // 'markUsed' is not a label for the break. + default: + markUsed("") + } + + let x : Int? = 42 + + // Should be able to pattern match 'nil' against optionals + switch x { // expected-error {{switch must be exhaustive}} + // expected-note@-1 {{missing case: '.some(_)'}} + case .some(42): break + case nil: break + + } + +} + + +enum MyEnumWithCaseLabels { + case Case(one: String, two: Int) +} + +func testMyEnumWithCaseLabels(_ a : MyEnumWithCaseLabels) { + // Enum case labels are ignored in "case let" statements + switch a { + case let .Case(one: _, two: x): break // ok + case let .Case(xxx: _, two: x): break // expected-error {{tuple pattern element label 'xxx' must be 'one'}} + // TODO: In principle, reordering like this could be supported. + case let .Case(two: _, one: x): break // expected-error {{tuple pattern element label}} + } +} + + + +// "defer" + +func test_defer(_ a : Int) { + + defer { VoidReturn1() } + defer { breakContinue(1)+42 } // expected-warning {{result of operator '+' is unused}} + + // Ok: + defer { while false { break } } + + // Not ok. + while false { defer { break } } // expected-error {{'break' cannot transfer control out of a defer statement}} + // expected-warning@-1 {{'defer' statement at end of scope always executes immediately}}{{17-22=do}} + defer { return } // expected-error {{'return' cannot transfer control out of a defer statement}} + // expected-warning@-1 {{'defer' statement at end of scope always executes immediately}}{{3-8=do}} +} + +class SomeTestClass { + var x = 42 + + func method() { + defer { x = 97 } // self. not required here! + // expected-warning@-1 {{'defer' statement at end of scope always executes immediately}}{{5-10=do}} + } +} + +enum DeferThrowError: Error { + case someError +} + +func throwInDefer() { + defer { throw DeferThrowError.someError } // expected-error {{errors cannot be thrown out of a defer body}} + print("Foo") +} + +func throwInDeferOK1() { + defer { + do { + throw DeferThrowError.someError + } catch {} + } + print("Bar") +} + +func throwInDeferOK2() throws { + defer { + do { + throw DeferThrowError.someError + } catch {} + } + print("Bar") +} + +func throwingFuncInDefer1() throws { + defer { try throwingFunctionCalledInDefer() } // expected-error {{errors cannot be thrown out of a defer body}} + print("Bar") +} + +func throwingFuncInDefer1a() throws { + defer { + do { + try throwingFunctionCalledInDefer() + } catch {} + } + print("Bar") +} + +func throwingFuncInDefer2() throws { + defer { throwingFunctionCalledInDefer() } // expected-error {{errors cannot be thrown out of a defer body}} + print("Bar") +} + +func throwingFuncInDefer2a() throws { + defer { + do { + throwingFunctionCalledInDefer() + // expected-error@-1 {{call can throw but is not marked with 'try'}} + // expected-note@-2 {{did you mean to use 'try'?}} + // expected-note@-3 {{did you mean to handle error as optional value?}} + // expected-note@-4 {{did you mean to disable error propagation?}} + } catch {} + } + print("Bar") +} + +func throwingFuncInDefer3() { + defer { try throwingFunctionCalledInDefer() } // expected-error {{errors cannot be thrown out of a defer body}} + print("Bar") +} + +func throwingFuncInDefer3a() { + defer { + do { + try throwingFunctionCalledInDefer() + } catch {} + } + print("Bar") +} + +func throwingFuncInDefer4() { + defer { throwingFunctionCalledInDefer() } // expected-error {{errors cannot be thrown out of a defer body}} + print("Bar") +} + +func throwingFuncInDefer4a() { + defer { + do { + throwingFunctionCalledInDefer() + // expected-error@-1 {{call can throw but is not marked with 'try'}} + // expected-note@-2 {{did you mean to use 'try'?}} + // expected-note@-3 {{did you mean to handle error as optional value?}} + // expected-note@-4 {{did you mean to disable error propagation?}} + } catch {} + } + print("Bar") +} + +func throwingFunctionCalledInDefer() throws { + throw DeferThrowError.someError +} + +class SomeDerivedClass: SomeTestClass { + override init() { + defer { + super.init() // expected-error {{initializer chaining ('super.init') cannot be nested in another expression}} + } + } +} + +func test_guard(_ x : Int, y : Int??, cond : Bool) { + + // These are all ok. + guard let a = y else {} + markUsed(a) + guard let b = y, cond else {} + guard case let c = x, cond else {} + guard case let Optional.some(d) = y else {} + guard x != 4, case _ = x else { } + + + guard let e, cond else {} // expected-error {{variable binding in a condition requires an initializer}} + guard case let f? : Int?, cond else {} // expected-error {{variable binding in a condition requires an initializer}} + + guard let g = y else { + markUsed(g) // expected-error {{variable declared in 'guard' condition is not usable in its body}} + } + + guard let h = y, cond {} // expected-error {{expected 'else' after 'guard' condition}} {{25-25=else }} + + + guard case _ = x else {} // expected-warning {{'guard' condition is always true, body is unreachable}} + + // SR-7567 + guard let outer = y else { + guard true else { + print(outer) // expected-error {{variable declared in 'guard' condition is not usable in its body}} + } + } +} + +func test_is_as_patterns() { + switch 4 { + case is Int: break // expected-warning {{'is' test is always true}} + case _ as Int: break // expected-warning {{'as' test is always true}} + // expected-warning@-1 {{case is already handled by previous patterns; consider removing it}} + case _: break // expected-warning {{case is already handled by previous patterns; consider removing it}} + } +} + +// Fuzzing SourceKit: crash in Parser::parseStmtForEach(...) +func matching_pattern_recursion() { + switch 42 { + case { // expected-error {{expression pattern of type '() -> ()' cannot match values of type 'Int'}} + for i in zs { + } + }: break + } +} + +// Swift's break operator in switch should be indicated in errors +func r18776073(_ a : Int?) { + switch a { + case nil: // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{14-14= break}} + case _?: break + } +} + +// unhelpful error message from "throw nil" +func testThrowNil() throws { + throw nil // expected-error {{cannot infer concrete Error for thrown 'nil' value}} +} + + +// rdar://problem/23684220 +// Even if the condition fails to typecheck, save it in the AST anyway; the old +// condition may have contained a SequenceExpr. +func r23684220(_ b: Any) { + if let _ = b ?? b {} // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'Any', so the right side is never used}} + // expected-error@-1 {{initializer for conditional binding must have Optional type, not 'Any'}} +} + + +// QoI: try/catch (instead of do/catch) creates silly diagnostics +func f21080671() { + try { // expected-error {{the 'do' keyword is used to specify a 'catch' region}} {{3-6=do}} + } catch { } + + + try { // expected-error {{the 'do' keyword is used to specify a 'catch' region}} {{3-6=do}} + f21080671() + } catch let x as Int { + } catch { + } +} + +// QoI: Using "&& #available" should fixit to comma +// https://twitter.com/radexp/status/694561060230184960 +func f(_ x : Int, y : Int) { + if x == y && #available(iOS 52, *) {} // expected-error {{expected ',' joining parts of a multi-clause condition}} {{12-15=,}} + if #available(iOS 52, *) && x == y {} // expected-error {{expected ',' joining parts of a multi-clause condition}} {{27-30=,}} + + // https://twitter.com/radexp/status/694790631881883648 + if x == y && let _ = Optional(y) {} // expected-error {{expected ',' joining parts of a multi-clause condition}} {{12-15=,}} + if x == y&&let _ = Optional(y) {} // expected-error {{expected ',' joining parts of a multi-clause condition}} {{12-14=,}} +} + + + +// QoI: Warn about cases where switch statement "ignores" where clause +enum Type { + case Foo + case Bar +} +func r25178926(_ a : Type) { + switch a { // expected-error {{switch must be exhaustive}} + // expected-note@-1 {{missing case: '.Bar'}} + case .Foo, .Bar where 1 != 100: + // expected-warning @-1 {{'where' only applies to the second pattern match in this case}} + // expected-note @-2 {{disambiguate by adding a line break between them if this is desired}} {{14-14=\n }} + // expected-note @-3 {{duplicate the 'where' on both patterns to check both patterns}} {{12-12= where 1 != 100}} + break + } + + switch a { // expected-error {{switch must be exhaustive}} + // expected-note@-1 {{missing case: '.Bar'}} + case .Foo: break + case .Bar where 1 != 100: break + } + + switch a { // expected-error {{switch must be exhaustive}} + // expected-note@-1 {{missing case: '.Bar'}} + case .Foo, // no warn + .Bar where 1 != 100: + break + } + + switch a { // expected-error {{switch must be exhaustive}} + // expected-note@-1 {{missing case: '.Foo'}} + // expected-note@-2 {{missing case: '.Bar'}} + case .Foo where 1 != 100, .Bar where 1 != 100: + break + } +} + +do { + guard 1 == 2 else { + break // expected-error {{unlabeled 'break' is only allowed inside a loop or switch, a labeled break is required to exit an if or do}} + } +} + +func fn(a: Int) { + guard a < 1 else { + break // expected-error {{'break' is only allowed inside a loop, if, do, or switch}} + } +} + +func fn(x: Int) { + if x >= 0 { + guard x < 1 else { + guard x < 2 else { + break // expected-error {{unlabeled 'break' is only allowed inside a loop or switch, a labeled break is required to exit an if or do}} + } + return + } + } +} + +func bad_if() { + if 1 {} // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} + if (x: false) {} // expected-error {{cannot convert value of type '(x: Bool)' to expected condition type 'Bool'}} + if (x: 1) {} // expected-error {{cannot convert value of type '(x: Int)' to expected condition type 'Bool'}} + if nil {} // expected-error {{'nil' is not compatible with expected condition type 'Bool'}} +} + +// Typo correction for loop labels +for _ in [1] { + break outerloop // expected-error {{cannot find label 'outerloop' in scope}} + continue outerloop // expected-error {{cannot find label 'outerloop' in scope}} +} +while true { + break outerloop // expected-error {{cannot find label 'outerloop' in scope}} + continue outerloop // expected-error {{cannot find label 'outerloop' in scope}} +} +repeat { + break outerloop // expected-error {{cannot find label 'outerloop' in scope}} + continue outerloop // expected-error {{cannot find label 'outerloop' in scope}} +} while true + +outerLoop: for _ in [1] { // expected-note {{'outerLoop' declared here}} + break outerloop // expected-error {{cannot find label 'outerloop' in scope; did you mean 'outerLoop'?}} {{9-18=outerLoop}} +} +outerLoop: for _ in [1] { // expected-note {{'outerLoop' declared here}} + continue outerloop // expected-error {{cannot find label 'outerloop' in scope; did you mean 'outerLoop'?}} {{12-21=outerLoop}} +} +outerLoop: while true { // expected-note {{'outerLoop' declared here}} + break outerloop // expected-error {{cannot find label 'outerloop' in scope; did you mean 'outerLoop'?}} {{9-18=outerLoop}} +} +outerLoop: while true { // expected-note {{'outerLoop' declared here}} + continue outerloop // expected-error {{cannot find label 'outerloop' in scope; did you mean 'outerLoop'?}} {{12-21=outerLoop}} +} +outerLoop: repeat { // expected-note {{'outerLoop' declared here}} + break outerloop // expected-error {{cannot find label 'outerloop' in scope; did you mean 'outerLoop'?}} {{9-18=outerLoop}} +} while true +outerLoop: repeat { // expected-note {{'outerLoop' declared here}} + continue outerloop // expected-error {{cannot find label 'outerloop' in scope; did you mean 'outerLoop'?}} {{12-21=outerLoop}} +} while true + +outerLoop1: for _ in [1] { // expected-note {{did you mean 'outerLoop1'?}} {{11-20=outerLoop1}} + outerLoop2: for _ in [1] { // expected-note {{did you mean 'outerLoop2'?}} {{11-20=outerLoop2}} + break outerloop // expected-error {{cannot find label 'outerloop' in scope}} + } +} +outerLoop1: for _ in [1] { // expected-note {{did you mean 'outerLoop1'?}} {{14-23=outerLoop1}} + outerLoop2: for _ in [1] { // expected-note {{did you mean 'outerLoop2'?}} {{14-23=outerLoop2}} + continue outerloop // expected-error {{cannot find label 'outerloop' in scope}} + } +} +outerLoop1: while true { // expected-note {{did you mean 'outerLoop1'?}} {{11-20=outerLoop1}} + outerLoop2: while true { // expected-note {{did you mean 'outerLoop2'?}} {{11-20=outerLoop2}} + break outerloop // expected-error {{cannot find label 'outerloop' in scope}} + } +} +outerLoop1: while true { // expected-note {{did you mean 'outerLoop1'?}} {{14-23=outerLoop1}} + outerLoop2: while true { // expected-note {{did you mean 'outerLoop2'?}} {{14-23=outerLoop2}} + continue outerloop // expected-error {{cannot find label 'outerloop' in scope}} + } +} +outerLoop1: repeat { // expected-note {{did you mean 'outerLoop1'?}} {{11-20=outerLoop1}} + outerLoop2: repeat { // expected-note {{did you mean 'outerLoop2'?}} {{11-20=outerLoop2}} + break outerloop // expected-error {{cannot find label 'outerloop' in scope}} + } while true +} while true +outerLoop1: repeat { // expected-note {{did you mean 'outerLoop1'?}} {{14-23=outerLoop1}} + outerLoop2: repeat { // expected-note {{did you mean 'outerLoop2'?}} {{14-23=outerLoop2}} + continue outerloop // expected-error {{cannot find label 'outerloop' in scope}} + } while true +} while true + +// Errors in case syntax +class +case, // expected-error {{expected identifier in enum 'case' declaration}} expected-error {{expected identifier after comma in enum 'case' declaration}} expected-error {{enum 'case' is not allowed outside of an enum}} +case // expected-error {{expected identifier in enum 'case' declaration}} expected-error {{enum 'case' is not allowed outside of an enum}} +// NOTE: EOF is important here to properly test a code path that used to crash the parser diff --git a/validation-test/compiler_crashers_2_fixed/0180-rdar45557325-parser-lookup.swift b/validation-test/compiler_crashers_2_fixed/0180-rdar45557325-parser-lookup.swift new file mode 100644 index 0000000000000..93e2285cf083c --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/0180-rdar45557325-parser-lookup.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -typecheck -verify -enable-parser-lookup + +// REQUIRES: objc_interop + +import Foundation + +class MyClass: NSObject { + func f() { + let url = URL(url) // expected-error{{variable used within its own initial value}} + } +} diff --git a/validation-test/compiler_crashers_2_fixed/0180-rdar45557325.swift b/validation-test/compiler_crashers_2_fixed/0180-rdar45557325.swift index 0276ac8627fb5..af0f3b45e45d2 100644 --- a/validation-test/compiler_crashers_2_fixed/0180-rdar45557325.swift +++ b/validation-test/compiler_crashers_2_fixed/0180-rdar45557325.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -typecheck -verify +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -typecheck -verify -disable-parser-lookup // REQUIRES: objc_interop @@ -6,6 +6,7 @@ import Foundation class MyClass: NSObject { func f() { - let url = URL(url) // expected-error{{variable used within its own initial value}} + let url = URL(url) // expected-error{{use of local variable 'url' before its declaration}} + // expected-note@-1 {{'url' declared here}} } } From 9294c0768731b1f8e56313814ea6ccdae8cab561 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 2 Oct 2020 21:42:06 -0700 Subject: [PATCH 201/745] CI: add concurrency to the Windows CI set Enable the concurrency support for Windows CI to mirror the other platforms. --- utils/build-windows.bat | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/build-windows.bat b/utils/build-windows.bat index 90462413e1ad1..39360fbc09a2f 100644 --- a/utils/build-windows.bat +++ b/utils/build-windows.bat @@ -201,6 +201,7 @@ cmake^ -DCMAKE_CXX_FLAGS:STRING="/GS- /Oy"^ -DCMAKE_EXE_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ + -DSWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY=YES^ -S "%source_root%\llvm-project\llvm" %exitOnError% cmake --build "%build_root%\llvm" %exitOnError% From 573886d5d60f84abc881b74b631288d94839b16f Mon Sep 17 00:00:00 2001 From: Dave Lee Date: Fri, 2 Oct 2020 13:54:17 -0700 Subject: [PATCH 202/745] [build] Remove forced tracing of lldb dotest --- utils/build-script-impl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index 2bb99c886a669..49e339d9715b6 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -2012,7 +2012,7 @@ for host in "${ALL_HOSTS[@]}"; do fi # Construct dotest arguments. We use semicolons so CMake interprets this as a list. - DOTEST_ARGS="--build-dir;${lldb_build_dir}/lldb-test-build.noindex;${LLDB_TEST_CATEGORIES};-t" + DOTEST_ARGS="--build-dir;${lldb_build_dir}/lldb-test-build.noindex;${LLDB_TEST_CATEGORIES}" # Only set the extra arguments if they're not empty. if [[ -n "${DOTEST_EXTRA}" ]]; then From 989b825f9d2a2934db4ecb8087f0266879960702 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 1 Oct 2020 18:44:56 -0400 Subject: [PATCH 203/745] Frontend: Disable parse-time name lookup by default --- lib/Frontend/CompilerInvocation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index d02bc2222b758..6dc8ed8bd1888 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -427,7 +427,7 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.DisableParserLookup |= Args.hasFlag(OPT_disable_parser_lookup, OPT_enable_parser_lookup, - /*default*/ false); + /*default*/ true); Opts.EnableNewOperatorLookup = Args.hasFlag(OPT_enable_new_operator_lookup, OPT_disable_new_operator_lookup, /*default*/ false); From 6ceda754149eecd41f4944dd95605489f6c2752b Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sun, 4 Oct 2020 16:19:06 -0300 Subject: [PATCH 204/745] [CSDiagnostic] Minor code improvement on MissingGenericArgumentsFailure addressing fixme --- lib/Sema/CSDiagnostics.cpp | 20 +++++++------------- lib/Sema/CSDiagnostics.h | 8 +++----- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index dd0572ee08baa..afe708f9fa38a 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -5271,14 +5271,10 @@ bool MissingGenericArgumentsFailure::diagnoseAsError() { scopedParameters[base].push_back(GP); }); - // FIXME: this code should be generalized now that we can anchor the - // fixes on the TypeRepr with the missing generic arg. if (!isScoped) { - assert(getAnchor().is() || getAnchor().is()); - if (auto *expr = getAsExpr(getAnchor())) - return diagnoseForAnchor(expr, Parameters); - - return diagnoseForAnchor(getAnchor().get(), Parameters); + auto anchor = getAnchor(); + assert(anchor.is() || anchor.is()); + return diagnoseForAnchor(anchor, Parameters); } bool diagnosed = false; @@ -5288,7 +5284,7 @@ bool MissingGenericArgumentsFailure::diagnoseAsError() { } bool MissingGenericArgumentsFailure::diagnoseForAnchor( - Anchor anchor, ArrayRef params) const { + ASTNode anchor, ArrayRef params) const { bool diagnosed = false; for (auto *GP : params) diagnosed |= diagnoseParameter(anchor, GP); @@ -5322,11 +5318,9 @@ bool MissingGenericArgumentsFailure::diagnoseForAnchor( } bool MissingGenericArgumentsFailure::diagnoseParameter( - Anchor anchor, GenericTypeParamType *GP) const { + ASTNode anchor, GenericTypeParamType *GP) const { auto &solution = getSolution(); - - auto loc = anchor.is() ? anchor.get()->getLoc() - : anchor.get()->getLoc(); + auto loc = ::getLoc(anchor); auto *locator = getLocator(); // Type variables associated with missing generic parameters are @@ -5372,7 +5366,7 @@ bool MissingGenericArgumentsFailure::diagnoseParameter( } void MissingGenericArgumentsFailure::emitGenericSignatureNote( - Anchor anchor) const { + ASTNode anchor) const { auto &solution = getSolution(); auto *paramDC = getDeclContext(); diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index c684aa2096959..4889dbeab30dc 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1698,8 +1698,6 @@ class InOutConversionFailure final : public ContextualFailure { /// _ = S() /// ``` class MissingGenericArgumentsFailure final : public FailureDiagnostic { - using Anchor = llvm::PointerUnion; - SmallVector Parameters; public: @@ -1722,13 +1720,13 @@ class MissingGenericArgumentsFailure final : public FailureDiagnostic { bool diagnoseAsError() override; - bool diagnoseForAnchor(Anchor anchor, + bool diagnoseForAnchor(ASTNode anchor, ArrayRef params) const; - bool diagnoseParameter(Anchor anchor, GenericTypeParamType *GP) const; + bool diagnoseParameter(ASTNode anchor, GenericTypeParamType *GP) const; private: - void emitGenericSignatureNote(Anchor anchor) const; + void emitGenericSignatureNote(ASTNode anchor) const; /// Retrieve representative locations for associated generic prameters. /// From 960d0e30ee2b70ba3bb7ec076c16045afab01589 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Fri, 7 Aug 2020 09:15:52 +0100 Subject: [PATCH 205/745] Linux: Fix -static-executable and remove swiftImageInspectionShared - Remove references to swiftImageInspectionShared on Linux and dont have split libraries between static/non-static linked executables. - -static-executable now links correctly Linux. Note: swift::lookupSymbol() will not work on statically linked executables but this can be worked around by using the https://github.com/swift-server/swift-backtrace package. --- stdlib/public/core/CMakeLists.txt | 2 +- stdlib/public/runtime/CMakeLists.txt | 52 ----------------------- test/Driver/static-executable-linux.swift | 10 +++++ unittests/runtime/CMakeLists.txt | 7 --- utils/static-executable-args.lnk | 7 ++- 5 files changed, 14 insertions(+), 64 deletions(-) create mode 100644 test/Driver/static-executable-linux.swift diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index ad74e0e699039..7f8c8a62c2482 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -274,7 +274,7 @@ elseif(SWIFT_PRIMARY_VARIANT_SDK STREQUAL FREEBSD) ${SWIFTLIB_DIR}/clang/lib/freebsd/libclang_rt.builtins-${SWIFT_PRIMARY_VARIANT_ARCH}.a) elseif(SWIFT_PRIMARY_VARIANT_SDK STREQUAL LINUX) if(SWIFT_BUILD_STATIC_STDLIB) - list(APPEND swift_core_private_link_libraries swiftImageInspectionShared) + list(APPEND swift_core_private_link_libraries) endif() elseif(SWIFT_PRIMARY_VARIANT_SDK STREQUAL WINDOWS) list(APPEND swift_core_private_link_libraries shell32;DbgHelp) diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index 4b51b04fec37b..56d5110ef94d8 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -81,48 +81,9 @@ list(APPEND swift_runtime_library_compile_flags -I${SWIFT_SOURCE_DIR}/stdlib/inc set(sdk "${SWIFT_HOST_VARIANT_SDK}") if(SWIFT_BUILD_STATIC_STDLIB AND "${sdk}" STREQUAL "LINUX") - list(REMOVE_ITEM swift_runtime_sources ImageInspectionELF.cpp) set(static_binary_lnk_file_list) string(TOLOWER "${sdk}" lowercase_sdk) - # These two libraries are only used with the static swiftcore - add_swift_target_library(swiftImageInspectionShared STATIC - ImageInspectionELF.cpp - C_COMPILE_FLAGS ${swift_runtime_library_compile_flags} - LINK_FLAGS ${swift_runtime_linker_flags} - SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - INSTALL_IN_COMPONENT stdlib) - - foreach(arch IN LISTS SWIFT_SDK_${sdk}_ARCHITECTURES) - set(FragileSupportLibrary swiftImageInspectionShared-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${arch}) - set(LibraryLocation ${SWIFTSTATICLIB_DIR}/${lowercase_sdk}/${arch}) - add_custom_command_target(swift_image_inspection_${arch}_static - COMMAND - "${CMAKE_COMMAND}" -E copy $ ${LibraryLocation} - OUTPUT - "${LibraryLocation}/${CMAKE_STATIC_LIBRARY_PREFIX}swiftImageInspectionShared${CMAKE_STATIC_LIBRARY_SUFFIX}" - DEPENDS - ${FragileSupportLibrary}) - add_dependencies(stdlib ${FragileSupportLibrary}) - swift_install_in_component(FILES $ - DESTINATION "lib/swift_static/${lowercase_sdk}/${arch}" - COMPONENT stdlib) - endforeach() - - set(FragileSupportLibraryPrimary swiftImageInspectionShared-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${SWIFT_PRIMARY_VARIANT_ARCH}) - set(LibraryLocationPrimary ${SWIFTSTATICLIB_DIR}/${lowercase_sdk}) - add_custom_command_target(swift_image_inspection_static_primary_arch - COMMAND - "${CMAKE_COMMAND}" -E copy $ ${LibraryLocationPrimary} - OUTPUT - "${LibraryLocationPrimary}/${CMAKE_STATIC_LIBRARY_PREFIX}swiftImageInspectionShared${CMAKE_STATIC_LIBRARY_SUFFIX}" - DEPENDS - ${FragileSupportLibraryPrimary}) - add_dependencies(stdlib ${FragileSupportLibraryPrimary}) - swift_install_in_component(FILES $ - DESTINATION "lib/swift_static/${lowercase_sdk}" - COMPONENT stdlib) - # Generate the static-executable-args.lnk file used for ELF systems (eg linux) set(linkfile "${lowercase_sdk}/static-executable-args.lnk") add_custom_command_target(swift_static_binary_${sdk}_args @@ -140,18 +101,6 @@ if(SWIFT_BUILD_STATIC_STDLIB AND "${sdk}" STREQUAL "LINUX") DESTINATION "lib/swift_static/${lowercase_sdk}" COMPONENT stdlib) add_custom_target(static_binary_magic ALL DEPENDS ${static_binary_lnk_file_list}) - foreach(arch IN LISTS SWIFT_SDK_LINUX_ARCHITECTURES) - add_dependencies(static_binary_magic ${swift_image_inspection_${arch}_static}) - endforeach() - add_dependencies(static_binary_magic ${swift_image_inspection_static_primary_arch}) - add_dependencies(stdlib static_binary_magic) - - add_swift_target_library(swiftImageInspectionSharedObject OBJECT_LIBRARY - ImageInspectionELF.cpp - C_COMPILE_FLAGS ${swift_runtime_library_compile_flags} - LINK_FLAGS ${swift_runtime_linker_flags} - SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - INSTALL_IN_COMPONENT never_install) endif() add_swift_target_library(swiftRuntime OBJECT_LIBRARY @@ -280,7 +229,6 @@ foreach(sdk ${SWIFT_CONFIGURED_SDKS}) -ldl -lpthread -lswiftCore --lswiftImageInspectionShared ${libicu_i18n_a} ${libicu_uc_a} ${libicu_data_a} diff --git a/test/Driver/static-executable-linux.swift b/test/Driver/static-executable-linux.swift new file mode 100644 index 0000000000000..8f0589aeccf36 --- /dev/null +++ b/test/Driver/static-executable-linux.swift @@ -0,0 +1,10 @@ +// Build a static executable "hello world" program +// REQUIRES: OS=linux-gnu +// REQUIRES: static_stdlib +print("hello world!") +// RUN: %empty-directory(%t) +// RUN: %target-swiftc_driver -static-executable -o %t/static-executable %s +// RUN: %t/static-executable | %FileCheck %s +// RUN: file %t/static-executable | %FileCheck %s --check-prefix=FILE +// CHECK: hello world! +// FILE: , statically linked, diff --git a/unittests/runtime/CMakeLists.txt b/unittests/runtime/CMakeLists.txt index 3ca27587f9e7b..33fa05245c412 100644 --- a/unittests/runtime/CMakeLists.txt +++ b/unittests/runtime/CMakeLists.txt @@ -28,12 +28,6 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND endif() endif() - set(swift_runtime_test_extra_libraries) - if(SWIFT_BUILD_STATIC_STDLIB AND "${SWIFT_HOST_VARIANT_SDK}" STREQUAL "LINUX") - list(APPEND swift_runtime_test_extra_libraries - $) - endif() - add_subdirectory(LongTests) set(PLATFORM_SOURCES) @@ -94,7 +88,6 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND PRIVATE swiftCore${SWIFT_PRIMARY_VARIANT_SUFFIX} ${PLATFORM_TARGET_LINK_LIBRARIES} - ${swift_runtime_test_extra_libraries} ) endif() diff --git a/utils/static-executable-args.lnk b/utils/static-executable-args.lnk index 98b814164c242..5e9d5c0c46014 100644 --- a/utils/static-executable-args.lnk +++ b/utils/static-executable-args.lnk @@ -1,12 +1,11 @@ -static -lswiftCore --lswiftImageInspectionShared -Xlinker ---defsym=__import_pthread_self=pthread_self +-undefined=pthread_self -Xlinker ---defsym=__import_pthread_once=pthread_once +-undefined=pthread_once -Xlinker ---defsym=__import_pthread_key_create=pthread_key_create +-undefined=pthread_key_create -lpthread -licui18nswift -licuucswift From 6ab35727cf5baed7d7bab1bad7b1201f057c5d59 Mon Sep 17 00:00:00 2001 From: Vincent Esche <138017+regexident@users.noreply.github.com> Date: Mon, 5 Oct 2020 15:00:47 +0200 Subject: [PATCH 206/745] Fixed docs link to Rust's demand-driven compilation guide --- docs/RequestEvaluator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/RequestEvaluator.md b/docs/RequestEvaluator.md index 77c4d37c7c412..13d5bd054621a 100644 --- a/docs/RequestEvaluator.md +++ b/docs/RequestEvaluator.md @@ -71,4 +71,4 @@ The request-evaluator is relatively new to the Swift compiler, having been intro * Port higher-level queries (e.g., those that come from SourceKit) over to the request-evaluator, so we can see the dependencies of a given SourceKit request for testing and performance tuning. ## Prior art -Rust's compiler went through a similar transformation to support [demand-driven compilation](https://rust-lang-nursery.github.io/rustc-guide/query.html). We should learn from their experience! +Rust's compiler went through a similar transformation to support [demand-driven compilation](https://rustc-dev-guide.rust-lang.org/query.html). We should learn from their experience! From 8ffaf79ec2e106a58dff2402a06210bf9ca97357 Mon Sep 17 00:00:00 2001 From: Cassie Jones Date: Mon, 5 Oct 2020 12:00:41 -0400 Subject: [PATCH 207/745] [build] Support installing swift-driver without swiftpm The install-swift-driver phase knows how to build swift-driver using CMake. Allowing only install-swift-driver without plain swift-driver allows installing it without requiring swiftpm. --- utils/build-script | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/build-script b/utils/build-script index 9282cd84cf6af..ab97e9e06a51a 100755 --- a/utils/build-script +++ b/utils/build-script @@ -821,7 +821,7 @@ class BuildScriptInvocation(object): product_classes = [] if self.args.build_swiftpm: product_classes.append(products.SwiftPM) - if self.args.build_swift_driver: + if self.args.build_swift_driver or self.args.install_swift_driver: product_classes.append(products.SwiftDriver) if self.args.build_swiftsyntax: product_classes.append(products.SwiftSyntax) From e7761cf997897aaa38af0d0969ca9acd7e504fbf Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 5 Oct 2020 11:27:10 -0500 Subject: [PATCH 208/745] [DebuggingTheCompiler] Document a few flags for dumping llvm-ir. I needed to use these and realized they were not documented here. I had them in my brain, but others may not, so seemed good to do. --- docs/DebuggingTheCompiler.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/DebuggingTheCompiler.md b/docs/DebuggingTheCompiler.md index 061ed609780a3..6f1d411698219 100644 --- a/docs/DebuggingTheCompiler.md +++ b/docs/DebuggingTheCompiler.md @@ -30,6 +30,8 @@ benefit of all Swift developers. - [Debugging the Compiler using advanced LLDB Breakpoints](#debugging-the-compiler-using-advanced-lldb-breakpoints) - [Debugging the Compiler using LLDB Scripts](#debugging-the-compiler-using-lldb-scripts) - [Custom LLDB Commands](#custom-lldb-commands) + - [Debugging at LLVM Level](#debugging-at-llvm-level) + - [Options for Dumping LLVM IR](#options-for-dumping-llvm-ir) - [Bisecting Compiler Errors](#bisecting-compiler-errors) - [Bisecting on SIL optimizer pass counts to identify optimizer bugs](#bisecting-on-sil-optimizer-pass-counts-to-identify-optimizer-bugs) - [Using git-bisect in the presence of branch forwarding/feature branches](#using-git-bisect-in-the-presence-of-branch-forwardingfeature-branches) @@ -537,6 +539,20 @@ to define custom commands using just other lldb commands. For example, (lldb) command alias cs sequence p/x $rax; stepi +## Debugging at LLVM Level + +### Options for Dumping LLVM IR + +Similar to SIL, one can configure LLVM to dump the llvm-ir at various points in +the pipeline. Here is a quick summary of the various options: + +* ``-Xllvm -print-before=$PASS_ID``: Print the LLVM IR before a specified LLVM pass runs. +* ``-Xllvm -print-before-all``: Print the LLVM IR before each pass runs. +* ``-Xllvm -print-after-all``: Print the LLVM IR after each pass runs. +* ``-Xllvm -filter-print-funcs=$FUNC_NAME_1,$FUNC_NAME_2,...,$FUNC_NAME_N``: + When printing IR for functions for print-[before|after]-all options, Only + print the IR for functions whose name is in this comma separated list. + ## Bisecting Compiler Errors ### Bisecting on SIL optimizer pass counts to identify optimizer bugs From f4bbafb392d838746f4de60d98aa655efff04c8e Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Mon, 5 Oct 2020 10:23:31 -0700 Subject: [PATCH 209/745] Revert "[PassManager] Update PassManager's function worklist for newly added SILFunctions" --- .../SILOptimizer/Analysis/FunctionOrder.h | 27 +++++------ lib/SILOptimizer/Analysis/FunctionOrder.cpp | 4 ++ lib/SILOptimizer/PassManager/PassManager.cpp | 46 +------------------ .../UtilityPasses/FunctionOrderPrinter.cpp | 4 +- test/DebugInfo/inlined-generics-basic.swift | 2 +- 5 files changed, 21 insertions(+), 62 deletions(-) diff --git a/include/swift/SILOptimizer/Analysis/FunctionOrder.h b/include/swift/SILOptimizer/Analysis/FunctionOrder.h index 8a443a886cbc7..6863cee08b4b6 100644 --- a/include/swift/SILOptimizer/Analysis/FunctionOrder.h +++ b/include/swift/SILOptimizer/Analysis/FunctionOrder.h @@ -31,6 +31,7 @@ class BottomUpFunctionOrder { typedef TinyPtrVector SCC; private: + SILModule &M; llvm::SmallVector TheSCCs; llvm::SmallVector TheFunctions; @@ -43,29 +44,24 @@ class BottomUpFunctionOrder { llvm::SmallSetVector DFSStack; public: - BottomUpFunctionOrder(BasicCalleeAnalysis *BCA) - : BCA(BCA), NextDFSNum(0) {} - - /// DFS on 'F' to compute bottom up order - void computeBottomUpOrder(SILFunction *F) { - DFS(F); - } - - /// DFS on all functions in the module to compute bottom up order - void computeBottomUpOrder(SILModule *M) { - for (auto &F : *M) - DFS(&F); - } + BottomUpFunctionOrder(SILModule &M, BasicCalleeAnalysis *BCA) + : M(M), BCA(BCA), NextDFSNum(0) {} /// Get the SCCs in bottom-up order. ArrayRef getSCCs() { + if (!TheSCCs.empty()) + return TheSCCs; + + FindSCCs(M); return TheSCCs; } - /// Get a flattened view of all functions in all the SCCs in bottom-up order - ArrayRef getBottomUpOrder() { + /// Get a flattened view of all functions in all the SCCs in + /// bottom-up order + ArrayRef getFunctions() { if (!TheFunctions.empty()) return TheFunctions; + for (auto SCC : getSCCs()) for (auto *F : SCC) TheFunctions.push_back(F); @@ -75,6 +71,7 @@ class BottomUpFunctionOrder { private: void DFS(SILFunction *F); + void FindSCCs(SILModule &M); }; } // end namespace swift diff --git a/lib/SILOptimizer/Analysis/FunctionOrder.cpp b/lib/SILOptimizer/Analysis/FunctionOrder.cpp index e0bf1a17ef177..03cc277a0dcc5 100644 --- a/lib/SILOptimizer/Analysis/FunctionOrder.cpp +++ b/lib/SILOptimizer/Analysis/FunctionOrder.cpp @@ -74,3 +74,7 @@ void BottomUpFunctionOrder::DFS(SILFunction *Start) { } } +void BottomUpFunctionOrder::FindSCCs(SILModule &M) { + for (auto &F : M) + DFS(&F); +} diff --git a/lib/SILOptimizer/PassManager/PassManager.cpp b/lib/SILOptimizer/PassManager/PassManager.cpp index f315eec53f65d..d08990304f95b 100644 --- a/lib/SILOptimizer/PassManager/PassManager.cpp +++ b/lib/SILOptimizer/PassManager/PassManager.cpp @@ -514,9 +514,8 @@ runFunctionPasses(unsigned FromTransIdx, unsigned ToTransIdx) { return; BasicCalleeAnalysis *BCA = getAnalysis(); - BottomUpFunctionOrder BottomUpOrder(BCA); - BottomUpOrder.computeBottomUpOrder(Mod); - auto BottomUpFunctions = BottomUpOrder.getBottomUpOrder(); + BottomUpFunctionOrder BottomUpOrder(*Mod, BCA); + auto BottomUpFunctions = BottomUpOrder.getFunctions(); assert(FunctionWorklist.empty() && "Expected empty function worklist!"); @@ -569,47 +568,6 @@ runFunctionPasses(unsigned FromTransIdx, unsigned ToTransIdx) { ++Entry.PipelineIdx; } clearRestartPipeline(); - - if (TailIdx == (FunctionWorklist.size() - 1)) { - // No new functions to process - continue; - } - - // Compute the bottom up order of the new functions and the callees in it - BottomUpFunctionOrder SubBottomUpOrder(BCA); - // Initialize BottomUpFunctionOrder with new functions - for (auto It = FunctionWorklist.begin() + TailIdx + 1; - It != FunctionWorklist.end(); It++) { - SubBottomUpOrder.computeBottomUpOrder(It->F); - } - auto NewFunctionsBottomUp = SubBottomUpOrder.getBottomUpOrder(); - SmallPtrSet NewBottomUpSet(NewFunctionsBottomUp.begin(), - NewFunctionsBottomUp.end()); - - // Remove all the functions in the new bottom up order from FunctionWorklist - llvm::DenseMap FunctionsToReorder; - auto RemoveFn = [&FunctionsToReorder, - &NewBottomUpSet](WorklistEntry Entry) { - if (NewBottomUpSet.find(Entry.F) == NewBottomUpSet.end()) { - return false; - } - FunctionsToReorder.insert(std::make_pair(Entry.F, Entry)); - return true; - }; - std::remove_if(FunctionWorklist.begin(), FunctionWorklist.end(), RemoveFn); - FunctionWorklist.erase((FunctionWorklist.begin() + FunctionWorklist.size() - - FunctionsToReorder.size()), - FunctionWorklist.end()); - - // Add back the functions in the new bottom up order to the FunctionWorklist - for (auto it = NewFunctionsBottomUp.rbegin(); - it != NewFunctionsBottomUp.rend(); it++) { - auto Entry = FunctionsToReorder.find(*it); - if (Entry == FunctionsToReorder.end()) { - continue; - } - FunctionWorklist.push_back((*Entry).second); - } } } diff --git a/lib/SILOptimizer/UtilityPasses/FunctionOrderPrinter.cpp b/lib/SILOptimizer/UtilityPasses/FunctionOrderPrinter.cpp index da66869263cac..59bd0b43a8fa7 100644 --- a/lib/SILOptimizer/UtilityPasses/FunctionOrderPrinter.cpp +++ b/lib/SILOptimizer/UtilityPasses/FunctionOrderPrinter.cpp @@ -35,8 +35,8 @@ class FunctionOrderPrinterPass : public SILModuleTransform { /// The entry point to the transformation. void run() override { BCA = getAnalysis(); - BottomUpFunctionOrder Orderer(BCA); - Orderer.computeBottomUpOrder(getModule()); + auto &M = *getModule(); + BottomUpFunctionOrder Orderer(M, BCA); llvm::outs() << "Bottom up function order:\n"; auto SCCs = Orderer.getSCCs(); diff --git a/test/DebugInfo/inlined-generics-basic.swift b/test/DebugInfo/inlined-generics-basic.swift index 5dddba06245a5..93f13e5d05955 100644 --- a/test/DebugInfo/inlined-generics-basic.swift +++ b/test/DebugInfo/inlined-generics-basic.swift @@ -91,9 +91,9 @@ public class C { // IR-LABEL: ret void // IR: ![[BOOL:[0-9]+]] = !DICompositeType({{.*}}name: "Bool" +// IR: ![[LET_BOOL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[BOOL]]) // IR: ![[INT:[0-9]+]] = !DICompositeType({{.*}}name: "Int" // IR: ![[LET_INT:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[INT]]) -// IR: ![[LET_BOOL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[BOOL]]) // IR: ![[TAU_0_0:[0-9]+]] = {{.*}}DW_TAG_structure_type, name: "$sxD", // IR: ![[LET_TAU_0_0:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[TAU_0_0]]) // IR: ![[TAU_1_0:[0-9]+]] = {{.*}}DW_TAG_structure_type, name: "$sqd__D", From 73727051fa61fe48b12bfc72233cdf28d804f58f Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Mon, 5 Oct 2020 10:54:41 -0700 Subject: [PATCH 210/745] [AST] Fix linker errors with the parser-only build --- lib/AST/Decl.cpp | 4 ++++ lib/AST/ExtInfo.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 86e1f8be8bca9..56363cd72fb74 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -119,6 +119,10 @@ const clang::Module *ClangNode::getClangModule() const { } void ClangNode::dump() const { +#if SWIFT_BUILD_ONLY_SYNTAXPARSERLIB + return; // not needed for the parser library. +#endif + if (auto D = getAsDecl()) D->dump(); else if (auto M = getAsMacro()) diff --git a/lib/AST/ExtInfo.cpp b/lib/AST/ExtInfo.cpp index 1fb30adc6af16..74e62cd6c5cf8 100644 --- a/lib/AST/ExtInfo.cpp +++ b/lib/AST/ExtInfo.cpp @@ -92,6 +92,10 @@ Optional UnexpectedClangTypeError::checkClangType( } void UnexpectedClangTypeError::dump() { +#if SWIFT_BUILD_ONLY_SYNTAXPARSERLIB + return; // not needed for the parser library. +#endif + auto &e = llvm::errs(); using Kind = UnexpectedClangTypeError::Kind; switch (errorKind) { From 00eb2e9db2140d7a7acdd9aa3176e12df9012a7d Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Mon, 5 Oct 2020 10:48:07 -0700 Subject: [PATCH 211/745] Frontend: add a frontend flag to disable building module from textual interface This is for testing purposes to ensure prebuilt modules are up to date. rdar://68770805 --- include/swift/Frontend/FrontendOptions.h | 4 ++++ .../swift/Frontend/ModuleInterfaceLoader.h | 2 ++ include/swift/Option/FrontendOptions.td | 4 ++++ .../ArgsToFrontendOptionsConverter.cpp | 1 + lib/Frontend/ModuleInterfaceLoader.cpp | 19 ++++++++++------- .../disable-building-interface.swift | 21 +++++++++++++++++++ 6 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 test/ModuleInterface/disable-building-interface.swift diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index 155ebcb7adf81..dc0dff3c449f2 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -272,6 +272,10 @@ class FrontendOptions { /// built and given to the compiler invocation. bool DisableImplicitModules = false; + /// Disable building Swift modules from textual interfaces. This should be + /// for testing purposes only. + bool DisableBuildingInterface = false; + /// When performing a dependency scanning action, only identify and output all imports /// of the main Swift module's source files. bool ImportPrescan = false; diff --git a/include/swift/Frontend/ModuleInterfaceLoader.h b/include/swift/Frontend/ModuleInterfaceLoader.h index 56c63173fb661..647cdf8f09651 100644 --- a/include/swift/Frontend/ModuleInterfaceLoader.h +++ b/include/swift/Frontend/ModuleInterfaceLoader.h @@ -290,11 +290,13 @@ struct ModuleInterfaceLoaderOptions { bool remarkOnRebuildFromInterface = false; bool disableInterfaceLock = false; bool disableImplicitSwiftModule = false; + bool disableBuildingInterface = false; std::string mainExecutablePath; ModuleInterfaceLoaderOptions(const FrontendOptions &Opts): remarkOnRebuildFromInterface(Opts.RemarkOnRebuildFromModuleInterface), disableInterfaceLock(Opts.DisableInterfaceFileLock), disableImplicitSwiftModule(Opts.DisableImplicitModules), + disableBuildingInterface(Opts.DisableBuildingInterface), mainExecutablePath(Opts.MainExecutablePath) { switch (Opts.RequestedAction) { diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index bab1f03411bdb..fe6e53afe9c98 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -758,4 +758,8 @@ def candidate_module_file def use_static_resource_dir : Flag<["-"], "use-static-resource-dir">, HelpText<"Use resources in the static resource directory">; + +def disable_building_interface + : Flag<["-"], "disable-building-interface">, + HelpText<"Disallow building binary module from textual interface">; } // end let Flags = [FrontendOption, NoDriverOption, HelpHidden] diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index c3cee42314da2..8634d26296f1c 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -212,6 +212,7 @@ bool ArgsToFrontendOptionsConverter::convert( Opts.ImportUnderlyingModule |= Args.hasArg(OPT_import_underlying_module); Opts.EnableIncrementalDependencyVerifier |= Args.hasArg(OPT_verify_incremental_dependencies); Opts.UseSharedResourceFolder = !Args.hasArg(OPT_use_static_resource_dir); + Opts.DisableBuildingInterface = Args.hasArg(OPT_disable_building_interface); computeImportObjCHeaderOptions(); computeImplicitImportModuleNames(); diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index ab054a3423ba8..d6dca20f0bee3 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -859,13 +859,6 @@ class ModuleInterfaceLoaderImpl { prebuiltCacheDir, /*serializeDependencyHashes*/false, trackSystemDependencies); - // Set up a builder if we need to build the module. It'll also set up - // the genericSubInvocation we'll need to use to compute the cache paths. - ModuleInterfaceBuilder builder( - ctx.SourceMgr, ctx.Diags, astDelegate, interfacePath, moduleName, cacheDir, - prebuiltCacheDir, - Opts.disableInterfaceLock, diagnosticLoc, - dependencyTracker); // Compute the output path if we're loading or emitting a cached module. llvm::SmallString<256> cachedOutputPath; @@ -908,6 +901,18 @@ class ModuleInterfaceLoaderImpl { return std::move(module.moduleBuffer); } + // If building from interface is disabled, return error. + if (Opts.disableBuildingInterface) { + return std::make_error_code(std::errc::not_supported); + } + + // Set up a builder if we need to build the module. It'll also set up + // the genericSubInvocation we'll need to use to compute the cache paths. + ModuleInterfaceBuilder builder( + ctx.SourceMgr, ctx.Diags, astDelegate, interfacePath, moduleName, cacheDir, + prebuiltCacheDir, + Opts.disableInterfaceLock, diagnosticLoc, + dependencyTracker); std::unique_ptr moduleBuffer; diff --git a/test/ModuleInterface/disable-building-interface.swift b/test/ModuleInterface/disable-building-interface.swift new file mode 100644 index 0000000000000..4a9a89889178d --- /dev/null +++ b/test/ModuleInterface/disable-building-interface.swift @@ -0,0 +1,21 @@ +// 1. Create folders +// RUN: %empty-directory(%t/PrebuiltModule.swiftmodule) +// RUN: %empty-directory(%t/ModuleCache) + +// 2. Define some public API +// RUN: echo 'public struct InPrebuiltModule {}' > %t/PrebuiltModule.swift + +// 3. Compile textual interface only into a directory +// RUN: %target-swift-frontend -emit-module %t/PrebuiltModule.swift -module-name PrebuiltModule -emit-module-interface-path %t/PrebuiltModule.swiftmodule/%target-swiftinterface-name + +// 4. Import this module with -disable-building-interface should fail +// RUN: not %target-swift-frontend -typecheck -I %t %s -module-cache-path %t/ModuleCache -sdk %t -disable-building-interface + +// 5. Import this module without -disable-building-interface should succeed +// RUN: %target-swift-frontend -typecheck -I %t %s -module-cache-path %t/ModuleCache -sdk %t + +import PrebuiltModule + +func x(_ x: T) {} + +x(InPrebuiltModule.self) From 20995ae0bbf765d39cb28c63318e301c27cc2157 Mon Sep 17 00:00:00 2001 From: Cassie Jones Date: Mon, 5 Oct 2020 15:07:02 -0400 Subject: [PATCH 212/745] [build] Add FILES_MATCHING to CMakeLists.txt The bare "PATTERN" argument by default does nothing, you need either "EXCLUDE" or "FILES_MATCHING" to make it do something. This likely wasn't previously a problem because clang is only installing headers, but it should be fixed for robustness. --- localization/CMakeLists.txt | 1 + stdlib/public/SwiftShims/CMakeLists.txt | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/localization/CMakeLists.txt b/localization/CMakeLists.txt index dcaac27691e35..221fb264ee62d 100644 --- a/localization/CMakeLists.txt +++ b/localization/CMakeLists.txt @@ -20,6 +20,7 @@ swift_install_in_component( DIRECTORY ${CMAKE_BINARY_DIR}/share/swift/diagnostics/ DESTINATION "share/swift/diagnostics" COMPONENT compiler + FILES_MATCHING PATTERN "*.db" PATTERN "*.yaml" ) diff --git a/stdlib/public/SwiftShims/CMakeLists.txt b/stdlib/public/SwiftShims/CMakeLists.txt index 4c2b3c4e59588..0bd8c57a98505 100644 --- a/stdlib/public/SwiftShims/CMakeLists.txt +++ b/stdlib/public/SwiftShims/CMakeLists.txt @@ -197,13 +197,13 @@ endif() swift_install_in_component(DIRECTORY "${clang_headers_location}/" DESTINATION "lib/swift/clang" COMPONENT clang-builtin-headers - PATTERN "*.h") + FILES_MATCHING PATTERN "*.h") if(SWIFT_BUILD_STATIC_STDLIB) swift_install_in_component(DIRECTORY "${clang_headers_location}/" DESTINATION "lib/swift_static/clang" COMPONENT clang-builtin-headers - PATTERN "*.h") + FILES_MATCHING PATTERN "*.h") endif() @@ -227,4 +227,4 @@ file(TO_CMAKE_PATH "${LLVM_LIBRARY_OUTPUT_INTDIR}" swift_install_in_component(DIRECTORY "${_SWIFT_SHIMS_PATH_TO_CLANG_BUILD}/lib/clang" DESTINATION "lib" COMPONENT clang-builtin-headers-in-clang-resource-dir - PATTERN "*.h") + FILES_MATCHING PATTERN "*.h") From f3d668e3621b96ae37660ac621fb35b49b901d47 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Mon, 5 Oct 2020 16:14:08 +0200 Subject: [PATCH 213/745] SILOptimizer: fix ARC code motion problem for metatype casts. RCIdentityAnalysis must not look through casts which cast from a trivial type, like a metatype, to something which is retainable, like an AnyObject. On some platforms such casts dynamically allocate a ref-counted box for the metatype. Now, if the RCRoot of such an AnyObject would be a trivial type, ARC optimizations get confused and might eliminate a retain of such an object completely. rdar://problem/69900051 --- .../Analysis/RCIdentityAnalysis.cpp | 26 +++++++- .../retain_release_code_motion.sil | 59 +++++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/lib/SILOptimizer/Analysis/RCIdentityAnalysis.cpp b/lib/SILOptimizer/Analysis/RCIdentityAnalysis.cpp index e9e07a1a13f0a..fe5863f5daa11 100644 --- a/lib/SILOptimizer/Analysis/RCIdentityAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/RCIdentityAnalysis.cpp @@ -58,14 +58,31 @@ static bool isRCIdentityPreservingCast(ValueKind Kind) { } } +/// Returns a null SILValue if V has a trivial type, otherwise returns V. +/// +/// RCIdentityAnalysis must not look through casts which cast from a trivial +/// type, like a metatype, to something which is retainable, like an AnyObject. +/// On some platforms such casts dynamically allocate a ref-counted box for the +/// metatype. +/// Now, if the RCRoot of such an AnyObject would be a trivial type, ARC +/// optimizations get confused and might eliminate a retain of such an object +/// completely. +static SILValue noTrivialType(SILValue V, SILFunction *F) { + if (V->getType().isTrivial(*F)) + return SILValue(); + return V; +} + //===----------------------------------------------------------------------===// // RC Identity Root Instruction Casting //===----------------------------------------------------------------------===// static SILValue stripRCIdentityPreservingInsts(SILValue V) { // First strip off RC identity preserving casts. - if (isRCIdentityPreservingCast(V->getKind())) - return cast(V)->getOperand(0); + if (isRCIdentityPreservingCast(V->getKind())) { + auto *inst = cast(V); + return noTrivialType(inst->getOperand(0), inst->getFunction()); + } // Then if we have a struct_extract that is extracting a non-trivial member // from a struct with no other non-trivial members, a ref count operation on @@ -118,7 +135,7 @@ static SILValue stripRCIdentityPreservingInsts(SILValue V) { // purposes. if (auto *A = dyn_cast(V)) if (SILValue Result = A->getSingleTerminatorOperand()) - return Result; + return noTrivialType(Result, A->getFunction()); return SILValue(); } @@ -321,6 +338,9 @@ SILValue RCIdentityFunctionInfo::stripRCIdentityPreservingArgs(SILValue V, } unsigned IVListSize = IncomingValues.size(); + if (IVListSize == 1 && + !noTrivialType(IncomingValues[0].second, A->getFunction())) + return SILValue(); assert(IVListSize != 1 && "Should have been handled in " "stripRCIdentityPreservingInsts"); diff --git a/test/SILOptimizer/retain_release_code_motion.sil b/test/SILOptimizer/retain_release_code_motion.sil index 3cbb366c7f38b..a2efe718a07a2 100644 --- a/test/SILOptimizer/retain_release_code_motion.sil +++ b/test/SILOptimizer/retain_release_code_motion.sil @@ -718,6 +718,65 @@ bb2: return %9999 : $() } +// Don't remove a retain of an AnyObject which comes from a metatype. +// +// CHECK-LABEL: sil @conditional_metatype_cast +// CHECK: bb2([[ARG:%[0-9]+]] : $AnyObject): +// CHECK: strong_retain [[ARG]] +// CHECK: checked_cast_addr_br +// CHECK: } // end sil function 'conditional_metatype_cast' +sil @conditional_metatype_cast : $@convention(thin) () -> AnyObject { +bb0: + %0 = metatype $@thick Int.Type + checked_cast_br %0 : $@thick Int.Type to AnyObject, bb2, bb1 + +bb1: + unreachable + +bb2(%6 : $AnyObject): + strong_retain %6 : $AnyObject + %9 = alloc_stack $AnyObject + store %6 to %9 : $*AnyObject + %11 = alloc_stack $@thick Int.Type + checked_cast_addr_br take_always AnyObject in %9 : $*AnyObject to Int.Type in %11 : $*@thick Int.Type, bb3, bb4 + +bb3: + dealloc_stack %11 : $*@thick Int.Type + dealloc_stack %9 : $*AnyObject + return %6 : $AnyObject + +bb4: + unreachable +} + +// Don't remove a retain of an AnyObject which comes from a metatype. +// +// CHECK-LABEL: sil @unconditional_metatype_cast +// CHECK: [[O:%[0-9]+]] = unconditional_checked_cast +// CHECK: strong_retain [[O]] +// CHECK: checked_cast_addr_br +// CHECK: } // end sil function 'unconditional_metatype_cast' +sil @unconditional_metatype_cast : $@convention(thin) () -> AnyObject { +bb0: + %0 = metatype $@thick Int.Type + %6 = unconditional_checked_cast %0 : $@thick Int.Type to AnyObject + strong_retain %6 : $AnyObject + %9 = alloc_stack $AnyObject + store %6 to %9 : $*AnyObject + %11 = alloc_stack $@thick Int.Type + checked_cast_addr_br take_always AnyObject in %9 : $*AnyObject to Int.Type in %11 : $*@thick Int.Type, bb3, bb4 + +bb3: + dealloc_stack %11 : $*@thick Int.Type + dealloc_stack %9 : $*AnyObject + return %6 : $AnyObject + +bb4: + unreachable +} + + + // Hoist releases above dealloc_stack // CHECK-LABEL: sil @testReleaseHoistDeallocStack : $@convention(thin) (AnyObject) -> () { // CHECK: bb0(%0 : $AnyObject): From 86918102c48447ed28b2082f46ace2719768bb8f Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Mon, 5 Oct 2020 13:49:01 -0700 Subject: [PATCH 214/745] ABIChecker: abort if given baseline file is absent --- tools/swift-api-digester/swift-api-digester.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tools/swift-api-digester/swift-api-digester.cpp b/tools/swift-api-digester/swift-api-digester.cpp index df3d233e7d1b3..015fd03858b78 100644 --- a/tools/swift-api-digester/swift-api-digester.cpp +++ b/tools/swift-api-digester/swift-api-digester.cpp @@ -2809,11 +2809,6 @@ static std::string getCustomBaselinePath(llvm::Triple Triple, bool ABI) { static SDKNodeRoot *getBaselineFromJson(const char *Main, SDKContext &Ctx) { SwiftDeclCollector Collector(Ctx); - // If the baseline path has been given, honor that. - if (!options::BaselineFilePath.empty()) { - Collector.deSerialize(options::BaselineFilePath); - return Collector.getSDKRoot(); - } CompilerInvocation Invok; llvm::StringSet<> Modules; // We need to call prepareForDump to parse target triple. @@ -2823,7 +2818,10 @@ static SDKNodeRoot *getBaselineFromJson(const char *Main, SDKContext &Ctx) { assert(Modules.size() == 1 && "Cannot find builtin baseline for more than one module"); std::string Path; - if (!options::BaselineDirPath.empty()) { + // If the baseline path has been given, honor that. + if (!options::BaselineFilePath.empty()) { + Path = options::BaselineFilePath; + } else if (!options::BaselineDirPath.empty()) { Path = getCustomBaselinePath(Invok.getLangOptions().Target, Ctx.checkingABI()); } else if (options::UseEmptyBaseline) { From b23b335ac85695b628deaf199214464d62a042b2 Mon Sep 17 00:00:00 2001 From: ladd Date: Mon, 5 Oct 2020 14:16:59 -0700 Subject: [PATCH 215/745] Update CompilerPerformance.md Typo fix, and fix parameter `--raw-clang-ast` --- docs/CompilerPerformance.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CompilerPerformance.md b/docs/CompilerPerformance.md index c052facc4cd56..702132799676a 100644 --- a/docs/CompilerPerformance.md +++ b/docs/CompilerPerformance.md @@ -815,8 +815,8 @@ are helpful in such cases: - `llvm-bcanalyzer` can print (in rough form) the contents of LLVM bitcode streams, such as Swift module files and the PCH/PCM files clang stores its - serialized ASTs in. The latter requires combing `llvm-objdump` and - `llvm-bcanalyzer` in the following fashion: `llvm-objdump -raw-clang-ast + serialized ASTs in. The latter requires combining `llvm-objdump` and + `llvm-bcanalyzer` in the following fashion: `llvm-objdump --raw-clang-ast file.pcm | llvm-bcanalyzer -dump` - `llvm-dwarfdump` and `llvm-dis` can be used to print textual representations From 82e9935885bed95b0091476bfda95f4fdae14e9b Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 5 Oct 2020 18:40:43 -0700 Subject: [PATCH 216/745] Correct the Serialization of Embedded Swift Dependencies llvm-bcanalyzer does *not* like it when there are multiple block info metadata blocks in the same bitstream file. This patch will skip the emission of that and just jump straight to the metadata block when we're not reading a "standalone" incremental dependency file. While I'm here, also add the right block abbreviation info so we can get a decent dump from llvm-bcanalyzer --- .../swift/AST/FineGrainedDependencyFormat.h | 3 +- lib/AST/FineGrainedDependencies.cpp | 2 +- lib/AST/FineGrainedDependencyFormat.cpp | 49 +++++++++++-------- lib/Serialization/CMakeLists.txt | 1 - lib/Serialization/Serialization.cpp | 12 ++++- lib/Serialization/SerializeIncremental.cpp | 34 ------------- test/Serialization/embedded-swiftdeps.swift | 9 ++++ 7 files changed, 51 insertions(+), 59 deletions(-) delete mode 100644 lib/Serialization/SerializeIncremental.cpp create mode 100644 test/Serialization/embedded-swiftdeps.swift diff --git a/include/swift/AST/FineGrainedDependencyFormat.h b/include/swift/AST/FineGrainedDependencyFormat.h index 9bfe743afef9f..78b95e3346a45 100644 --- a/include/swift/AST/FineGrainedDependencyFormat.h +++ b/include/swift/AST/FineGrainedDependencyFormat.h @@ -136,7 +136,8 @@ bool writeFineGrainedDependencyGraphToPath(DiagnosticEngine &diags, const SourceFileDepGraph &g); void writeFineGrainedDependencyGraph(llvm::BitstreamWriter &Out, - const SourceFileDepGraph &g); + const SourceFileDepGraph &g, + bool standalone); } // namespace fine_grained_dependencies } // namespace swift diff --git a/lib/AST/FineGrainedDependencies.cpp b/lib/AST/FineGrainedDependencies.cpp index 594b78931eaac..5a16f674b704e 100644 --- a/lib/AST/FineGrainedDependencies.cpp +++ b/lib/AST/FineGrainedDependencies.cpp @@ -61,7 +61,7 @@ SourceFileDepGraph::loadFromBuffer(llvm::MemoryBuffer &buffer) { Optional SourceFileDepGraph::loadFromSwiftModuleBuffer(llvm::MemoryBuffer &buffer) { SourceFileDepGraph fg; - if (swift::fine_grained_dependencies:: + if (!swift::fine_grained_dependencies:: readFineGrainedDependencyGraphFromSwiftModule(buffer, fg)) return None; return Optional(std::move(fg)); diff --git a/lib/AST/FineGrainedDependencyFormat.cpp b/lib/AST/FineGrainedDependencyFormat.cpp index 2eacef0a22156..d7570d414e85a 100644 --- a/lib/AST/FineGrainedDependencyFormat.cpp +++ b/lib/AST/FineGrainedDependencyFormat.cpp @@ -44,7 +44,7 @@ class Deserializer { public: Deserializer(llvm::MemoryBufferRef Data) : Cursor(Data) {} - bool readFineGrainedDependencyGraph(SourceFileDepGraph &g); + bool readFineGrainedDependencyGraph(SourceFileDepGraph &g, bool standalone); bool readFineGrainedDependencyGraphFromSwiftModule(SourceFileDepGraph &g); }; @@ -151,13 +151,14 @@ static llvm::Optional getDeclAspect(unsigned declAspect) { return None; } -bool Deserializer::readFineGrainedDependencyGraph(SourceFileDepGraph &g) { +bool Deserializer::readFineGrainedDependencyGraph(SourceFileDepGraph &g, + bool standalone) { using namespace record_block; - if (readSignature()) + if (standalone && readSignature()) return true; - if (enterTopLevelBlock()) + if (standalone && enterTopLevelBlock()) return true; if (readMetadata()) @@ -260,7 +261,7 @@ bool Deserializer::readFineGrainedDependencyGraph(SourceFileDepGraph &g) { bool swift::fine_grained_dependencies::readFineGrainedDependencyGraph( llvm::MemoryBuffer &buffer, SourceFileDepGraph &g) { Deserializer deserializer(buffer.getMemBufferRef()); - return deserializer.readFineGrainedDependencyGraph(g); + return deserializer.readFineGrainedDependencyGraph(g, /*standalone*/true); } bool swift::fine_grained_dependencies::readFineGrainedDependencyGraph( @@ -322,7 +323,8 @@ class Serializer { Serializer(llvm::BitstreamWriter &ExistingOut) : Out(ExistingOut) {} public: - void writeFineGrainedDependencyGraph(const SourceFileDepGraph &g); + void writeFineGrainedDependencyGraph(const SourceFileDepGraph &g, + bool standalone); }; } // end namespace @@ -382,11 +384,15 @@ void Serializer::writeMetadata() { } void -Serializer::writeFineGrainedDependencyGraph(const SourceFileDepGraph &g) { - writeSignature(); - writeBlockInfoBlock(); - - llvm::BCBlockRAII restoreBlock(Out, RECORD_BLOCK_ID, 8); +Serializer::writeFineGrainedDependencyGraph(const SourceFileDepGraph &g, + bool standalone) { + auto blockID = INCREMENTAL_INFORMATION_BLOCK_ID; + if (standalone) { + writeSignature(); + writeBlockInfoBlock(); + blockID = RECORD_BLOCK_ID; + } + llvm::BCBlockRAII restoreBlock(Out, blockID, 8); using namespace record_block; @@ -468,9 +474,9 @@ unsigned Serializer::getIdentifier(StringRef str) { } void swift::fine_grained_dependencies::writeFineGrainedDependencyGraph( - llvm::BitstreamWriter &Out, const SourceFileDepGraph &g) { + llvm::BitstreamWriter &Out, const SourceFileDepGraph &g, bool standalone) { Serializer serializer{Out}; - serializer.writeFineGrainedDependencyGraph(g); + serializer.writeFineGrainedDependencyGraph(g, standalone); } bool swift::fine_grained_dependencies::writeFineGrainedDependencyGraphToPath( @@ -480,7 +486,7 @@ bool swift::fine_grained_dependencies::writeFineGrainedDependencyGraphToPath( return withOutputFile(diags, path, [&](llvm::raw_ostream &out) { SmallVector Buffer; llvm::BitstreamWriter Writer{Buffer}; - writeFineGrainedDependencyGraph(Writer, g); + writeFineGrainedDependencyGraph(Writer, g, /*standalone*/ true); out.write(Buffer.data(), Buffer.size()); out.flush(); return false; @@ -516,7 +522,7 @@ static bool enterTopLevelModuleBlock(llvm::BitstreamCursor &cursor, unsigned ID, if (next.Kind != llvm::BitstreamEntry::SubBlock) return false; - if (next.ID == RECORD_BLOCK_ID) { + if (next.ID == llvm::bitc::BLOCKINFO_BLOCK_ID) { if (shouldReadBlockInfo) { if (!cursor.ReadBlockInfoBlock()) return false; @@ -531,7 +537,6 @@ static bool enterTopLevelModuleBlock(llvm::BitstreamCursor &cursor, unsigned ID, return false; if (llvm::Error Err = cursor.EnterSubBlock(ID)) { - // FIXME this drops the error on the floor. consumeError(std::move(Err)); return false; } @@ -549,12 +554,12 @@ bool swift::fine_grained_dependencies:: bool Deserializer::readFineGrainedDependencyGraphFromSwiftModule( SourceFileDepGraph &g) { if (!checkModuleSignature(Cursor, {0xE2, 0x9C, 0xA8, 0x0E}) || - !enterTopLevelModuleBlock(Cursor, RECORD_BLOCK_ID, false)) { + !enterTopLevelModuleBlock(Cursor, llvm::bitc::FIRST_APPLICATION_BLOCKID, false)) { return false; } llvm::BitstreamEntry topLevelEntry; - + bool ReadFineGrainedDependencies = false; while (!Cursor.AtEndOfStream()) { llvm::Expected maybeEntry = Cursor.advance(llvm::BitstreamCursor::AF_DontPopBlockAtEnd); @@ -573,7 +578,11 @@ bool Deserializer::readFineGrainedDependencyGraphFromSwiftModule( consumeError(std::move(Err)); return false; } - readFineGrainedDependencyGraph(g); + if (readFineGrainedDependencyGraph(g, /*standalone*/ false)) { + break; + } + + ReadFineGrainedDependencies = true; break; } @@ -587,5 +596,5 @@ bool Deserializer::readFineGrainedDependencyGraphFromSwiftModule( } } - return false; + return ReadFineGrainedDependencies; } diff --git a/lib/Serialization/CMakeLists.txt b/lib/Serialization/CMakeLists.txt index 1e7c14b1ce161..c10fa09a159f4 100644 --- a/lib/Serialization/CMakeLists.txt +++ b/lib/Serialization/CMakeLists.txt @@ -9,7 +9,6 @@ add_swift_host_library(swiftSerialization STATIC SerializedModuleLoader.cpp SerializedSILLoader.cpp SerializeDoc.cpp - SerializeIncremental.cpp SerializeSIL.cpp LLVM_LINK_COMPONENTS diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index df2c0ca859e2d..78eb2b7eda821 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -861,6 +861,13 @@ void Serializer::writeBlockInfoBlock() { BLOCK_RECORD(sil_index_block, SIL_DIFFERENTIABILITY_WITNESS_NAMES); BLOCK_RECORD(sil_index_block, SIL_DIFFERENTIABILITY_WITNESS_OFFSETS); + BLOCK(INCREMENTAL_INFORMATION_BLOCK); + BLOCK_RECORD(fine_grained_dependencies::record_block, METADATA); + BLOCK_RECORD(fine_grained_dependencies::record_block, SOURCE_FILE_DEP_GRAPH_NODE); + BLOCK_RECORD(fine_grained_dependencies::record_block, FINGERPRINT_NODE); + BLOCK_RECORD(fine_grained_dependencies::record_block, DEPENDS_ON_DEFINITION_NODE); + BLOCK_RECORD(fine_grained_dependencies::record_block, IDENTIFIER_NODE); + #undef BLOCK #undef BLOCK_RECORD } @@ -5231,8 +5238,9 @@ void Serializer::writeToStream( S.writeInputBlock(options); S.writeSIL(SILMod, options.SerializeAllSIL); S.writeAST(DC); - if (options.ExperimentalCrossModuleIncrementalInfo) { - S.writeIncrementalInfo(DepGraph); + if (options.ExperimentalCrossModuleIncrementalInfo && DepGraph) { + fine_grained_dependencies::writeFineGrainedDependencyGraph( + S.Out, *DepGraph, /*standalone*/ false); } } diff --git a/lib/Serialization/SerializeIncremental.cpp b/lib/Serialization/SerializeIncremental.cpp deleted file mode 100644 index 02eca8660acb4..0000000000000 --- a/lib/Serialization/SerializeIncremental.cpp +++ /dev/null @@ -1,34 +0,0 @@ -//===--- SerializeIncremental.cpp - Write incremental swiftdeps -----------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "serialize-incremental" -#include "Serialization.h" -#include "swift/AST/FineGrainedDependencyFormat.h" - -#include - -using namespace swift; -using namespace swift::serialization; -using namespace llvm::support; -using llvm::BCBlockRAII; - -void Serializer::writeIncrementalInfo( - const fine_grained_dependencies::SourceFileDepGraph *DepGraph) { - if (!DepGraph) - return; - - { - BCBlockRAII restoreBlock(Out, INCREMENTAL_INFORMATION_BLOCK_ID, 5); - swift::fine_grained_dependencies::writeFineGrainedDependencyGraph( - Out, *DepGraph); - } -} diff --git a/test/Serialization/embedded-swiftdeps.swift b/test/Serialization/embedded-swiftdeps.swift new file mode 100644 index 0000000000000..51b2fc5641be8 --- /dev/null +++ b/test/Serialization/embedded-swiftdeps.swift @@ -0,0 +1,9 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -module-name EmbeddedIncremental -o %t/EmbeddedIncremental~partial.swiftmodule -primary-file %s +// RUN: %target-swift-frontend -enable-experimental-cross-module-incremental-build -emit-module -module-name EmbeddedIncremental -o %t/EmbeddedIncremental.swiftmodule %t/EmbeddedIncremental~partial.swiftmodule +// RUN: llvm-bcanalyzer -dump %t/EmbeddedIncremental.swiftmodule | %FileCheck %s --dump-input=always + +public struct AnyWindows {} + +// CHECK: From d7cd605c3129bd567f5be18b57eec33135dc439e Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 5 Oct 2020 18:42:28 -0700 Subject: [PATCH 217/745] Adjust the Registration of the Pseudo-Job for External Incremental Dependencies Register the module the external dependencies ride in with the pseudo-job in the Driver. This is a hack. --- include/swift/Driver/FineGrainedDependencyDriverGraph.h | 2 -- lib/Driver/Compilation.cpp | 4 ---- lib/Driver/FineGrainedDependencyDriverGraph.cpp | 4 ++-- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/include/swift/Driver/FineGrainedDependencyDriverGraph.h b/include/swift/Driver/FineGrainedDependencyDriverGraph.h index 07029becc8be8..8ae3ac783d332 100644 --- a/include/swift/Driver/FineGrainedDependencyDriverGraph.h +++ b/include/swift/Driver/FineGrainedDependencyDriverGraph.h @@ -278,8 +278,6 @@ class ModuleDepGraph { assert(swiftDeps.hasValue() && "Don't call me for expats."); auto iter = jobsBySwiftDeps.find(swiftDeps.getValue()); assert(iter != jobsBySwiftDeps.end() && "All jobs should be tracked."); - assert(getSwiftDeps(iter->second) == swiftDeps.getValue() && - "jobsBySwiftDeps should be inverse of getSwiftDeps."); return iter->second; } diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index 11332f958b731..c373d401a2542 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -1161,10 +1161,6 @@ namespace driver { continue; } - // Is this module out of date? If not, just keep searching. - if (Comp.getLastBuildTime() >= depStatus.getLastModificationTime()) - continue; - // Can we run a cross-module incremental build at all? If not, fallback. if (!Comp.getEnableCrossModuleIncrementalBuild()) { fallbackToExternalBehavior(external); diff --git a/lib/Driver/FineGrainedDependencyDriverGraph.cpp b/lib/Driver/FineGrainedDependencyDriverGraph.cpp index d5ab5a5228b57..f6e260611a014 100644 --- a/lib/Driver/FineGrainedDependencyDriverGraph.cpp +++ b/lib/Driver/FineGrainedDependencyDriverGraph.cpp @@ -120,7 +120,7 @@ ModuleDepGraph::Changes ModuleDepGraph::loadFromSwiftModuleBuffer( SourceFileDepGraph::loadFromSwiftModuleBuffer(buffer); if (!sourceFileDepGraph) return None; - registerJob(Cmd); + jobsBySwiftDeps.insert(std::make_pair(buffer.getBufferIdentifier(), Cmd)); auto changes = integrate(*sourceFileDepGraph, buffer.getBufferIdentifier()); if (verifyFineGrainedDependencyGraphAfterEveryImport) verify(); @@ -445,11 +445,11 @@ bool ModuleDepGraph::recordWhatUseDependsUpon( StringRef externalSwiftDeps = def->getKey().getName(); if (def->getKey().getKind() == NodeKind::externalDepend) { externalDependencies.insert(externalSwiftDeps.str()); + useHasNewExternalDependency = true; } else if (def->getKey().getKind() == NodeKind::incrementalExternalDepend) { incrementalExternalDependencies.insert(externalSwiftDeps.str()); } - useHasNewExternalDependency = true; } }); return useHasNewExternalDependency; From a77f059c8292c157f07a4de9a2d1ee9597c5b57d Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 5 Oct 2020 18:50:29 -0700 Subject: [PATCH 218/745] =?UTF-8?q?Turn=20on=20Cross-Module=C2=A0Increment?= =?UTF-8?q?al=20Dependencies!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cross-Module incremental dependencies are a new experimental mode of the Swift driver and frontend. Through a tight partnership between the two, we enable the driver to have far greater visibility into the dependency structure of a Swift module. Rather than invent a new model, we have chosen to extend the existing incremental compilation model that works for a single module to multiple modules. To do this, we need the frontend to emit Swift dependencies in a form the driver can consume. We could emit these metadata in the form of an extra supplementary output that summarizes the contents of a generated module. However, this approach comes with a number of downsides: - It requires additional integration with the build system - It assumes swiftmodule files will be consumed directly from the build directory; they are not - It incorrectly assumes a swiftmodule has but one interface. Taken in aggregate, a swiftmodule directory has one interface *per triple* Given this, the approach we take here is to encode these dependencies directly into the swiftmodule file itself. When frontends load these souped-up incremental swiftmodule files, they record in their own swiftdeps files that they depend on an incremental swiftmodule. Upon the next build, the driver is then able to read that module file, extract the swiftdeps information from it, and use it to influence the way it schedules jobs. The sum total is that we neatly extend the intra-module case of incremental builds to the inter-module case by treating swiftmodule inputs not as opaque entities, but as "big ol' flat Swift files" that just export an interface like any other Swift file within the module. As a further optimization, and because clients literally cannot observe this aspect of the incremental build, we only serialize the provides (the "defs" side of a "use-def" edge) when emitting swiftdeps metadata into swiftmodule files. rdar://69595010 --- include/swift/Option/Options.td | 1 + lib/Serialization/SerializedModuleLoader.cpp | 19 +++++---- .../CrossModule/Inputs/linear/A.json | 11 +++++ .../CrossModule/Inputs/linear/A.swift | 1 + .../CrossModule/Inputs/linear/B.json | 11 +++++ .../CrossModule/Inputs/linear/B.swift | 5 +++ .../CrossModule/Inputs/linear/C.json | 11 +++++ .../CrossModule/Inputs/linear/C.swift | 7 ++++ .../CrossModule/Inputs/transitive/A.json | 11 +++++ .../CrossModule/Inputs/transitive/A.swift | 9 ++++ .../CrossModule/Inputs/transitive/B.json | 11 +++++ .../CrossModule/Inputs/transitive/B.swift | 5 +++ .../CrossModule/Inputs/transitive/C.json | 11 +++++ .../CrossModule/Inputs/transitive/C.swift | 7 ++++ test/Incremental/CrossModule/linear.swift | 38 +++++++++++++++++ test/Incremental/CrossModule/transitive.swift | 41 +++++++++++++++++++ 16 files changed, 192 insertions(+), 7 deletions(-) create mode 100644 test/Incremental/CrossModule/Inputs/linear/A.json create mode 100644 test/Incremental/CrossModule/Inputs/linear/A.swift create mode 100644 test/Incremental/CrossModule/Inputs/linear/B.json create mode 100644 test/Incremental/CrossModule/Inputs/linear/B.swift create mode 100644 test/Incremental/CrossModule/Inputs/linear/C.json create mode 100644 test/Incremental/CrossModule/Inputs/linear/C.swift create mode 100644 test/Incremental/CrossModule/Inputs/transitive/A.json create mode 100644 test/Incremental/CrossModule/Inputs/transitive/A.swift create mode 100644 test/Incremental/CrossModule/Inputs/transitive/B.json create mode 100644 test/Incremental/CrossModule/Inputs/transitive/B.swift create mode 100644 test/Incremental/CrossModule/Inputs/transitive/C.json create mode 100644 test/Incremental/CrossModule/Inputs/transitive/C.swift create mode 100644 test/Incremental/CrossModule/linear.swift create mode 100644 test/Incremental/CrossModule/transitive.swift diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 3c412e153c7dc..48e01d78b14f1 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -573,6 +573,7 @@ def experimental_cxx_stdlib : def enable_experimental_cross_module_incremental_build : Flag<["-"], "enable-experimental-cross-module-incremental-build">, + Flags<[FrontendOption]>, HelpText<"(experimental) Enable cross-module incremental build metadata and " "driver scheduling">; diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index 98e499b6aebba..c3ae09130cf70 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -973,13 +973,6 @@ SerializedModuleLoaderBase::loadModule(SourceLoc importLoc, isFramework, isSystemModule)) { return nullptr; } - if (dependencyTracker) { - // Don't record cached artifacts as dependencies. - StringRef DepPath = moduleInputBuffer->getBufferIdentifier(); - if (!isCached(DepPath)) { - dependencyTracker->addDependency(DepPath, /*isSystem=*/false); - } - } assert(moduleInputBuffer); @@ -997,6 +990,18 @@ SerializedModuleLoaderBase::loadModule(SourceLoc importLoc, } else { M->setFailedToLoad(); } + + if (dependencyTracker && file) { + auto DepPath = file->getFilename(); + // Don't record cached artifacts as dependencies. + if (!isCached(DepPath)) { + if (M->hasIncrementalInfo()) { + dependencyTracker->addIncrementalDependency(DepPath); + } else { + dependencyTracker->addDependency(DepPath, /*isSystem=*/false); + } + } + } return M; } diff --git a/test/Incremental/CrossModule/Inputs/linear/A.json b/test/Incremental/CrossModule/Inputs/linear/A.json new file mode 100644 index 0000000000000..50005aae33f05 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/linear/A.json @@ -0,0 +1,11 @@ +{ + "A.swift": { + "object": "./A.o", + "swift-dependencies": "./A.swiftdeps", + "swiftmodule": "./A~partial.swiftmodule", + "swiftdoc": "./A.swiftdoc", + }, + "": { + "swift-dependencies": "./A~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/linear/A.swift b/test/Incremental/CrossModule/Inputs/linear/A.swift new file mode 100644 index 0000000000000..720de06824628 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/linear/A.swift @@ -0,0 +1 @@ +import B diff --git a/test/Incremental/CrossModule/Inputs/linear/B.json b/test/Incremental/CrossModule/Inputs/linear/B.json new file mode 100644 index 0000000000000..30c08e2ae8a00 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/linear/B.json @@ -0,0 +1,11 @@ +{ + "B.swift": { + "object": "./B.o", + "swift-dependencies": "./B.swiftdeps", + "swiftmodule": "./B~partial.swiftmodule", + "swiftdoc": "./B.swiftdoc", + }, + "": { + "swift-dependencies": "./B~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/linear/B.swift b/test/Incremental/CrossModule/Inputs/linear/B.swift new file mode 100644 index 0000000000000..14f2e9a84b5c5 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/linear/B.swift @@ -0,0 +1,5 @@ +import C + +public func fromB() { + return fromC() +} diff --git a/test/Incremental/CrossModule/Inputs/linear/C.json b/test/Incremental/CrossModule/Inputs/linear/C.json new file mode 100644 index 0000000000000..34775828548e8 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/linear/C.json @@ -0,0 +1,11 @@ +{ + "C.swift": { + "object": "./C.o", + "swift-dependencies": "./C.swiftdeps", + "swiftmodule": "./C~partial.swiftmodule", + "swiftdoc": "./C.swiftdoc", + }, + "": { + "swift-dependencies": "./C~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/linear/C.swift b/test/Incremental/CrossModule/Inputs/linear/C.swift new file mode 100644 index 0000000000000..d06ab94ab007c --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/linear/C.swift @@ -0,0 +1,7 @@ +#if OLD +public func fromC() {} +#elseif NEW +public func fromC(parameter: Int = 0) {} +#else +#error("test must define one of NEW or OLD macros") +#endif diff --git a/test/Incremental/CrossModule/Inputs/transitive/A.json b/test/Incremental/CrossModule/Inputs/transitive/A.json new file mode 100644 index 0000000000000..50005aae33f05 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/transitive/A.json @@ -0,0 +1,11 @@ +{ + "A.swift": { + "object": "./A.o", + "swift-dependencies": "./A.swiftdeps", + "swiftmodule": "./A~partial.swiftmodule", + "swiftdoc": "./A.swiftdoc", + }, + "": { + "swift-dependencies": "./A~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/transitive/A.swift b/test/Incremental/CrossModule/Inputs/transitive/A.swift new file mode 100644 index 0000000000000..84d2bead2d914 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/transitive/A.swift @@ -0,0 +1,9 @@ +import B +import C + +public func use() { + fromB() + #if USEC + fromC() + #endif +} diff --git a/test/Incremental/CrossModule/Inputs/transitive/B.json b/test/Incremental/CrossModule/Inputs/transitive/B.json new file mode 100644 index 0000000000000..30c08e2ae8a00 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/transitive/B.json @@ -0,0 +1,11 @@ +{ + "B.swift": { + "object": "./B.o", + "swift-dependencies": "./B.swiftdeps", + "swiftmodule": "./B~partial.swiftmodule", + "swiftdoc": "./B.swiftdoc", + }, + "": { + "swift-dependencies": "./B~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/transitive/B.swift b/test/Incremental/CrossModule/Inputs/transitive/B.swift new file mode 100644 index 0000000000000..14f2e9a84b5c5 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/transitive/B.swift @@ -0,0 +1,5 @@ +import C + +public func fromB() { + return fromC() +} diff --git a/test/Incremental/CrossModule/Inputs/transitive/C.json b/test/Incremental/CrossModule/Inputs/transitive/C.json new file mode 100644 index 0000000000000..34775828548e8 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/transitive/C.json @@ -0,0 +1,11 @@ +{ + "C.swift": { + "object": "./C.o", + "swift-dependencies": "./C.swiftdeps", + "swiftmodule": "./C~partial.swiftmodule", + "swiftdoc": "./C.swiftdoc", + }, + "": { + "swift-dependencies": "./C~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/transitive/C.swift b/test/Incremental/CrossModule/Inputs/transitive/C.swift new file mode 100644 index 0000000000000..d06ab94ab007c --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/transitive/C.swift @@ -0,0 +1,7 @@ +#if OLD +public func fromC() {} +#elseif NEW +public func fromC(parameter: Int = 0) {} +#else +#error("test must define one of NEW or OLD macros") +#endif diff --git a/test/Incremental/CrossModule/linear.swift b/test/Incremental/CrossModule/linear.swift new file mode 100644 index 0000000000000..73ae072d2ff03 --- /dev/null +++ b/test/Incremental/CrossModule/linear.swift @@ -0,0 +1,38 @@ +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/linear/* %t + +// +// This test establishes a "linear" chain of modules that import one another +// and ensures that a cross-module incremental build does not needlessly +// rebuild the transitive dependency. +// +// Module C Module B Module A +// -------- -> -------- -> -------- +// | ^ +// | | +// ------------X------------ + +// +// Set up a clean incremental build of all three modules +// + +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DOLD %t/C.swift +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift + +// +// Now change C and ensure that B rebuilds but A does not +// + +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s + +// MODULE-C: Incremental compilation has been disabled + +// MODULE-B: Queuing because of incremental external dependencies: {compile: B.o <= B.swift} +// MODULE-B: Job finished: {compile: B.o <= B.swift} +// MODULE-B: Job finished: {merge-module: B.swiftmodule <= B.o} + +// MODULE-A: Job skipped: {compile: A.o <= A.swift} +// MODULE-A: Job finished: {merge-module: A.swiftmodule <= A.o} diff --git a/test/Incremental/CrossModule/transitive.swift b/test/Incremental/CrossModule/transitive.swift new file mode 100644 index 0000000000000..f281f1e8e014f --- /dev/null +++ b/test/Incremental/CrossModule/transitive.swift @@ -0,0 +1,41 @@ +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/transitive/* %t + +// +// This test establishes a "transitive" chain of modules that import one another +// and ensures that a cross-module incremental build rebuilds all modules +// involved in the chain. +// +// Module C Module B Module A +// -------- -> -------- -> -------- +// | ^ +// | | +// ------------------------- +// + +// +// Set up a clean incremental build of all three modules +// + +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DOLD %t/C.swift +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/B.swift +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/A.swift + +// +// Now change C and ensure that B and A rebuild because of the change to +// an incremental external dependency. +// + +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s + +// MODULE-C: Incremental compilation has been disabled + +// MODULE-B: Queuing because of incremental external dependencies: {compile: B.o <= B.swift} +// MODULE-B: Job finished: {compile: B.o <= B.swift} +// MODULE-B: Job finished: {merge-module: B.swiftmodule <= B.o} + +// MODULE-A: Queuing because of incremental external dependencies: {compile: A.o <= A.swift} +// MODULE-A: Job finished: {compile: A.o <= A.swift} +// MODULE-A: Job finished: {merge-module: A.swiftmodule <= A.o} From ee88152d6baa1bef03790d3e6acfa7448ef467f3 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 2 Oct 2020 13:58:08 -0700 Subject: [PATCH 219/745] [Concurrency] First steps towards async CC. Here, the following is implemented: - Construction of SwiftContext struct with the fields needed for calling functions. - Allocating and deallocating these swift context via runtime calls before calling async functions and after returning from them. - Storing arguments (including bindings and the self parameter but not including protocol fields for witness methods) and returns (both direct and indirect). - Calling async functions. Additional things that still need to be done: - protocol extension methods - protocol witness methods - storing yields - partial applies --- lib/IRGen/CallEmission.h | 30 +- lib/IRGen/EntryPointArgumentEmission.h | 46 + lib/IRGen/Explosion.h | 5 + lib/IRGen/GenBuiltin.cpp | 8 +- lib/IRGen/GenCall.cpp | 832 +++++++++++++----- lib/IRGen/GenCall.h | 169 +++- lib/IRGen/GenFunc.cpp | 3 +- lib/IRGen/GenFunc.h | 2 + lib/IRGen/GenObjC.cpp | 16 +- lib/IRGen/GenProto.cpp | 83 +- lib/IRGen/GenProto.h | 8 +- lib/IRGen/IRGenFunction.h | 20 +- lib/IRGen/IRGenSIL.cpp | 360 ++++++-- lib/IRGen/NecessaryBindings.h | 16 +- .../run-call-classinstance-int64-to-void.sil | 105 +++ .../run-call-classinstance-void-to-void.sil | 102 +++ .../async/run-call-existential-to-void.sil | 78 ++ .../async/run-call-generic-to-generic.sil | 54 ++ test/IRGen/async/run-call-generic-to-void.sil | 45 + .../run-call-genericEquatable-x2-to-bool.sil | 59 ++ .../run-call-int64-and-int64-to-void.sil | 44 + test/IRGen/async/run-call-int64-to-void.sil | 39 + .../run-call-structinstance-int64-to-void.sil | 65 ++ .../run-call-void-throws-to-int-throwing.sil | 126 +++ ...ing_call-async-nothrow_call-sync-throw.sil | 152 ++++ ...hrows-to-int-throwing_call-async-throw.sil | 138 +++ ...ing_call-sync-nothrow_call-async-throw.sil | 153 ++++ ...throws-to-int-throwing_call-sync-throw.sil | 137 +++ .../async/run-call-void-to-existential.sil | 83 ++ .../run-call-void-to-int64-and-int64.sil | 47 + test/IRGen/async/run-call-void-to-int64.sil | 40 + .../async/run-call-void-to-struct_large.sil | 132 +++ test/Inputs/print-shims.swift | 19 + 33 files changed, 2892 insertions(+), 324 deletions(-) create mode 100644 lib/IRGen/EntryPointArgumentEmission.h create mode 100644 test/IRGen/async/run-call-classinstance-int64-to-void.sil create mode 100644 test/IRGen/async/run-call-classinstance-void-to-void.sil create mode 100644 test/IRGen/async/run-call-existential-to-void.sil create mode 100644 test/IRGen/async/run-call-generic-to-generic.sil create mode 100644 test/IRGen/async/run-call-generic-to-void.sil create mode 100644 test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil create mode 100644 test/IRGen/async/run-call-int64-and-int64-to-void.sil create mode 100644 test/IRGen/async/run-call-int64-to-void.sil create mode 100644 test/IRGen/async/run-call-structinstance-int64-to-void.sil create mode 100644 test/IRGen/async/run-call-void-throws-to-int-throwing.sil create mode 100644 test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil create mode 100644 test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil create mode 100644 test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil create mode 100644 test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil create mode 100644 test/IRGen/async/run-call-void-to-existential.sil create mode 100644 test/IRGen/async/run-call-void-to-int64-and-int64.sil create mode 100644 test/IRGen/async/run-call-void-to-int64.sil create mode 100644 test/IRGen/async/run-call-void-to-struct_large.sil create mode 100644 test/Inputs/print-shims.swift diff --git a/lib/IRGen/CallEmission.h b/lib/IRGen/CallEmission.h index c48ed42f0062d..dcab4708bb933 100644 --- a/lib/IRGen/CallEmission.h +++ b/lib/IRGen/CallEmission.h @@ -33,10 +33,15 @@ struct WitnessMetadata; /// A plan for emitting a series of calls. class CallEmission { + enum class State { Emitting, Finished }; + State state = State::Emitting; + public: IRGenFunction &IGF; -private: +protected: + llvm::Value *selfValue; + /// The builtin/special arguments to pass to the call. SmallVector Args; @@ -57,21 +62,21 @@ class CallEmission { /// RemainingArgsForCallee, at least between calls. bool EmittedCall; - void setFromCallee(); + virtual void setFromCallee(); void emitToUnmappedMemory(Address addr); void emitToUnmappedExplosion(Explosion &out); + virtual void emitCallToUnmappedExplosion(llvm::CallInst *call, Explosion &out) = 0; void emitYieldsToExplosion(Explosion &out); llvm::CallInst *emitCallSite(); + CallEmission(IRGenFunction &IGF, llvm::Value *selfValue, Callee &&callee) + : IGF(IGF), selfValue(selfValue), CurCallee(std::move(callee)) {} + public: - CallEmission(IRGenFunction &IGF, Callee &&callee) - : IGF(IGF), CurCallee(std::move(callee)) { - setFromCallee(); - } CallEmission(const CallEmission &other) = delete; CallEmission(CallEmission &&other); CallEmission &operator=(const CallEmission &other) = delete; - ~CallEmission(); + virtual ~CallEmission(); const Callee &getCallee() const { return CurCallee; } @@ -79,9 +84,13 @@ class CallEmission { return CurCallee.getSubstitutions(); } + virtual void begin(); + virtual void end(); + virtual SILType getParameterType(unsigned index) = 0; /// Set the arguments to the function from an explosion. - void setArgs(Explosion &arg, bool isOutlined, - WitnessMetadata *witnessMetadata = nullptr); + virtual void setArgs(Explosion &arg, bool isOutlined, + WitnessMetadata *witnessMetadata); + virtual Address getCalleeErrorSlot(SILType errorType) = 0; void addAttribute(unsigned Index, llvm::Attribute::AttrKind Attr); @@ -100,6 +109,9 @@ class CallEmission { } }; +std::unique_ptr +getCallEmission(IRGenFunction &IGF, llvm::Value *selfValue, Callee &&callee); + } // end namespace irgen } // end namespace swift diff --git a/lib/IRGen/EntryPointArgumentEmission.h b/lib/IRGen/EntryPointArgumentEmission.h new file mode 100644 index 0000000000000..5358d24d233e1 --- /dev/null +++ b/lib/IRGen/EntryPointArgumentEmission.h @@ -0,0 +1,46 @@ +//===-- EntryPointArgumentEmission.h - Emit function entries. -------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#pragma once + +namespace llvm { +class Value; +} + +namespace swift { +namespace irgen { + +class Explosion; + +class EntryPointArgumentEmission { + +public: + virtual ~EntryPointArgumentEmission() {} + virtual bool requiresIndirectResult(SILType retType) = 0; + virtual llvm::Value *getIndirectResultForFormallyDirectResult() = 0; + virtual llvm::Value *getIndirectResult(unsigned index) = 0; +}; + +class NativeCCEntryPointArgumentEmission + : public virtual EntryPointArgumentEmission { + +public: + virtual llvm::Value *getCallerErrorResultArgument() = 0; + virtual llvm::Value *getContext() = 0; + virtual Explosion getArgumentExplosion(unsigned index, unsigned size) = 0; + virtual llvm::Value *getSelfWitnessTable() = 0; + virtual llvm::Value *getSelfMetadata() = 0; + virtual llvm::Value *getCoroutineBuffer() = 0; +}; + +} // end namespace irgen +} // end namespace swift diff --git a/lib/IRGen/Explosion.h b/lib/IRGen/Explosion.h index db24fa97a8cb2..e77bd9749084e 100644 --- a/lib/IRGen/Explosion.h +++ b/lib/IRGen/Explosion.h @@ -152,6 +152,11 @@ class Explosion { return Values[NextValue-1]; } + llvm::Value *peek(unsigned n) { + assert(n < Values.size()); + return Values[n]; + } + /// Claim and remove the last item in the array. /// Unlike the normal 'claim' methods, the item is gone forever. llvm::Value *takeLast() { diff --git a/lib/IRGen/GenBuiltin.cpp b/lib/IRGen/GenBuiltin.cpp index fa1c53f73a465..72abfd50e7a92 100644 --- a/lib/IRGen/GenBuiltin.cpp +++ b/lib/IRGen/GenBuiltin.cpp @@ -408,10 +408,10 @@ if (Builtin.ID == BuiltinValueKind::id) { \ auto *fn = cast(IGF.IGM.getWillThrowFn()); auto error = args.claimNext(); - auto errorBuffer = IGF.getErrorResultSlot( - SILType::getPrimitiveObjectType(IGF.IGM.Context.getErrorDecl() - ->getDeclaredInterfaceType() - ->getCanonicalType())); + auto errorBuffer = IGF.getCalleeErrorResultSlot( + SILType::getPrimitiveObjectType(IGF.IGM.Context.getErrorDecl() + ->getDeclaredInterfaceType() + ->getCanonicalType())); IGF.Builder.CreateStore(error, errorBuffer); auto context = llvm::UndefValue::get(IGF.IGM.Int8PtrTy); diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index bd530aac23ac9..d4db4e9e228d7 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -16,23 +16,25 @@ // //===----------------------------------------------------------------------===// -#include "GenCall.h" -#include "Signature.h" - +#include "swift/ABI/MetadataValues.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/GenericEnvironment.h" +#include "swift/Runtime/Config.h" +#include "swift/SIL/SILType.h" #include "clang/AST/ASTContext.h" #include "clang/AST/RecordLayout.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CodeGenABITypes.h" #include "clang/CodeGen/ModuleBuilder.h" -#include "swift/AST/GenericEnvironment.h" -#include "swift/SIL/SILType.h" -#include "swift/ABI/MetadataValues.h" -#include "swift/Runtime/Config.h" #include "llvm/IR/GlobalPtrAuthInfo.h" #include "llvm/Support/Compiler.h" #include "CallEmission.h" +#include "EntryPointArgumentEmission.h" #include "Explosion.h" +#include "GenCall.h" +#include "GenFunc.h" +#include "GenHeap.h" #include "GenObjC.h" #include "GenPointerAuth.h" #include "GenPoly.h" @@ -42,6 +44,8 @@ #include "IRGenModule.h" #include "LoadableTypeInfo.h" #include "NativeConventionSchema.h" +#include "Signature.h" +#include "StructLayout.h" using namespace swift; using namespace irgen; @@ -73,6 +77,174 @@ static Size getCoroutineContextSize(IRGenModule &IGM, llvm_unreachable("bad kind"); } +AsyncContextLayout irgen::getAsyncContextLayout(IRGenFunction &IGF, + SILFunction *function) { + SubstitutionMap forwardingSubstitutionMap = + function->getForwardingSubstitutionMap(); + CanSILFunctionType originalType = function->getLoweredFunctionType(); + CanSILFunctionType substitutedType = originalType->substGenericArgs( + IGF.IGM.getSILModule(), forwardingSubstitutionMap, + IGF.IGM.getMaximalTypeExpansionContext()); + auto layout = getAsyncContextLayout(IGF, originalType, substitutedType, + forwardingSubstitutionMap); + return layout; +} + +AsyncContextLayout irgen::getAsyncContextLayout( + IRGenFunction &IGF, CanSILFunctionType originalType, + CanSILFunctionType substitutedType, SubstitutionMap substitutionMap) { + SmallVector typeInfos; + SmallVector valTypes; + SmallVector paramInfos; + SmallVector indirectReturnInfos; + SmallVector directReturnInfos; + + auto parameters = substitutedType->getParameters(); + SILFunctionConventions fnConv(substitutedType, IGF.getSILModule()); + + // SwiftError *errorResult; + auto errorCanType = IGF.IGM.Context.getExceptionType(); + auto errorType = SILType::getPrimitiveObjectType(errorCanType); + auto &errorTypeInfo = IGF.getTypeInfoForLowered(errorCanType); + typeInfos.push_back(&errorTypeInfo); + valTypes.push_back(errorType); + + // IndirectResultTypes *indirectResults...; + auto indirectResults = fnConv.getIndirectSILResults(); + for (auto indirectResult : indirectResults) { + auto ty = fnConv.getSILType(indirectResult, + IGF.IGM.getMaximalTypeExpansionContext()); + auto retLoweringTy = CanInOutType::get(ty.getASTType()); + auto &ti = IGF.getTypeInfoForLowered(retLoweringTy); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + indirectReturnInfos.push_back(indirectResult); + } + + // SelfType self?; + bool hasLocalContextParameter = hasSelfContextParameter(substitutedType); + bool canHaveValidError = substitutedType->hasErrorResult(); + bool hasLocalContext = (hasLocalContextParameter || canHaveValidError || + substitutedType->getRepresentation() == + SILFunctionTypeRepresentation::Thick); + SILParameterInfo localContextParameter = + hasLocalContextParameter ? parameters.back() : SILParameterInfo(); + if (hasLocalContextParameter) { + parameters = parameters.drop_back(); + } + Optional localContextInfo = llvm::None; + if (hasLocalContext) { + if (hasLocalContextParameter) { + SILType ty = + IGF.IGM.silConv.getSILType(localContextParameter, substitutedType, + IGF.IGM.getMaximalTypeExpansionContext()); + auto &ti = IGF.getTypeInfoForLowered(ty.getASTType()); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + localContextInfo = {ty, localContextParameter.getConvention()}; + } else { + // TODO: DETERMINE: Is there a field in this case to match the sync ABI? + auto &ti = IGF.IGM.getNativeObjectTypeInfo(); + SILType ty = SILType::getNativeObjectType(IGF.IGM.Context); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + localContextInfo = {ty, substitutedType->getCalleeConvention()}; + } + } + + // ArgTypes formalArguments...; + auto bindings = NecessaryBindings::forAsyncFunctionInvocations( + IGF.IGM, originalType, substitutionMap); + if (!bindings.empty()) { + auto bindingsSize = bindings.getBufferSize(IGF.IGM); + auto &bindingsTI = IGF.IGM.getOpaqueStorageTypeInfo( + bindingsSize, IGF.IGM.getPointerAlignment()); + valTypes.push_back(SILType()); + typeInfos.push_back(&bindingsTI); + } + for (auto parameter : parameters) { + SILType ty = IGF.IGM.silConv.getSILType( + parameter, substitutedType, IGF.IGM.getMaximalTypeExpansionContext()); + + auto argumentLoweringType = + getArgumentLoweringType(ty.getASTType(), parameter, + /*isNoEscape*/ true); + + auto &ti = IGF.getTypeInfoForLowered(argumentLoweringType); + + valTypes.push_back(ty); + typeInfos.push_back(&ti); + paramInfos.push_back({ty, parameter.getConvention()}); + } + + // ResultTypes directResults...; + auto directResults = fnConv.getDirectSILResults(); + for (auto result : directResults) { + auto ty = + fnConv.getSILType(result, IGF.IGM.getMaximalTypeExpansionContext()); + auto &ti = IGF.getTypeInfoForLowered(ty.getASTType()); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + directReturnInfos.push_back(result); + } + + return AsyncContextLayout(IGF.IGM, LayoutStrategy::Optimal, valTypes, + typeInfos, IGF, originalType, substitutedType, + substitutionMap, std::move(bindings), errorType, + canHaveValidError, paramInfos, indirectReturnInfos, + directReturnInfos, localContextInfo); +} + +AsyncContextLayout::AsyncContextLayout( + IRGenModule &IGM, LayoutStrategy strategy, ArrayRef fieldTypes, + ArrayRef fieldTypeInfos, IRGenFunction &IGF, + CanSILFunctionType originalType, CanSILFunctionType substitutedType, + SubstitutionMap substitutionMap, NecessaryBindings &&bindings, + SILType errorType, bool canHaveValidError, + ArrayRef argumentInfos, + ArrayRef indirectReturnInfos, + ArrayRef directReturnInfos, + Optional localContextInfo) + : StructLayout(IGM, /*decl=*/nullptr, LayoutKind::NonHeapObject, strategy, + fieldTypeInfos, /*typeToFill*/ nullptr), + IGF(IGF), originalType(originalType), substitutedType(substitutedType), + substitutionMap(substitutionMap), errorType(errorType), + canHaveValidError(canHaveValidError), + directReturnInfos(directReturnInfos.begin(), directReturnInfos.end()), + indirectReturnInfos(indirectReturnInfos.begin(), + indirectReturnInfos.end()), + localContextInfo(localContextInfo), bindings(std::move(bindings)), + argumentInfos(argumentInfos.begin(), argumentInfos.end()) { +#ifndef NDEBUG + assert(fieldTypeInfos.size() == fieldTypes.size() && + "type infos don't match types"); + if (!bindings.empty()) { + assert(fieldTypeInfos.size() >= 2 && "no field for bindings"); + auto fixedBindingsField = + dyn_cast(fieldTypeInfos[getBindingsIndex()]); + assert(fixedBindingsField && "bindings field is not fixed size"); + assert(fixedBindingsField->getFixedSize() == bindings.getBufferSize(IGM) && + fixedBindingsField->getFixedAlignment() == + IGM.getPointerAlignment() && + "bindings field doesn't fit bindings"); + } + assert(this->isFixedLayout()); +#endif +} + +static Size getAsyncContextSize(AsyncContextLayout layout) { + return layout.getSize(); +} + +static Alignment getAsyncContextAlignment(IRGenModule &IGM) { + return IGM.getPointerAlignment(); +} + +static llvm::Value *getAsyncTask(IRGenFunction &IGF) { + // TODO: Return the appropriate task. + return llvm::Constant::getNullValue(IGF.IGM.SwiftTaskPtrTy); +} + llvm::Type *ExplosionSchema::getScalarResultType(IRGenModule &IGM) const { if (size() == 0) { return IGM.VoidTy; @@ -288,6 +460,7 @@ namespace { } void addCoroutineContextParameter(); + void addAsyncParameters(); void expandResult(); llvm::Type *expandDirectResult(); @@ -312,6 +485,12 @@ llvm::Type *SignatureExpansion::addIndirectResult() { /// Expand all of the direct and indirect result types. void SignatureExpansion::expandResult() { + if (FnType->isAsync()) { + // The result will be stored within the SwiftContext that is passed to async + // functions. + ResultIRType = IGM.VoidTy; + return; + } if (FnType->isCoroutine()) { // This should be easy enough to support if we need to: use the // same algorithm but add the direct results to the results as if @@ -482,6 +661,12 @@ void SignatureExpansion::expandCoroutineContinuationParameters() { ParamIRTypes.push_back(IGM.Int1Ty); } +void SignatureExpansion::addAsyncParameters() { + ParamIRTypes.push_back(IGM.SwiftContextPtrTy); + // TODO: Add actor. + // TODO: Add task. +} + void SignatureExpansion::addCoroutineContextParameter() { // Flag that the context is dereferenceable and unaliased. auto contextSize = getCoroutineContextSize(IGM, FnType); @@ -1343,6 +1528,13 @@ void SignatureExpansion::expandParameters() { assert(FnType->getRepresentation() != SILFunctionTypeRepresentation::Block && "block with non-C calling conv?!"); + if (FnType->isAsync()) { + addAsyncParameters(); + // All other parameters will be passed inside the context added by the + // addAsyncParameters call. + return; + } + // First, if this is a coroutine, add the coroutine-context parameter. switch (FnType->getCoroutineKind()) { case SILCoroutineKind::None: @@ -1515,69 +1707,373 @@ void irgen::extractScalarResults(IRGenFunction &IGF, llvm::Type *bodyType, out.add(returned); } -/// Emit the unsubstituted result of this call into the given explosion. -/// The unsubstituted result must be naturally returned directly. -void CallEmission::emitToUnmappedExplosion(Explosion &out) { - assert(LastArgWritten == 0 && "emitting unnaturally to explosion"); +static void externalizeArguments(IRGenFunction &IGF, const Callee &callee, + Explosion &in, Explosion &out, + TemporarySet &temporaries, bool isOutlined); - auto call = emitCallSite(); +namespace { - // Bail out immediately on a void result. - llvm::Value *result = call; - if (result->getType()->isVoidTy()) - return; +class SyncCallEmission final : public CallEmission { + using super = CallEmission; - SILFunctionConventions fnConv(getCallee().getOrigFunctionType(), - IGF.getSILModule()); +public: + SyncCallEmission(IRGenFunction &IGF, llvm::Value *selfValue, Callee &&callee) + : CallEmission(IGF, selfValue, std::move(callee)) { + setFromCallee(); + } - // If the result was returned autoreleased, implicitly insert the reclaim. - // This is only allowed on a single direct result. - if (fnConv.getNumDirectSILResults() == 1 - && (fnConv.getDirectSILResults().begin()->getConvention() - == ResultConvention::Autoreleased)) { - result = emitObjCRetainAutoreleasedReturnValue(IGF, result); + SILType getParameterType(unsigned index) override { + SILFunctionConventions origConv(getCallee().getOrigFunctionType(), + IGF.getSILModule()); + return origConv.getSILArgumentType( + index, IGF.IGM.getMaximalTypeExpansionContext()); } + void begin() override { super::begin(); } + void end() override { super::end(); } + void setFromCallee() override { + super::setFromCallee(); - auto origFnType = getCallee().getOrigFunctionType(); + auto fnType = CurCallee.getOrigFunctionType(); - // Specially handle noreturn c function which would return a 'Never' SIL result - // type. - if (origFnType->getLanguage() == SILFunctionLanguage::C && - origFnType->isNoReturnFunction( - IGF.getSILModule(), IGF.IGM.getMaximalTypeExpansionContext())) { - auto clangResultTy = result->getType(); - extractScalarResults(IGF, clangResultTy, result, out); - return; + if (fnType->getRepresentation() == + SILFunctionTypeRepresentation::WitnessMethod) { + unsigned n = getTrailingWitnessSignatureLength(IGF.IGM, fnType); + while (n--) { + Args[--LastArgWritten] = nullptr; + } + } + + llvm::Value *contextPtr = CurCallee.getSwiftContext(); + + // Add the error result if we have one. + if (fnType->hasErrorResult()) { + // The invariant is that this is always zero-initialized, so we + // don't need to do anything extra here. + SILFunctionConventions fnConv(fnType, IGF.getSILModule()); + Address errorResultSlot = IGF.getCalleeErrorResultSlot( + fnConv.getSILErrorType(IGF.IGM.getMaximalTypeExpansionContext())); + + assert(LastArgWritten > 0); + Args[--LastArgWritten] = errorResultSlot.getAddress(); + addAttribute(LastArgWritten + llvm::AttributeList::FirstArgIndex, + llvm::Attribute::NoCapture); + IGF.IGM.addSwiftErrorAttributes(CurCallee.getMutableAttributes(), + LastArgWritten); + + // Fill in the context pointer if necessary. + if (!contextPtr) { + assert(!CurCallee.getOrigFunctionType()->getExtInfo().hasContext() && + "Missing context?"); + contextPtr = llvm::UndefValue::get(IGF.IGM.RefCountedPtrTy); + } + } + + // Add the data pointer if we have one. + // (Note that we're emitting backwards, so this correctly goes + // *before* the error pointer.) + if (contextPtr) { + assert(LastArgWritten > 0); + Args[--LastArgWritten] = contextPtr; + IGF.IGM.addSwiftSelfAttributes(CurCallee.getMutableAttributes(), + LastArgWritten); + } } + void setArgs(Explosion &original, bool isOutlined, + WitnessMetadata *witnessMetadata) override { + // Convert arguments to a representation appropriate to the calling + // convention. + Explosion adjusted; - // Get the natural IR type in the body of the function that makes - // the call. This may be different than the IR type returned by the - // call itself due to ABI type coercion. - auto resultType = - fnConv.getSILResultType(IGF.IGM.getMaximalTypeExpansionContext()); - auto &nativeSchema = IGF.IGM.getTypeInfo(resultType).nativeReturnValueSchema(IGF.IGM); + auto origCalleeType = CurCallee.getOrigFunctionType(); + SILFunctionConventions fnConv(origCalleeType, IGF.getSILModule()); - // For ABI reasons the result type of the call might not actually match the - // expected result type. - // - // This can happen when calling C functions, or class method dispatch thunks - // for methods that have covariant ABI-compatible overrides. - auto expectedNativeResultType = nativeSchema.getExpandedType(IGF.IGM); - if (result->getType() != expectedNativeResultType) { - result = - IGF.coerceValue(result, expectedNativeResultType, IGF.IGM.DataLayout); + // Pass along the indirect result pointers. + original.transferInto(adjusted, fnConv.getNumIndirectSILResults()); + + // Pass along the coroutine buffer. + switch (origCalleeType->getCoroutineKind()) { + case SILCoroutineKind::YieldMany: + case SILCoroutineKind::YieldOnce: + original.transferInto(adjusted, 1); + break; + + case SILCoroutineKind::None: + break; + } + + // Translate the formal arguments and handle any special arguments. + switch (getCallee().getRepresentation()) { + case SILFunctionTypeRepresentation::ObjCMethod: + adjusted.add(getCallee().getObjCMethodReceiver()); + adjusted.add(getCallee().getObjCMethodSelector()); + externalizeArguments(IGF, getCallee(), original, adjusted, Temporaries, + isOutlined); + break; + + case SILFunctionTypeRepresentation::Block: + adjusted.add(getCallee().getBlockObject()); + LLVM_FALLTHROUGH; + + case SILFunctionTypeRepresentation::CFunctionPointer: + externalizeArguments(IGF, getCallee(), original, adjusted, Temporaries, + isOutlined); + break; + + case SILFunctionTypeRepresentation::WitnessMethod: + assert(witnessMetadata); + assert(witnessMetadata->SelfMetadata->getType() == + IGF.IGM.TypeMetadataPtrTy); + assert(witnessMetadata->SelfWitnessTable->getType() == + IGF.IGM.WitnessTablePtrTy); + Args.rbegin()[1] = witnessMetadata->SelfMetadata; + Args.rbegin()[0] = witnessMetadata->SelfWitnessTable; + LLVM_FALLTHROUGH; + + case SILFunctionTypeRepresentation::Closure: + case SILFunctionTypeRepresentation::Method: + case SILFunctionTypeRepresentation::Thin: + case SILFunctionTypeRepresentation::Thick: { + // Check for value arguments that need to be passed indirectly. + // But don't expect to see 'self' if it's been moved to the context + // position. + auto params = origCalleeType->getParameters(); + if (hasSelfContextParameter(origCalleeType)) { + params = params.drop_back(); + } + for (auto param : params) { + addNativeArgument(IGF, original, origCalleeType, param, adjusted, + isOutlined); + } + + // Anything else, just pass along. This will include things like + // generic arguments. + adjusted.add(original.claimAll()); + + break; + } + } + super::setArgs(adjusted, isOutlined, witnessMetadata); } + void emitCallToUnmappedExplosion(llvm::CallInst *call, Explosion &out) override { + // Bail out immediately on a void result. + llvm::Value *result = call; + if (result->getType()->isVoidTy()) + return; - // Gather the values. - Explosion nativeExplosion; - extractScalarResults(IGF, result->getType(), result, nativeExplosion); + SILFunctionConventions fnConv(getCallee().getOrigFunctionType(), + IGF.getSILModule()); + + // If the result was returned autoreleased, implicitly insert the reclaim. + // This is only allowed on a single direct result. + if (fnConv.getNumDirectSILResults() == 1 + && (fnConv.getDirectSILResults().begin()->getConvention() + == ResultConvention::Autoreleased)) { + result = emitObjCRetainAutoreleasedReturnValue(IGF, result); + } + + auto origFnType = getCallee().getOrigFunctionType(); + + // Specially handle noreturn c function which would return a 'Never' SIL result + // type. + if (origFnType->getLanguage() == SILFunctionLanguage::C && + origFnType->isNoReturnFunction( + IGF.getSILModule(), IGF.IGM.getMaximalTypeExpansionContext())) { + auto clangResultTy = result->getType(); + extractScalarResults(IGF, clangResultTy, result, out); + return; + } + + // Get the natural IR type in the body of the function that makes + // the call. This may be different than the IR type returned by the + // call itself due to ABI type coercion. + auto resultType = + fnConv.getSILResultType(IGF.IGM.getMaximalTypeExpansionContext()); + auto &nativeSchema = IGF.IGM.getTypeInfo(resultType).nativeReturnValueSchema(IGF.IGM); + + // For ABI reasons the result type of the call might not actually match the + // expected result type. + // + // This can happen when calling C functions, or class method dispatch thunks + // for methods that have covariant ABI-compatible overrides. + auto expectedNativeResultType = nativeSchema.getExpandedType(IGF.IGM); + if (result->getType() != expectedNativeResultType) { + result = + IGF.coerceValue(result, expectedNativeResultType, IGF.IGM.DataLayout); + } + + // Gather the values. + Explosion nativeExplosion; + extractScalarResults(IGF, result->getType(), result, nativeExplosion); + + out = nativeSchema.mapFromNative(IGF.IGM, IGF, nativeExplosion, resultType); + } + Address getCalleeErrorSlot(SILType errorType) override { + return IGF.getCalleeErrorResultSlot(errorType); + }; +}; + +class AsyncCallEmission final : public CallEmission { + using super = CallEmission; + + Address contextBuffer; + Size contextSize; + Address context; + + AsyncContextLayout getAsyncContextLayout() { + return ::getAsyncContextLayout(IGF, getCallee().getOrigFunctionType(), + getCallee().getSubstFunctionType(), + getCallee().getSubstitutions()); + } + +public: + AsyncCallEmission(IRGenFunction &IGF, llvm::Value *selfValue, Callee &&callee) + : CallEmission(IGF, selfValue, std::move(callee)) { + setFromCallee(); + } + + void begin() override { + super::begin(); + assert(!contextBuffer.isValid()); + assert(!context.isValid()); + // Allocate space for the async arguments. + auto layout = getAsyncContextLayout(); + std::tie(contextBuffer, contextSize) = emitAllocAsyncContext(IGF, layout); + context = layout.emitCastTo(IGF, contextBuffer.getAddress()); + if (layout.canHaveError()) { + auto fieldLayout = layout.getErrorLayout(); + auto addr = fieldLayout.project(IGF, context, /*offsets*/ llvm::None); + auto &ti = fieldLayout.getType(); + auto nullError = llvm::Constant::getNullValue(ti.getStorageType()); + IGF.Builder.CreateStore(nullError, addr); + } + } + void end() override { + assert(contextBuffer.isValid()); + assert(context.isValid()); + emitDeallocAsyncContext(IGF, contextBuffer, contextSize); + super::end(); + } + void setFromCallee() override { super::setFromCallee(); } + SILType getParameterType(unsigned index) override { + return getAsyncContextLayout().getParameterType(index); + } + void setArgs(Explosion &llArgs, bool isOutlined, + WitnessMetadata *witnessMetadata) override { + Explosion asyncExplosion; + asyncExplosion.add(contextBuffer.getAddress()); + super::setArgs(asyncExplosion, false, witnessMetadata); + SILFunctionConventions fnConv(getCallee().getSubstFunctionType(), + IGF.getSILModule()); + + // Move all the arguments into the context. + if (selfValue) { + llArgs.add(selfValue); + } + auto layout = getAsyncContextLayout(); + auto params = fnConv.getParameters(); + for (auto index : indices(params)) { + Optional fieldLayout; + if (selfValue && index == params.size() - 1) { + fieldLayout = layout.getLocalContextLayout(); + } else { + fieldLayout = layout.getArgumentLayout(index); + } + Address fieldAddr = + fieldLayout->project(IGF, context, /*offsets*/ llvm::None); + auto &ti = cast(fieldLayout->getType()); + ti.initialize(IGF, llArgs, fieldAddr, isOutlined); + } + unsigned index = 0; + for (auto indirectResult : fnConv.getIndirectSILResultTypes( + IGF.IGM.getMaximalTypeExpansionContext())) { + (void)indirectResult; + auto fieldLayout = layout.getIndirectReturnLayout(index); + Address fieldAddr = + fieldLayout.project(IGF, context, /*offsets*/ llvm::None); + cast(fieldLayout.getType()) + .initialize(IGF, llArgs, fieldAddr, isOutlined); + ++index; + } + if (layout.hasBindings()) { + auto bindingLayout = layout.getBindingsLayout(); + auto bindingsAddr = bindingLayout.project(IGF, context, /*offsets*/ None); + layout.getBindings().save(IGF, bindingsAddr); + } + // At this point, llArgs contains the arguments that are being passed along + // via the async context. We can safely drop them on the floor. + (void)llArgs.claimAll(); + // TODO: Validation: we should be able to check that the contents of llArgs + // matches what is expected by the layout. + } + void emitCallToUnmappedExplosion(llvm::CallInst *call, Explosion &out) override { + SILFunctionConventions fnConv(getCallee().getSubstFunctionType(), + IGF.getSILModule()); + auto resultType = + fnConv.getSILResultType(IGF.IGM.getMaximalTypeExpansionContext()); + auto &nativeSchema = + IGF.IGM.getTypeInfo(resultType).nativeReturnValueSchema(IGF.IGM); + auto expectedNativeResultType = nativeSchema.getExpandedType(IGF.IGM); + if (expectedNativeResultType->isVoidTy()) { + // If the async return is void, there is no return to move out of the + // argument buffer. + return; + } + assert(call->arg_size() == 1); + auto context = call->arg_begin()->get(); + // Gather the values. + Explosion nativeExplosion; + auto layout = getAsyncContextLayout(); + auto dataAddr = layout.emitCastTo(IGF, context); + int index = layout.getFirstDirectReturnIndex(); + for (auto result : fnConv.getDirectSILResults()) { + auto &fieldLayout = layout.getElement(index); + Address fieldAddr = + fieldLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); + auto &fieldTI = fieldLayout.getType(); + cast(fieldTI).loadAsTake(IGF, fieldAddr, + nativeExplosion); + ++index; + } + + out = nativeSchema.mapFromNative(IGF.IGM, IGF, nativeExplosion, resultType); + } + Address getCalleeErrorSlot(SILType errorType) override { + auto layout = getAsyncContextLayout(); + auto errorLayout = layout.getErrorLayout(); + auto address = errorLayout.project(IGF, context, /*offsets*/ llvm::None); + return address; + }; +}; + +} // end anonymous namespace + +std::unique_ptr irgen::getCallEmission(IRGenFunction &IGF, + llvm::Value *selfValue, + Callee &&callee) { + if (callee.getOrigFunctionType()->isAsync()) { + return std::make_unique(IGF, selfValue, + std::move(callee)); + } else { + return std::make_unique(IGF, selfValue, + std::move(callee)); + } +} + +/// Emit the unsubstituted result of this call into the given explosion. +/// The unsubstituted result must be naturally returned directly. +void CallEmission::emitToUnmappedExplosion(Explosion &out) { + assert(state == State::Emitting); + assert(LastArgWritten == 0 && "emitting unnaturally to explosion"); + + auto call = emitCallSite(); - out = nativeSchema.mapFromNative(IGF.IGM, IGF, nativeExplosion, resultType); + emitCallToUnmappedExplosion(call, out); } /// Emit the unsubstituted result of this call to the given address. /// The unsubstituted result must be naturally returned indirectly. void CallEmission::emitToUnmappedMemory(Address result) { + assert(state == State::Emitting); assert(LastArgWritten == 1 && "emitting unnaturally to indirect result"); Args[0] = result.getAddress(); @@ -1594,6 +2090,7 @@ void CallEmission::emitToUnmappedMemory(Address result) { /// The private routine to ultimately emit a call or invoke instruction. llvm::CallInst *CallEmission::emitCallSite() { + assert(state == State::Emitting); assert(LastArgWritten == 0); assert(!EmittedCall); EmittedCall = true; @@ -1681,6 +2178,7 @@ llvm::CallInst *IRBuilder::CreateCall(const FunctionPointer &fn, void CallEmission::emitToMemory(Address addr, const LoadableTypeInfo &indirectedResultTI, bool isOutlined) { + assert(state == State::Emitting); assert(LastArgWritten <= 1); // If the call is naturally to an explosion, emit it that way and @@ -1742,6 +2240,7 @@ static void emitCastToSubstSchema(IRGenFunction &IGF, Explosion &in, } void CallEmission::emitYieldsToExplosion(Explosion &out) { + assert(state == State::Emitting); // Emit the call site. auto call = emitCallSite(); @@ -1820,6 +2319,7 @@ void CallEmission::emitYieldsToExplosion(Explosion &out) { /// Emit the result of this call to an explosion. void CallEmission::emitToExplosion(Explosion &out, bool isOutlined) { + assert(state == State::Emitting); assert(LastArgWritten <= 1); // For coroutine calls, we need to collect the yields, not the results; @@ -1892,12 +2392,21 @@ CallEmission::CallEmission(CallEmission &&other) // Prevent other's destructor from asserting. LastArgWritten = 0; EmittedCall = true; + state = State::Finished; } CallEmission::~CallEmission() { assert(LastArgWritten == 0); assert(EmittedCall); assert(Temporaries.hasBeenCleared()); + assert(state == State::Finished); +} + +void CallEmission::begin() {} + +void CallEmission::end() { + assert(state == State::Emitting); + state = State::Finished; } Callee::Callee(CalleeInfo &&info, const FunctionPointer &fn, @@ -1982,6 +2491,7 @@ llvm::Value *Callee::getObjCMethodSelector() const { /// Set up this emitter afresh from the current callee specs. void CallEmission::setFromCallee() { + assert(state == State::Emitting); IsCoroutine = CurCallee.getSubstFunctionType()->isCoroutine(); EmittedCall = false; @@ -1992,51 +2502,6 @@ void CallEmission::setFromCallee() { Args.reserve(numArgs); Args.set_size(numArgs); LastArgWritten = numArgs; - - auto fnType = CurCallee.getOrigFunctionType(); - - if (fnType->getRepresentation() - == SILFunctionTypeRepresentation::WitnessMethod) { - unsigned n = getTrailingWitnessSignatureLength(IGF.IGM, fnType); - while (n--) { - Args[--LastArgWritten] = nullptr; - } - } - - llvm::Value *contextPtr = CurCallee.getSwiftContext(); - - // Add the error result if we have one. - if (fnType->hasErrorResult()) { - // The invariant is that this is always zero-initialized, so we - // don't need to do anything extra here. - SILFunctionConventions fnConv(fnType, IGF.getSILModule()); - Address errorResultSlot = IGF.getErrorResultSlot( - fnConv.getSILErrorType(IGF.IGM.getMaximalTypeExpansionContext())); - - assert(LastArgWritten > 0); - Args[--LastArgWritten] = errorResultSlot.getAddress(); - addAttribute(LastArgWritten + llvm::AttributeList::FirstArgIndex, - llvm::Attribute::NoCapture); - IGF.IGM.addSwiftErrorAttributes(CurCallee.getMutableAttributes(), - LastArgWritten); - - // Fill in the context pointer if necessary. - if (!contextPtr) { - assert(!CurCallee.getOrigFunctionType()->getExtInfo().hasContext() && - "Missing context?"); - contextPtr = llvm::UndefValue::get(IGF.IGM.RefCountedPtrTy); - } - } - - // Add the data pointer if we have one. - // (Note that we're emitting backwards, so this correctly goes - // *before* the error pointer.) - if (contextPtr) { - assert(LastArgWritten > 0); - Args[--LastArgWritten] = contextPtr; - IGF.IGM.addSwiftSelfAttributes(CurCallee.getMutableAttributes(), - LastArgWritten); - } } bool irgen::canCoerceToSchema(IRGenModule &IGM, @@ -2610,12 +3075,11 @@ irgen::getCoroutineResumeFunctionPointerAuth(IRGenModule &IGM, llvm_unreachable("bad coroutine kind"); } -static void emitRetconCoroutineEntry(IRGenFunction &IGF, - CanSILFunctionType fnType, - Explosion &allParamValues, - llvm::Intrinsic::ID idIntrinsic, - Size bufferSize, - Alignment bufferAlignment) { +static void +emitRetconCoroutineEntry(IRGenFunction &IGF, CanSILFunctionType fnType, + NativeCCEntryPointArgumentEmission &emission, + llvm::Intrinsic::ID idIntrinsic, Size bufferSize, + Alignment bufferAlignment) { auto prototype = IGF.IGM.getOpaquePtr(IGF.IGM.getAddrOfContinuationPrototype(fnType)); @@ -2624,7 +3088,7 @@ static void emitRetconCoroutineEntry(IRGenFunction &IGF, auto deallocFn = IGF.IGM.getOpaquePtr(IGF.IGM.getFreeFn()); // Call the right 'llvm.coro.id.retcon' variant. - llvm::Value *buffer = allParamValues.claimNext(); + llvm::Value *buffer = emission.getCoroutineBuffer(); llvm::Value *id = IGF.Builder.CreateIntrinsicCall(idIntrinsic, { llvm::ConstantInt::get(IGF.IGM.Int32Ty, bufferSize.getValue()), llvm::ConstantInt::get(IGF.IGM.Int32Ty, bufferAlignment.getValue()), @@ -2645,19 +3109,19 @@ static void emitRetconCoroutineEntry(IRGenFunction &IGF, IGF.setCoroutineHandle(hdl); } -void irgen::emitYieldOnceCoroutineEntry(IRGenFunction &IGF, - CanSILFunctionType fnType, - Explosion &allParamValues) { - emitRetconCoroutineEntry(IGF, fnType, allParamValues, +void irgen::emitYieldOnceCoroutineEntry( + IRGenFunction &IGF, CanSILFunctionType fnType, + NativeCCEntryPointArgumentEmission &emission) { + emitRetconCoroutineEntry(IGF, fnType, emission, llvm::Intrinsic::coro_id_retcon_once, getYieldOnceCoroutineBufferSize(IGF.IGM), getYieldOnceCoroutineBufferAlignment(IGF.IGM)); } -void irgen::emitYieldManyCoroutineEntry(IRGenFunction &IGF, - CanSILFunctionType fnType, - Explosion &allParamValues) { - emitRetconCoroutineEntry(IGF, fnType, allParamValues, +void irgen::emitYieldManyCoroutineEntry( + IRGenFunction &IGF, CanSILFunctionType fnType, + NativeCCEntryPointArgumentEmission &emission) { + emitRetconCoroutineEntry(IGF, fnType, emission, llvm::Intrinsic::coro_id_retcon, getYieldManyCoroutineBufferSize(IGF.IGM), getYieldManyCoroutineBufferAlignment(IGF.IGM)); @@ -2694,9 +3158,49 @@ void irgen::emitDeallocYieldManyCoroutineBuffer(IRGenFunction &IGF, IGF.Builder.CreateLifetimeEnd(buffer, bufferSize); } +Address irgen::emitTaskAlloc(IRGenFunction &IGF, llvm::Value *size, + Alignment alignment) { + auto *call = IGF.Builder.CreateCall(IGF.IGM.getTaskAllocFn(), + {getAsyncTask(IGF), size}); + call->setDoesNotThrow(); + call->setCallingConv(IGF.IGM.SwiftCC); + call->addAttribute(llvm::AttributeList::FunctionIndex, + llvm::Attribute::ReadNone); + auto address = Address(call, alignment); + return address; +} + +void irgen::emitTaskDealloc(IRGenFunction &IGF, Address address, + llvm::Value *size) { + auto *call = IGF.Builder.CreateCall( + IGF.IGM.getTaskDeallocFn(), {getAsyncTask(IGF), address.getAddress()}); + call->setDoesNotThrow(); + call->setCallingConv(IGF.IGM.SwiftCC); + call->addAttribute(llvm::AttributeList::FunctionIndex, + llvm::Attribute::ReadNone); +} + +std::pair +irgen::emitAllocAsyncContext(IRGenFunction &IGF, AsyncContextLayout layout) { + auto size = getAsyncContextSize(layout); + auto *sizeValue = llvm::ConstantInt::get(IGF.IGM.SizeTy, size.getValue()); + auto alignment = getAsyncContextAlignment(IGF.IGM); + auto address = emitTaskAlloc(IGF, sizeValue, alignment); + IGF.Builder.CreateLifetimeStart(address, size); + return {address, size}; +} + +void irgen::emitDeallocAsyncContext(IRGenFunction &IGF, Address context, + Size size) { + auto *sizeValue = llvm::ConstantInt::get(IGF.IGM.SizeTy, size.getValue()); + emitTaskDealloc(IGF, context, sizeValue); + IGF.Builder.CreateLifetimeEnd(context, size); +} + llvm::Value *irgen::emitYield(IRGenFunction &IGF, CanSILFunctionType coroutineType, Explosion &substValues) { + // TODO: Handle async! auto coroSignature = IGF.IGM.getSignature(coroutineType); auto coroInfo = coroSignature.getCoroutineInfo(); @@ -2772,88 +3276,16 @@ llvm::Value *irgen::emitYield(IRGenFunction &IGF, } /// Add a new set of arguments to the function. -void CallEmission::setArgs(Explosion &original, bool isOutlined, +void CallEmission::setArgs(Explosion &adjusted, bool isOutlined, WitnessMetadata *witnessMetadata) { - // Convert arguments to a representation appropriate to the calling - // convention. - Explosion adjusted; - - auto origCalleeType = CurCallee.getOrigFunctionType(); - SILFunctionConventions fnConv(origCalleeType, IGF.getSILModule()); - - // Pass along the indirect result pointers. - original.transferInto(adjusted, fnConv.getNumIndirectSILResults()); - - // Pass along the coroutine buffer. - switch (origCalleeType->getCoroutineKind()) { - case SILCoroutineKind::YieldMany: - case SILCoroutineKind::YieldOnce: - original.transferInto(adjusted, 1); - break; - - case SILCoroutineKind::None: - break; - } - - // Translate the formal arguments and handle any special arguments. - switch (getCallee().getRepresentation()) { - case SILFunctionTypeRepresentation::ObjCMethod: - adjusted.add(getCallee().getObjCMethodReceiver()); - adjusted.add(getCallee().getObjCMethodSelector()); - externalizeArguments(IGF, getCallee(), original, adjusted, - Temporaries, isOutlined); - break; - - case SILFunctionTypeRepresentation::Block: - adjusted.add(getCallee().getBlockObject()); - LLVM_FALLTHROUGH; - - case SILFunctionTypeRepresentation::CFunctionPointer: - externalizeArguments(IGF, getCallee(), original, adjusted, - Temporaries, isOutlined); - break; - - case SILFunctionTypeRepresentation::WitnessMethod: - assert(witnessMetadata); - assert(witnessMetadata->SelfMetadata->getType() == - IGF.IGM.TypeMetadataPtrTy); - assert(witnessMetadata->SelfWitnessTable->getType() == - IGF.IGM.WitnessTablePtrTy); - Args.rbegin()[1] = witnessMetadata->SelfMetadata; - Args.rbegin()[0] = witnessMetadata->SelfWitnessTable; - LLVM_FALLTHROUGH; - - case SILFunctionTypeRepresentation::Closure: - case SILFunctionTypeRepresentation::Method: - case SILFunctionTypeRepresentation::Thin: - case SILFunctionTypeRepresentation::Thick: { - // Check for value arguments that need to be passed indirectly. - // But don't expect to see 'self' if it's been moved to the context - // position. - auto params = origCalleeType->getParameters(); - if (hasSelfContextParameter(origCalleeType)) { - params = params.drop_back(); - } - for (auto param : params) { - addNativeArgument(IGF, original, - origCalleeType, param, adjusted, isOutlined); - } - - // Anything else, just pass along. This will include things like - // generic arguments. - adjusted.add(original.claimAll()); - - break; - } - } - + assert(state == State::Emitting); // Add the given number of arguments. assert(LastArgWritten >= adjusted.size()); size_t targetIndex = LastArgWritten - adjusted.size(); assert(targetIndex <= 1); LastArgWritten = targetIndex; - + auto argIterator = Args.begin() + targetIndex; for (auto value : adjusted.claimAll()) { *argIterator++ = value; @@ -2862,6 +3294,7 @@ void CallEmission::setArgs(Explosion &original, bool isOutlined, void CallEmission::addAttribute(unsigned index, llvm::Attribute::AttrKind attr) { + assert(state == State::Emitting); auto &attrs = CurCallee.getMutableAttributes(); attrs = attrs.addAttribute(IGF.IGM.getLLVMContext(), index, attr); } @@ -2877,8 +3310,8 @@ Explosion IRGenFunction::collectParameters() { } /// Fetch the error result slot. -Address IRGenFunction::getErrorResultSlot(SILType errorType) { - if (!ErrorResultSlot) { +Address IRGenFunction::getCalleeErrorResultSlot(SILType errorType) { + if (!CalleeErrorResultSlot) { auto &errorTI = cast(getTypeInfo(errorType)); IRBuilder builder(IGM.getLLVMContext(), IGM.DebugInfo != nullptr); @@ -2902,23 +3335,28 @@ Address IRGenFunction::getErrorResultSlot(SILType errorType) { cast(errorTI.getStorageType())); builder.CreateStore(nullError, addr); - ErrorResultSlot = addr.getAddress(); + CalleeErrorResultSlot = addr.getAddress(); } - return Address(ErrorResultSlot, IGM.getPointerAlignment()); + return Address(CalleeErrorResultSlot, IGM.getPointerAlignment()); } /// Fetch the error result slot received from the caller. Address IRGenFunction::getCallerErrorResultSlot() { - assert(ErrorResultSlot && "no error result slot!"); - assert(isa(ErrorResultSlot) && "error result slot is local!"); - return Address(ErrorResultSlot, IGM.getPointerAlignment()); + assert(CallerErrorResultSlot && "no error result slot!"); + assert(isa(CallerErrorResultSlot) && !isAsync() || + isa(CallerErrorResultSlot) && isAsync() && + "error result slot is local!"); + return Address(CallerErrorResultSlot, IGM.getPointerAlignment()); } // Set the error result slot. This should only be done in the prologue. -void IRGenFunction::setErrorResultSlot(llvm::Value *address) { - assert(!ErrorResultSlot && "already have error result slot!"); +void IRGenFunction::setCallerErrorResultSlot(llvm::Value *address) { + assert(!CallerErrorResultSlot && "already have a caller error result slot!"); assert(isa(address->getType())); - ErrorResultSlot = address; + CallerErrorResultSlot = address; + if (!isAsync()) { + CalleeErrorResultSlot = address; + } } /// Emit the basic block that 'return' should branch to and insert it into diff --git a/lib/IRGen/GenCall.h b/lib/IRGen/GenCall.h index c8e3a2c54dd19..86dd7a1afcee4 100644 --- a/lib/IRGen/GenCall.h +++ b/lib/IRGen/GenCall.h @@ -20,10 +20,14 @@ #include -#include "swift/Basic/LLVM.h" #include "swift/AST/Types.h" +#include "swift/Basic/LLVM.h" +#include "swift/SIL/ApplySite.h" #include "llvm/IR/CallingConv.h" +#include "GenHeap.h" +#include "IRGenModule.h" + namespace llvm { class AttributeList; class Constant; @@ -49,6 +53,7 @@ namespace irgen { class IRGenFunction; class IRGenModule; class LoadableTypeInfo; + class NativeCCEntryPointArgumentEmission; class Size; class TypeInfo; @@ -60,6 +65,147 @@ namespace irgen { return TranslationDirection(!bool(direction)); } + // struct SwiftContext { + // SwiftContext * __ptrauth(...) callerContext; + // SwiftPartialFunction * __ptrauth(...) returnToCaller; + // SwiftActor * __ptrauth(...) callerActor; + // SwiftPartialFunction * __ptrauth(...) yieldToCaller?; + // SwiftError *errorResult; + // IndirectResultTypes *indirectResults...; + // SelfType self?; + // ArgTypes formalArguments...; + // union { + // struct { + // SwiftPartialFunction * __ptrauth(...) resumeFromYield?; + // SwiftPartialFunction * __ptrauth(...) abortFromYield?; + // SwiftActor * __ptrauth(...) calleeActorDuringYield?; + // YieldTypes yieldValues...; + // }; + // ResultTypes directResults...; + // }; + // }; + struct AsyncContextLayout : StructLayout { + struct ArgumentInfo { + SILType type; + ParameterConvention convention; + }; + + private: + enum class FixedIndex : unsigned { + Error = 0, + }; + enum class FixedCount : unsigned { + Error = 1, + }; + IRGenFunction &IGF; + CanSILFunctionType originalType; + CanSILFunctionType substitutedType; + SubstitutionMap substitutionMap; + SILType errorType; + bool canHaveValidError; + SmallVector directReturnInfos; + SmallVector indirectReturnInfos; + Optional localContextInfo; + NecessaryBindings bindings; + SmallVector argumentInfos; + + public: + bool canHaveError() { return canHaveValidError; } + unsigned getErrorIndex() { return (unsigned)FixedIndex::Error; } + ElementLayout getErrorLayout() { return getElement(getErrorIndex()); } + unsigned getErrorCount() { return (unsigned)FixedCount::Error; } + SILType getErrorType() { return errorType; } + + unsigned getFirstIndirectReturnIndex() { + return getErrorIndex() + getErrorCount(); + } + ElementLayout getIndirectReturnLayout(unsigned index) { + return getElement(getFirstIndirectReturnIndex() + index); + } + unsigned getIndirectReturnCount() { return indirectReturnInfos.size(); } + + bool hasLocalContext() { return (bool)localContextInfo; } + unsigned getLocalContextIndex() { + assert(hasLocalContext()); + return getFirstIndirectReturnIndex() + getIndirectReturnCount(); + } + ElementLayout getLocalContextLayout() { + assert(hasLocalContext()); + return getElement(getLocalContextIndex()); + } + ParameterConvention getLocalContextConvention() { + assert(hasLocalContext()); + return localContextInfo->convention; + } + SILType getLocalContextType() { + assert(hasLocalContext()); + return localContextInfo->type; + } + unsigned getIndexAfterLocalContext() { + return getFirstIndirectReturnIndex() + getIndirectReturnCount() + + (hasLocalContext() ? 1 : 0); + } + + bool hasBindings() const { return !bindings.empty(); } + unsigned getBindingsIndex() { + assert(hasBindings()); + return getIndexAfterLocalContext(); + } + ElementLayout getBindingsLayout() { + assert(hasBindings()); + return getElement(getBindingsIndex()); + } + ParameterConvention getBindingsConvention() { + return ParameterConvention::Direct_Unowned; + } + const NecessaryBindings &getBindings() const { return bindings; } + + unsigned getFirstArgumentIndex() { + return getIndexAfterLocalContext() + (hasBindings() ? 1 : 0); + } + ElementLayout getArgumentLayout(unsigned index) { + return getElement(getFirstArgumentIndex() + index); + } + ParameterConvention getArgumentConvention(unsigned index) { + return argumentInfos[index].convention; + } + SILType getArgumentType(unsigned index) { + return argumentInfos[index].type; + } + SILType getParameterType(unsigned index) { + SILFunctionConventions origConv(substitutedType, IGF.getSILModule()); + return origConv.getSILArgumentType( + index, IGF.IGM.getMaximalTypeExpansionContext()); + } + unsigned getArgumentCount() { return argumentInfos.size(); } + unsigned getIndexAfterArguments() { + return getFirstArgumentIndex() + getArgumentCount(); + } + + unsigned getFirstDirectReturnIndex() { return getIndexAfterArguments(); } + + AsyncContextLayout(IRGenModule &IGM, LayoutStrategy strategy, + ArrayRef fieldTypes, + ArrayRef fieldTypeInfos, + IRGenFunction &IGF, CanSILFunctionType originalType, + CanSILFunctionType substitutedType, + SubstitutionMap substitutionMap, + NecessaryBindings &&bindings, SILType errorType, + bool canHaveValidError, + ArrayRef argumentInfos, + ArrayRef directReturnInfos, + ArrayRef indirectReturnInfos, + Optional localContextInfo); + }; + + AsyncContextLayout getAsyncContextLayout(IRGenFunction &IGF, + SILFunction *function); + + AsyncContextLayout getAsyncContextLayout(IRGenFunction &IGF, + CanSILFunctionType originalType, + CanSILFunctionType substitutedType, + SubstitutionMap substitutionMap); + llvm::CallingConv::ID expandCallingConv(IRGenModule &IGM, SILFunctionTypeRepresentation convention); @@ -127,15 +273,24 @@ namespace irgen { Address emitAllocYieldOnceCoroutineBuffer(IRGenFunction &IGF); void emitDeallocYieldOnceCoroutineBuffer(IRGenFunction &IGF, Address buffer); - void emitYieldOnceCoroutineEntry(IRGenFunction &IGF, - CanSILFunctionType coroutineType, - Explosion &allParams); + void + emitYieldOnceCoroutineEntry(IRGenFunction &IGF, + CanSILFunctionType coroutineType, + NativeCCEntryPointArgumentEmission &emission); Address emitAllocYieldManyCoroutineBuffer(IRGenFunction &IGF); void emitDeallocYieldManyCoroutineBuffer(IRGenFunction &IGF, Address buffer); - void emitYieldManyCoroutineEntry(IRGenFunction &IGF, - CanSILFunctionType coroutineType, - Explosion &allParams); + void + emitYieldManyCoroutineEntry(IRGenFunction &IGF, + CanSILFunctionType coroutineType, + NativeCCEntryPointArgumentEmission &emission); + + Address emitTaskAlloc(IRGenFunction &IGF, llvm::Value *size, + Alignment alignment); + void emitTaskDealloc(IRGenFunction &IGF, Address address, llvm::Value *size); + std::pair emitAllocAsyncContext(IRGenFunction &IGF, + AsyncContextLayout layout); + void emitDeallocAsyncContext(IRGenFunction &IGF, Address context, Size size); /// Yield the given values from the current continuation. /// diff --git a/lib/IRGen/GenFunc.cpp b/lib/IRGen/GenFunc.cpp index 45fce96be67e4..ba6eaa593aed7 100644 --- a/lib/IRGen/GenFunc.cpp +++ b/lib/IRGen/GenFunc.cpp @@ -189,6 +189,7 @@ namespace { class FuncTypeInfo : public ScalarPairTypeInfo, public FuncSignatureInfo { + protected: FuncTypeInfo(CanSILFunctionType formalType, llvm::StructType *storageType, Size size, Alignment align, SpareBitVector &&spareBits, IsPOD_t pod) @@ -655,7 +656,7 @@ static void emitApplyArgument(IRGenFunction &IGF, out); } -static CanType getArgumentLoweringType(CanType type, SILParameterInfo paramInfo, +CanType irgen::getArgumentLoweringType(CanType type, SILParameterInfo paramInfo, bool isNoEscape) { switch (paramInfo.getConvention()) { // Capture value parameters by value, consuming them. diff --git a/lib/IRGen/GenFunc.h b/lib/IRGen/GenFunc.h index 04bc2e4732182..4c839cb6b509f 100644 --- a/lib/IRGen/GenFunc.h +++ b/lib/IRGen/GenFunc.h @@ -53,6 +53,8 @@ namespace irgen { ArrayRef argTypes, SubstitutionMap subs, CanSILFunctionType origType, CanSILFunctionType substType, CanSILFunctionType outType, Explosion &out, bool isOutlined); + CanType getArgumentLoweringType(CanType type, SILParameterInfo paramInfo, + bool isNoEscape); } // end namespace irgen } // end namespace swift diff --git a/lib/IRGen/GenObjC.cpp b/lib/IRGen/GenObjC.cpp index c46cadcaa5745..d31ee8328e724 100644 --- a/lib/IRGen/GenObjC.cpp +++ b/lib/IRGen/GenObjC.cpp @@ -833,11 +833,13 @@ static llvm::Function *emitObjCPartialApplicationForwarder(IRGenModule &IGM, } // Prepare the call to the underlying method. - CallEmission emission(subIGF, - getObjCMethodCallee(subIGF, method, self, + auto emission = getCallEmission( + subIGF, self, + getObjCMethodCallee(subIGF, method, self, CalleeInfo(origMethodType, origMethodType, {}))); + emission->begin(); - emission.setArgs(translatedParams, false); + emission->setArgs(translatedParams, false, /*witnessMetadata*/ nullptr); // Cleanup that always has to occur after the function call. auto cleanup = [&]{ @@ -855,14 +857,16 @@ static llvm::Function *emitObjCPartialApplicationForwarder(IRGenModule &IGM, if (indirectedDirectResult) { Address addr = indirectedResultTI->getAddressForPointer(indirectedDirectResult); - emission.emitToMemory(addr, *indirectedResultTI, false); + emission->emitToMemory(addr, *indirectedResultTI, false); + emission->end(); cleanup(); subIGF.Builder.CreateRetVoid(); } else { Explosion result; - emission.emitToExplosion(result, false); + emission->emitToExplosion(result, false); + emission->end(); cleanup(); - auto &callee = emission.getCallee(); + auto &callee = emission->getCallee(); auto resultType = callee.getOrigFunctionType()->getDirectFormalResultsType( IGM.getSILModule(), IGM.getMaximalTypeExpansionContext()); subIGF.emitScalarReturn(resultType, resultType, result, diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 2795553485a5a..706e64cf6d74e 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -54,11 +54,13 @@ #include "CallEmission.h" #include "ConformanceDescription.h" #include "ConstantBuilder.h" +#include "EntryPointArgumentEmission.h" #include "EnumPayload.h" #include "Explosion.h" #include "FixedTypeInfo.h" #include "Fulfillment.h" #include "GenArchetype.h" +#include "GenCall.h" #include "GenCast.h" #include "GenClass.h" #include "GenEnum.h" @@ -73,6 +75,7 @@ #include "IRGenFunction.h" #include "IRGenMangler.h" #include "IRGenModule.h" +#include "LoadableTypeInfo.h" #include "MetadataPath.h" #include "MetadataRequest.h" #include "NecessaryBindings.h" @@ -2232,11 +2235,43 @@ void EmitPolymorphicParameters::emit(Explosion &in, return getTypeInContext(type); }; + unsigned index = 0; // Collect any concrete type metadata that's been passed separately. enumerateUnfulfilledRequirements([&](GenericRequirement requirement) { - auto value = in.claimNext(); + llvm::Value *value; + if (Fn.isAsync()) { + auto *context = in.peek( + /*the index of the swift.context in the async function CC*/ 0); + auto layout = getAsyncContextLayout(IGF, &Fn); + Address dataAddr = layout.emitCastTo(IGF, context); + assert(layout.hasBindings()); + + auto bindingLayout = layout.getBindingsLayout(); + auto bindingsAddr = + bindingLayout.project(IGF, dataAddr, /*offsets*/ None); + auto erasedBindingsAddr = + IGF.Builder.CreateBitCast(bindingsAddr, IGF.IGM.Int8PtrPtrTy); + auto uncastBindingAddr = IGF.Builder.CreateConstArrayGEP( + erasedBindingsAddr, index, IGF.IGM.getPointerSize()); + if (requirement.Protocol) { + auto bindingAddrAddr = IGF.Builder.CreateBitCast( + uncastBindingAddr.getAddress(), IGF.IGM.WitnessTablePtrPtrTy); + auto bindingAddr = IGF.Builder.CreateLoad( + bindingAddrAddr, IGF.IGM.getPointerAlignment()); + value = bindingAddr; + } else { + auto bindingAddrAddr = IGF.Builder.CreateBitCast( + uncastBindingAddr.getAddress(), IGF.IGM.TypeMetadataPtrPtrTy); + auto bindingAddr = IGF.Builder.CreateLoad( + bindingAddrAddr, IGF.IGM.getPointerAlignment()); + value = bindingAddr; + } + } else { + value = in.claimNext(); + } bindGenericRequirement(IGF, requirement, value, MetadataState::Complete, getInContext); + ++index; }); // Bind all the fulfillments we can from the formal parameters. @@ -2631,21 +2666,21 @@ void MetadataPath::print(llvm::raw_ostream &out) const { /// Collect any required metadata for a witness method from the end of /// the given parameter list. -void irgen::collectTrailingWitnessMetadata(IRGenFunction &IGF, - SILFunction &fn, - Explosion ¶ms, - WitnessMetadata &witnessMetadata) { +void irgen::collectTrailingWitnessMetadata( + IRGenFunction &IGF, SILFunction &fn, + NativeCCEntryPointArgumentEmission &emission, + WitnessMetadata &witnessMetadata) { assert(fn.getLoweredFunctionType()->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod); - llvm::Value *wtable = params.takeLast(); + llvm::Value *wtable = emission.getSelfWitnessTable(); assert(wtable->getType() == IGF.IGM.WitnessTablePtrTy && "parameter signature mismatch: witness metadata didn't " "end in witness table?"); wtable->setName("SelfWitnessTable"); witnessMetadata.SelfWitnessTable = wtable; - llvm::Value *metatype = params.takeLast(); + llvm::Value *metatype = emission.getSelfMetadata(); assert(metatype->getType() == IGF.IGM.TypeMetadataPtrTy && "parameter signature mismatch: witness metadata didn't " "end in metatype?"); @@ -2698,9 +2733,14 @@ void NecessaryBindings::save(IRGenFunction &IGF, Address buffer) const { [&](GenericRequirement requirement) -> llvm::Value* { CanType type = requirement.TypeParameter; if (auto protocol = requirement.Protocol) { - auto wtable = - emitArchetypeWitnessTableRef(IGF, cast(type), protocol); - return wtable; + if (auto archetype = dyn_cast(type)) { + auto wtable = emitArchetypeWitnessTableRef(IGF, archetype, protocol); + return wtable; + } else { + auto conformance = getConformance(requirement); + auto wtable = emitWitnessTableRef(IGF, type, conformance); + return wtable; + } } else { auto metadata = IGF.emitTypeMetadataRef(type); return metadata; @@ -2712,7 +2752,8 @@ void NecessaryBindings::addTypeMetadata(CanType type) { assert(!isa(type)); // Bindings are only necessary at all if the type is dependent. - if (!type->hasArchetype()) return; + if (!type->hasArchetype() && !forAsyncFunction()) + return; // Break down structural types so that we don't eagerly pass metadata // for the structural type. Future considerations for this: @@ -2774,16 +2815,26 @@ static void addAbstractConditionalRequirements( void NecessaryBindings::addProtocolConformance(CanType type, ProtocolConformanceRef conf) { if (!conf.isAbstract()) { + auto concreteConformance = conf.getConcrete(); auto specializedConf = - dyn_cast(conf.getConcrete()); + dyn_cast(concreteConformance); // The partial apply forwarder does not have the context to reconstruct // abstract conditional conformance requirements. if (forPartialApply && specializedConf) { addAbstractConditionalRequirements(specializedConf, Requirements); + } else if (forAsyncFunction()) { + ProtocolDecl *protocol = conf.getRequirement(); + GenericRequirement requirement; + requirement.TypeParameter = type; + requirement.Protocol = protocol; + std::pair pair{requirement, + conf}; + Conformances.insert(pair); + Requirements.insert({type, concreteConformance->getProtocol()}); } return; } - assert(isa(type)); + assert(isa(type) || forAsyncFunction()); // TODO: pass something about the root conformance necessary to // reconstruct this. @@ -2970,10 +3021,8 @@ void EmitPolymorphicArguments::emit(SubstitutionMap subs, } } -NecessaryBindings -NecessaryBindings::forFunctionInvocations(IRGenModule &IGM, - CanSILFunctionType origType, - SubstitutionMap subs) { +NecessaryBindings NecessaryBindings::forAsyncFunctionInvocations( + IRGenModule &IGM, CanSILFunctionType origType, SubstitutionMap subs) { return computeBindings(IGM, origType, subs, false /*forPartialApplyForwarder*/); } diff --git a/lib/IRGen/GenProto.h b/lib/IRGen/GenProto.h index f91b802841c85..0cd6b594ebe33 100644 --- a/lib/IRGen/GenProto.h +++ b/lib/IRGen/GenProto.h @@ -46,6 +46,7 @@ namespace irgen { class IRGenModule; class MetadataPath; class MetadataResponse; + class NativeCCEntryPointArgumentEmission; class ProtocolInfo; class TypeInfo; @@ -120,9 +121,10 @@ namespace irgen { /// Collect any required metadata for a witness method from the end /// of the given parameter list. - void collectTrailingWitnessMetadata(IRGenFunction &IGF, SILFunction &fn, - Explosion ¶ms, - WitnessMetadata &metadata); + void + collectTrailingWitnessMetadata(IRGenFunction &IGF, SILFunction &fn, + NativeCCEntryPointArgumentEmission ¶ms, + WitnessMetadata &metadata); using GetParameterFn = llvm::function_ref; diff --git a/lib/IRGen/IRGenFunction.h b/lib/IRGen/IRGenFunction.h index 0a1affaf92acb..9dd387c364ad1 100644 --- a/lib/IRGen/IRGenFunction.h +++ b/lib/IRGen/IRGenFunction.h @@ -100,15 +100,18 @@ class IRGenFunction { void emitBBForReturn(); bool emitBranchToReturnBB(); - /// Return the error result slot, given an error type. There's - /// always only one error type. - Address getErrorResultSlot(SILType errorType); + /// Return the error result slot to be passed to the callee, given an error + /// type. There's always only one error type. + /// + /// For async functions, this is different from the caller result slot because + /// that is a gep into the %swift.context. + Address getCalleeErrorResultSlot(SILType errorType); /// Return the error result slot provided by the caller. Address getCallerErrorResultSlot(); - /// Set the error result slot. - void setErrorResultSlot(llvm::Value *address); + /// Set the error result slot for the current function. + void setCallerErrorResultSlot(llvm::Value *address); /// Are we currently emitting a coroutine? bool isCoroutine() { @@ -131,8 +134,10 @@ class IRGenFunction { Address ReturnSlot; llvm::BasicBlock *ReturnBB; - llvm::Value *ErrorResultSlot = nullptr; + llvm::Value *CalleeErrorResultSlot = nullptr; + llvm::Value *CallerErrorResultSlot = nullptr; llvm::Value *CoroutineHandle = nullptr; + bool IsAsync = false; //--- Helper methods ----------------------------------------------------------- public: @@ -146,6 +151,9 @@ class IRGenFunction { return getEffectiveOptimizationMode() == OptimizationMode::ForSize; } + bool isAsync() const { return IsAsync; } + void setAsync(bool async = true) { IsAsync = async; } + Address createAlloca(llvm::Type *ty, Alignment align, const llvm::Twine &name = ""); Address createAlloca(llvm::Type *ty, llvm::Value *arraySize, Alignment align, diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 2656badc54db5..dc95725adfa14 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -55,6 +55,7 @@ #include "llvm/Transforms/Utils/Local.h" #include "CallEmission.h" +#include "EntryPointArgumentEmission.h" #include "Explosion.h" #include "GenArchetype.h" #include "GenBuiltin.h" @@ -70,8 +71,8 @@ #include "GenIntegerLiteral.h" #include "GenObjC.h" #include "GenOpaque.h" -#include "GenPoly.h" #include "GenPointerAuth.h" +#include "GenPoly.h" #include "GenProto.h" #include "GenStruct.h" #include "GenTuple.h" @@ -1119,6 +1120,188 @@ class IRGenSILFunction : } // end anonymous namespace +static AsyncContextLayout getAsyncContextLayout(IRGenSILFunction &IGF) { + return getAsyncContextLayout(IGF, IGF.CurSILFn); +} + +namespace { +class SyncEntryPointArgumentEmission + : public virtual EntryPointArgumentEmission { +protected: + IRGenSILFunction &IGF; + SILBasicBlock &entry; + Explosion &allParamValues; + SyncEntryPointArgumentEmission(IRGenSILFunction &IGF, SILBasicBlock &entry, + Explosion &allParamValues) + : IGF(IGF), entry(entry), allParamValues(allParamValues){}; + +public: + bool requiresIndirectResult(SILType retType) override { + auto &schema = + IGF.IGM.getTypeInfo(retType).nativeReturnValueSchema(IGF.IGM); + return schema.requiresIndirect(); + } + llvm::Value *getIndirectResultForFormallyDirectResult() override { + return allParamValues.claimNext(); + } + llvm::Value *getIndirectResult(unsigned index) override { + return allParamValues.claimNext(); + }; +}; +class AsyncEntryPointArgumentEmission + : public virtual EntryPointArgumentEmission { +protected: + IRGenSILFunction &IGF; + SILBasicBlock &entry; + Explosion &allParamValues; + AsyncEntryPointArgumentEmission(IRGenSILFunction &IGF, SILBasicBlock &entry, + Explosion &allParamValues) + : IGF(IGF), entry(entry), allParamValues(allParamValues){}; +}; + +class COrObjCEntryPointArgumentEmission + : public virtual EntryPointArgumentEmission {}; + +class SyncCOrObjCEntryPointArgumentEmission + : public SyncEntryPointArgumentEmission, + public COrObjCEntryPointArgumentEmission { +public: + SyncCOrObjCEntryPointArgumentEmission(IRGenSILFunction &_IGF, + SILBasicBlock &_entry, + Explosion &_allParamValues) + : SyncEntryPointArgumentEmission(_IGF, _entry, _allParamValues){}; +}; + +class SyncNativeCCEntryPointArgumentEmission final + : public NativeCCEntryPointArgumentEmission, + public SyncEntryPointArgumentEmission { +public: + SyncNativeCCEntryPointArgumentEmission(IRGenSILFunction &_IGF, + SILBasicBlock &_entry, + Explosion &_allParamValues) + : SyncEntryPointArgumentEmission(_IGF, _entry, _allParamValues){}; + + llvm::Value *getCallerErrorResultArgument() override { + return allParamValues.takeLast(); + } + llvm::Value *getContext() override { return allParamValues.takeLast(); } + Explosion getArgumentExplosion(unsigned index, unsigned size) override { + assert(size > 0); + Explosion result; + allParamValues.transferInto(result, size); + return result; + } + llvm::Value *getSelfWitnessTable() override { + return allParamValues.takeLast(); + } + llvm::Value *getSelfMetadata() override { return allParamValues.takeLast(); } + llvm::Value *getCoroutineBuffer() override { + return allParamValues.claimNext(); + } +}; + +class AsyncNativeCCEntryPointArgumentEmission final + : public NativeCCEntryPointArgumentEmission, + public AsyncEntryPointArgumentEmission { + llvm::Value *context; + /*const*/ AsyncContextLayout layout; + const Address dataAddr; + +public: + AsyncNativeCCEntryPointArgumentEmission(IRGenSILFunction &IGF, + SILBasicBlock &entry, + Explosion &allParamValues) + : AsyncEntryPointArgumentEmission(IGF, entry, allParamValues), + context(allParamValues.claimNext()), layout(getAsyncContextLayout(IGF)), + dataAddr(layout.emitCastTo(IGF, context)){}; + + llvm::Value *getCallerErrorResultArgument() override { + auto errorLayout = layout.getErrorLayout(); + Address addr = errorLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); + return addr.getAddress(); + } + llvm::Value *getErrorResultAddrForCall() { + auto errorLayout = layout.getErrorLayout(); + auto &ti = cast(errorLayout.getType()); + auto allocaAddr = ti.allocateStack(IGF, layout.getErrorType(), "arg"); + auto addrInContext = + layout.getErrorLayout().project(IGF, dataAddr, /*offsets*/ llvm::None); + Explosion explosion; + ti.loadAsTake(IGF, addrInContext, explosion); + ti.initialize(IGF, explosion, allocaAddr.getAddress(), + /*isOutlined*/ false); + return allocaAddr.getAddress().getAddress(); + } + llvm::Value *getContext() override { + auto contextLayout = layout.getLocalContextLayout(); + Address addr = contextLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); + auto &ti = cast(contextLayout.getType()); + Explosion explosion; + ti.loadAsTake(IGF, addr, explosion); + return explosion.claimNext(); + } + Explosion getArgumentExplosion(unsigned index, unsigned size) override { + assert(size > 0); + Explosion result; + for (unsigned i = index, end = index + size; i < end; ++i) { + auto argumentLayout = layout.getArgumentLayout(i); + auto addr = argumentLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); + auto &ti = cast(argumentLayout.getType()); + Explosion explosion; + ti.loadAsTake(IGF, addr, explosion); + result.add(explosion.claimAll()); + } + return result; + } + bool requiresIndirectResult(SILType retType) override { return false; } + llvm::Value *getIndirectResultForFormallyDirectResult() override { + llvm_unreachable("async function do not need to lower direct SIL results " + "into indirect IR results because all results are already " + "indirected through the context"); + } + llvm::Value *getIndirectResult(unsigned index) override { + Address dataAddr = layout.emitCastTo(IGF, context); + unsigned baseIndirectReturnIndex = layout.getFirstIndirectReturnIndex(); + unsigned elementIndex = baseIndirectReturnIndex + index; + auto &fieldLayout = layout.getElement(elementIndex); + Address fieldAddr = + fieldLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); + return IGF.Builder.CreateLoad(fieldAddr); + }; + llvm::Value *getSelfWitnessTable() override { + llvm_unreachable("unimplemented"); + } + llvm::Value *getSelfMetadata() override { llvm_unreachable("unimplemented"); } + llvm::Value *getCoroutineBuffer() override { + llvm_unreachable("unimplemented"); + } +}; + +std::unique_ptr +getNativeCCEntryPointArgumentEmission(IRGenSILFunction &IGF, + SILBasicBlock &entry, + Explosion &allParamValues) { + if (IGF.CurSILFn->isAsync()) { + return std::make_unique( + IGF, entry, allParamValues); + } else { + return std::make_unique( + IGF, entry, allParamValues); + } +} +std::unique_ptr +getCOrObjCEntryPointArgumentEmission(IRGenSILFunction &IGF, + SILBasicBlock &entry, + Explosion &allParamValues) { + if (IGF.CurSILFn->isAsync()) { + llvm_unreachable("unsupported"); + } else { + return std::make_unique( + IGF, entry, allParamValues); + } +} +} // end anonymous namespace + void LoweredValue::getExplosion(IRGenFunction &IGF, SILType type, Explosion &ex) const { switch (kind) { @@ -1221,6 +1404,8 @@ IRGenSILFunction::IRGenSILFunction(IRGenModule &IGM, SILFunction *f) if (f->isDynamicallyReplaceable()) { IGM.createReplaceableProlog(*this, f); } + + setAsync(f->getLoweredFunctionType()->isAsync()); } IRGenSILFunction::~IRGenSILFunction() { @@ -1303,22 +1488,24 @@ static void addIncomingExplosionToPHINodes(IRGenSILFunction &IGF, Explosion &argValue); // TODO: Handle this during SIL AddressLowering. -static ArrayRef emitEntryPointIndirectReturn( - IRGenSILFunction &IGF, - SILBasicBlock *entry, - Explosion ¶ms, - CanSILFunctionType funcTy, - llvm::function_ref requiresIndirectResult) { +static ArrayRef emitEntryPointIndirectReturn( + EntryPointArgumentEmission &emission, IRGenSILFunction &IGF, + SILBasicBlock *entry, CanSILFunctionType funcTy, + llvm::function_ref requiresIndirectResult) { // Map an indirect return for a type SIL considers loadable but still // requires an indirect return at the IR level. SILFunctionConventions fnConv(funcTy, IGF.getSILModule()); SILType directResultType = IGF.CurSILFn->mapTypeIntoContext( fnConv.getSILResultType(IGF.IGM.getMaximalTypeExpansionContext())); if (requiresIndirectResult(directResultType)) { + assert(!IGF.CurSILFn->isAsync() && + "async function do not need to lower direct SIL results into " + "indirect IR results because all results are already indirected " + "through the context"); auto ¶mTI = IGF.IGM.getTypeInfo(directResultType); auto &retTI = IGF.IGM.getTypeInfo(IGF.getLoweredTypeInContext(directResultType)); - auto ptr = params.claimNext(); + auto ptr = emission.getIndirectResultForFormallyDirectResult(); if (paramTI.getStorageType() != retTI.getStorageType()) { assert(directResultType.getASTType()->hasOpaqueArchetype()); ptr = IGF.Builder.CreateBitCast(ptr, @@ -1338,10 +1525,11 @@ static ArrayRef emitEntryPointIndirectReturn( auto inContextResultType = IGF.CurSILFn->mapTypeIntoContext(indirectResultType); auto &retTI = IGF.IGM.getTypeInfo(ret->getType()); + auto ¶mTI = IGF.IGM.getTypeInfo(inContextResultType); + // The parameter's type might be different due to looking through opaque // archetypes. - auto ptr = params.claimNext(); - auto ¶mTI = IGF.IGM.getTypeInfo(inContextResultType); + llvm::Value *ptr = emission.getIndirectResult(idx); if (paramTI.getStorageType() != retTI.getStorageType()) { assert(inContextResultType.getASTType()->hasOpaqueArchetype()); ptr = IGF.Builder.CreateBitCast(ptr, @@ -1356,10 +1544,10 @@ static ArrayRef emitEntryPointIndirectReturn( return bbargs.slice(numIndirectResults); } -static void bindParameter(IRGenSILFunction &IGF, - SILArgument *param, - SILType paramTy, - Explosion &allParamValues) { +template +static void bindParameter(IRGenSILFunction &IGF, unsigned index, + SILArgument *param, SILType paramTy, + ExplosionForArgument explosionForArgument) { // Pull out the parameter value and its formal type. auto ¶mTI = IGF.getTypeInfo(IGF.CurSILFn->mapTypeIntoContext(paramTy)); auto &argTI = IGF.getTypeInfo(param->getType()); @@ -1374,8 +1562,9 @@ static void bindParameter(IRGenSILFunction &IGF, // indirect address. auto &nativeSchema = argTI.nativeParameterValueSchema(IGF.IGM); if (nativeSchema.requiresIndirect()) { + Explosion paramExplosion = explosionForArgument(index, 1); Address paramAddr = - loadableParamTI.getAddressForPointer(allParamValues.claimNext()); + loadableParamTI.getAddressForPointer(paramExplosion.claimNext()); if (paramTI.getStorageType() != argTI.getStorageType()) paramAddr = loadableArgTI.getAddressForPointer(IGF.Builder.CreateBitCast( @@ -1387,7 +1576,9 @@ static void bindParameter(IRGenSILFunction &IGF, // Otherwise, we map from the native convention to the type's explosion // schema. Explosion nativeParam; - allParamValues.transferInto(nativeParam, nativeSchema.size()); + unsigned size = nativeSchema.size(); + Explosion paramExplosion = explosionForArgument(index, size); + paramExplosion.transferInto(nativeParam, size); paramValues = nativeSchema.mapFromNative(IGF.IGM, IGF, nativeParam, param->getType()); } else { @@ -1403,7 +1594,8 @@ static void bindParameter(IRGenSILFunction &IGF, // FIXME: that doesn't mean we should physically pass it // indirectly at this resilience expansion. An @in or @in_guaranteed parameter // could be passed by value in the right resilience domain. - auto ptr = allParamValues.claimNext(); + Explosion paramExplosion = explosionForArgument(index, 1); + auto ptr = paramExplosion.claimNext(); if (paramTI.getStorageType() != argTI.getStorageType()) { ptr = IGF.Builder.CreateBitCast(ptr, argTI.getStorageType()->getPointerTo()); @@ -1417,36 +1609,36 @@ static void bindParameter(IRGenSILFunction &IGF, static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, SILBasicBlock *entry, Explosion &allParamValues) { + auto emission = + getNativeCCEntryPointArgumentEmission(IGF, *entry, allParamValues); auto funcTy = IGF.CurSILFn->getLoweredFunctionType(); - + // Map the indirect return if present. ArrayRef params = emitEntryPointIndirectReturn( - IGF, entry, allParamValues, funcTy, [&](SILType retType) -> bool { - auto &schema = - IGF.IGM.getTypeInfo(retType).nativeReturnValueSchema(IGF.IGM); - return schema.requiresIndirect(); + *emission, IGF, entry, funcTy, [&](SILType retType) -> bool { + return emission->requiresIndirectResult(retType); }); // The witness method CC passes Self as a final argument. WitnessMetadata witnessMetadata; if (funcTy->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod) { - collectTrailingWitnessMetadata(IGF, *IGF.CurSILFn, allParamValues, + collectTrailingWitnessMetadata(IGF, *IGF.CurSILFn, *emission, witnessMetadata); } // Bind the error result by popping it off the parameter list. if (funcTy->hasErrorResult()) { - IGF.setErrorResultSlot(allParamValues.takeLast()); + IGF.setCallerErrorResultSlot(emission->getCallerErrorResultArgument()); } // The coroutine context should be the first parameter. switch (funcTy->getCoroutineKind()) { case SILCoroutineKind::None: break; case SILCoroutineKind::YieldOnce: - emitYieldOnceCoroutineEntry(IGF, funcTy, allParamValues); + emitYieldOnceCoroutineEntry(IGF, funcTy, *emission); break; case SILCoroutineKind::YieldMany: - emitYieldManyCoroutineEntry(IGF, funcTy, allParamValues); + emitYieldManyCoroutineEntry(IGF, funcTy, *emission); break; } @@ -1458,20 +1650,24 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, SILArgument *selfParam = params.back(); params = params.drop_back(); - Explosion selfTemp; - selfTemp.add(allParamValues.takeLast()); bindParameter( - IGF, selfParam, + IGF, 0, selfParam, conv.getSILArgumentType(conv.getNumSILArguments() - 1, IGF.IGM.getMaximalTypeExpansionContext()), - selfTemp); + [&](unsigned startIndex, unsigned size) { + assert(size == 1); + Explosion selfTemp; + selfTemp.add(emission->getContext()); + return selfTemp; + }); // Even if we don't have a 'self', if we have an error result, we // should have a placeholder argument here. } else if (funcTy->hasErrorResult() || funcTy->getRepresentation() == SILFunctionTypeRepresentation::Thick) { - llvm::Value *contextPtr = allParamValues.takeLast(); (void) contextPtr; + llvm::Value *contextPtr = emission->getContext(); + (void)contextPtr; assert(contextPtr->getType() == IGF.IGM.RefCountedPtrTy); } @@ -1479,10 +1675,12 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, unsigned i = 0; for (SILArgument *param : params) { auto argIdx = conv.getSILArgIndexOfFirstParam() + i; - bindParameter(IGF, param, + bindParameter(IGF, i, param, conv.getSILArgumentType( argIdx, IGF.IGM.getMaximalTypeExpansionContext()), - allParamValues); + [&](unsigned index, unsigned size) { + return emission->getArgumentExplosion(index, size); + }); ++i; } @@ -1507,6 +1705,7 @@ static void emitEntryPointArgumentsCOrObjC(IRGenSILFunction &IGF, SILBasicBlock *entry, Explosion ¶ms, CanSILFunctionType funcTy) { + auto emission = getCOrObjCEntryPointArgumentEmission(IGF, *entry, params); // First, lower the method type. ForeignFunctionInfo foreignInfo = IGF.IGM.getForeignFunctionInfo(funcTy); assert(foreignInfo.ClangInfo); @@ -1515,9 +1714,8 @@ static void emitEntryPointArgumentsCOrObjC(IRGenSILFunction &IGF, // Okay, start processing the parameters explosion. // First, claim all the indirect results. - ArrayRef args - = emitEntryPointIndirectReturn(IGF, entry, params, funcTy, - [&](SILType directResultType) -> bool { + ArrayRef args = emitEntryPointIndirectReturn( + *emission, IGF, entry, funcTy, [&](SILType directResultType) -> bool { return FI.getReturnInfo().isIndirect(); }); @@ -2342,14 +2540,11 @@ Callee LoweredValue::getCallee(IRGenFunction &IGF, llvm_unreachable("bad kind"); } -static CallEmission getCallEmissionForLoweredValue(IRGenSILFunction &IGF, - CanSILFunctionType origCalleeType, - CanSILFunctionType substCalleeType, - const LoweredValue &lv, - llvm::Value *selfValue, - SubstitutionMap substitutions, - WitnessMetadata *witnessMetadata, - Explosion &args) { +static std::unique_ptr getCallEmissionForLoweredValue( + IRGenSILFunction &IGF, CanSILFunctionType origCalleeType, + CanSILFunctionType substCalleeType, const LoweredValue &lv, + llvm::Value *selfValue, SubstitutionMap substitutions, + WitnessMetadata *witnessMetadata) { Callee callee = lv.getCallee(IGF, selfValue, CalleeInfo(origCalleeType, substCalleeType, substitutions)); @@ -2371,10 +2566,10 @@ static CallEmission getCallEmissionForLoweredValue(IRGenSILFunction &IGF, break; } - CallEmission callEmission(IGF, std::move(callee)); + auto callEmission = getCallEmission(IGF, selfValue, std::move(callee)); if (IGF.CurSILFn->isThunk()) - callEmission.addAttribute(llvm::AttributeList::FunctionIndex, - llvm::Attribute::NoInline); + callEmission->addAttribute(llvm::AttributeList::FunctionIndex, + llvm::Attribute::NoInline); return callEmission; } @@ -2455,11 +2650,11 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { Explosion llArgs; WitnessMetadata witnessMetadata; - CallEmission emission = - getCallEmissionForLoweredValue(*this, origCalleeType, substCalleeType, - calleeLV, selfValue, - site.getSubstitutionMap(), - &witnessMetadata, llArgs); + auto emission = getCallEmissionForLoweredValue( + *this, origCalleeType, substCalleeType, calleeLV, selfValue, + site.getSubstitutionMap(), &witnessMetadata); + + emission->begin(); // Lower the arguments and return value in the callee's generic context. GenericContextScope scope(IGM, origCalleeType->getInvocationGenericSignature()); @@ -2486,9 +2681,7 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { // Turn the formal SIL parameters into IR-gen things. for (auto index : indices(args)) { - emitApplyArgument(*this, args[index], - origConv.getSILArgumentType( - index, IGM.getMaximalTypeExpansionContext()), + emitApplyArgument(*this, args[index], emission->getParameterType(index), llArgs); } @@ -2500,29 +2693,30 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { } // Add all those arguments. - emission.setArgs(llArgs, false, &witnessMetadata); + emission->setArgs(llArgs, false, &witnessMetadata); SILInstruction *i = site.getInstruction(); Explosion result; - emission.emitToExplosion(result, false); + emission->emitToExplosion(result, false); // For a simple apply, just bind the apply result to the result of the call. if (auto apply = dyn_cast(i)) { setLoweredExplosion(apply, result); + emission->end(); // For begin_apply, we have to destructure the call. } else if (auto beginApply = dyn_cast(i)) { // Grab the continuation pointer. This will still be an i8*. auto continuation = result.claimNext(); - setLoweredCoroutine(beginApply->getTokenResult(), - { *coroutineBuffer, - continuation, - emission.claimTemporaries() }); + setLoweredCoroutine( + beginApply->getTokenResult(), + {*coroutineBuffer, continuation, emission->claimTemporaries()}); setCorrespondingLoweredValues(beginApply->getYieldedValues(), result); + emission->end(); } else { auto tryApplyInst = cast(i); @@ -2530,8 +2724,9 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { SILFunctionConventions substConv(substCalleeType, getSILModule()); SILType errorType = substConv.getSILErrorType(IGM.getMaximalTypeExpansionContext()); - Address errorSlot = getErrorResultSlot(errorType); - auto errorValue = Builder.CreateLoad(errorSlot); + Address calleeErrorSlot = emission->getCalleeErrorSlot(errorType); + auto errorValue = Builder.CreateLoad(calleeErrorSlot); + emission->end(); auto &normalDest = getLoweredBB(tryApplyInst->getNormalBB()); auto &errorDest = getLoweredBB(tryApplyInst->getErrorBB()); @@ -2542,7 +2737,7 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { if (!tryApplyInst->getErrorBB()->getSinglePredecessorBlock()) { // Only do that here if we can't move the store to the error block. // See below. - Builder.CreateStore(nullError, errorSlot); + Builder.CreateStore(nullError, calleeErrorSlot); } // If the error value is non-null, branch to the error destination. @@ -2563,7 +2758,7 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { // that it will become a dead store. auto origBB = Builder.GetInsertBlock(); Builder.SetInsertPoint(errorDest.bb); - Builder.CreateStore(nullError, errorSlot); + Builder.CreateStore(nullError, calleeErrorSlot); Builder.SetInsertPoint(origBB); } } @@ -2705,6 +2900,7 @@ static bool isSimplePartialApply(IRGenFunction &IGF, PartialApplyInst *i) { } void IRGenSILFunction::visitPartialApplyInst(swift::PartialApplyInst *i) { + // TODO: Handle async! SILValue v(i); if (isSimplePartialApply(*this, i)) { @@ -2891,9 +3087,32 @@ static void emitReturnInst(IRGenSILFunction &IGF, // Even if SIL has a direct return, the IR-level calling convention may // require an indirect return. if (IGF.IndirectReturn.isValid()) { + assert(!IGF.isAsync()); auto &retTI = cast(IGF.getTypeInfo(resultTy)); retTI.initialize(IGF, result, IGF.IndirectReturn, false); IGF.Builder.CreateRetVoid(); + } else if (IGF.isAsync()) { + // If we're generating an async function, store the result into the buffer. + assert(!IGF.IndirectReturn.isValid() && + "Formally direct results should stay direct results for async " + "functions"); + Explosion parameters = IGF.collectParameters(); + llvm::Value *context = parameters.claimNext(); + auto layout = getAsyncContextLayout(IGF); + + Address dataAddr = layout.emitCastTo(IGF, context); + unsigned index = layout.getFirstDirectReturnIndex(); + for (auto r : + IGF.CurSILFn->getLoweredFunctionType()->getDirectFormalResults()) { + (void)r; + auto &fieldLayout = layout.getElement(index); + Address fieldAddr = + fieldLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); + cast(fieldLayout.getType()) + .initialize(IGF, result, fieldAddr, /*isOutlined*/ false); + ++index; + } + IGF.Builder.CreateRetVoid(); } else { auto funcLang = IGF.CurSILFn->getLoweredFunctionType()->getLanguage(); auto swiftCCReturn = funcLang == SILFunctionLanguage::Swift; @@ -3918,7 +4137,7 @@ void IRGenSILFunction::emitErrorResultVar(CanSILFunctionType FnTy, // swifterror in a register. if (IGM.IsSwiftErrorInRegister) return; - auto ErrorResultSlot = getErrorResultSlot(IGM.silConv.getSILType( + auto ErrorResultSlot = getCalleeErrorResultSlot(IGM.silConv.getSILType( ErrorInfo, FnTy, IGM.getMaximalTypeExpansionContext())); auto Var = DbgValue->getVarInfo(); assert(Var && "error result without debug info"); @@ -4357,8 +4576,9 @@ void IRGenSILFunction::visitAllocStackInst(swift::AllocStackInst *i) { dbgname = getVarName(i, IsAnonymous); # endif - auto addr = type.allocateStack(*this, i->getElementType(), dbgname); - setLoweredStackAddress(i, addr); + auto stackAddr = type.allocateStack(*this, i->getElementType(), dbgname); + setLoweredStackAddress(i, stackAddr); + Address addr = stackAddr.getAddress(); // Generate Debug Info. if (!Decl) @@ -4369,8 +4589,8 @@ void IRGenSILFunction::visitAllocStackInst(swift::AllocStackInst *i) { Type Desugared = Decl->getType()->getDesugaredType(); if (Desugared->getClassOrBoundGenericClass() || Desugared->getStructOrBoundGenericStruct()) - zeroInit(dyn_cast(addr.getAddress().getAddress())); - emitDebugInfoForAllocStack(i, type, addr.getAddress().getAddress()); + zeroInit(dyn_cast(addr.getAddress())); + emitDebugInfoForAllocStack(i, type, addr.getAddress()); } static void diff --git a/lib/IRGen/NecessaryBindings.h b/lib/IRGen/NecessaryBindings.h index b4ad8200bbd12..df73cae8621ca 100644 --- a/lib/IRGen/NecessaryBindings.h +++ b/lib/IRGen/NecessaryBindings.h @@ -41,7 +41,7 @@ namespace irgen { /// order to perform some set of operations on a type. class NecessaryBindings { llvm::SetVector Requirements; - + llvm::DenseMap Conformances; /// Are the bindings to be computed for a partial apply forwarder. /// In the case this is true we need to store/restore the conformance of a @@ -54,9 +54,9 @@ class NecessaryBindings { /// Collect the necessary bindings to invoke a function with the given /// signature. - static NecessaryBindings forFunctionInvocations(IRGenModule &IGM, - CanSILFunctionType origType, - SubstitutionMap subs); + static NecessaryBindings + forAsyncFunctionInvocations(IRGenModule &IGM, CanSILFunctionType origType, + SubstitutionMap subs); static NecessaryBindings forPartialApplyForwarder(IRGenModule &IGM, CanSILFunctionType origType, SubstitutionMap subs, @@ -71,6 +71,11 @@ class NecessaryBindings { return Requirements[i]; } + ProtocolConformanceRef + getConformance(const GenericRequirement &requirement) const { + return Conformances.lookup(requirement); + } + size_t size() const { return Requirements.size(); } @@ -95,6 +100,9 @@ class NecessaryBindings { const llvm::SetVector &getRequirements() const { return Requirements; } + + bool forAsyncFunction() { return !forPartialApply; } + private: static NecessaryBindings computeBindings(IRGenModule &IGM, CanSILFunctionType origType, diff --git a/test/IRGen/async/run-call-classinstance-int64-to-void.sil b/test/IRGen/async/run-call-classinstance-int64-to-void.sil new file mode 100644 index 0000000000000..cce841fa8e0f3 --- /dev/null +++ b/test/IRGen/async/run-call-classinstance-int64-to-void.sil @@ -0,0 +1,105 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printAny : $@convention(thin) (@in_guaranteed Any) -> () + + + + + +class S { + func classinstanceSInt64ToVoid(_ int: Int64) async + deinit + init() +} + +// CHECK-LL: define hidden swiftcc void @classinstanceSInt64ToVoid(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @classinstanceSInt64ToVoid : $@async @convention(method) (Int64, @guaranteed S) -> () { +bb0(%int : $Int64, %instance : $S): + %take_s = function_ref @take_S : $@convention(thin) (@guaranteed S) -> () + %result = apply %take_s(%instance) : $@convention(thin) (@guaranteed S) -> () + return %result : $() +} + +sil hidden @take_S : $@convention(thin) (@guaranteed S) -> () { +bb0(%instance : $S): + %any = alloc_stack $Any + strong_retain %instance : $S + %any_addr = init_existential_addr %any : $*Any, $S + store %instance to %any_addr : $*S + %print_any = function_ref @printAny : $@convention(thin) (@in_guaranteed Any) -> () + %result = apply %print_any(%any) : $@convention(thin) (@in_guaranteed Any) -> () + destroy_addr %any_addr : $*S + dealloc_stack %any : $*Any + return %result : $() +} + +sil hidden [exact_self_class] @S_allocating_init : $@convention(method) (@thick S.Type) -> @owned S { +bb0(%0 : $@thick S.Type): + %1 = alloc_ref $S + %2 = function_ref @$S_init : $@convention(method) (@owned S) -> @owned S + %3 = apply %2(%1) : $@convention(method) (@owned S) -> @owned S + return %3 : $S +} + +sil hidden @$S_init : $@convention(method) (@owned S) -> @owned S { +bb0(%0 : $S): + return %0 : $S +} + +sil hidden @$S_deinit : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject { +bb0(%0 : $S): + %2 = unchecked_ref_cast %0 : $S to $Builtin.NativeObject + return %2 : $Builtin.NativeObject +} + +sil hidden @S_deallocating_deinit : $@convention(method) (@owned S) -> () { +bb0(%0 : $S): + %2 = function_ref @$S_deinit : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject + %3 = apply %2(%0) : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject + %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $S + dealloc_ref %4 : $S + %6 = tuple () + return %6 : $() +} + +sil_vtable S { + #S.classinstanceSInt64ToVoid: (S) -> (Int64) async -> () : @classinstanceSInt64ToVoid + #S.init!allocator: (S.Type) -> () -> S : @S_allocating_init + #S.deinit!deallocator: @S_deallocating_deinit +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %s_type = metatype $@thick S.Type + %allocating_init = function_ref @S_allocating_init : $@convention(method) (@thick S.Type) -> @owned S + %instance = apply %allocating_init(%s_type) : $@convention(method) (@thick S.Type) -> @owned S + %classinstanceSInt64ToVoid = class_method %instance : $S, #S.classinstanceSInt64ToVoid : (S) -> (Int64) async -> (), $@convention(method) @async (Int64, @guaranteed S) -> () + strong_retain %instance : $S + %int_literal = integer_literal $Builtin.Int64, 42 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %result = apply %classinstanceSInt64ToVoid(%int, %instance) : $@convention(method) @async (Int64, @guaranteed S) -> () // CHECK: main.S + strong_release %instance : $S + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} + + + diff --git a/test/IRGen/async/run-call-classinstance-void-to-void.sil b/test/IRGen/async/run-call-classinstance-void-to-void.sil new file mode 100644 index 0000000000000..cad66b346e9a9 --- /dev/null +++ b/test/IRGen/async/run-call-classinstance-void-to-void.sil @@ -0,0 +1,102 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printAny : $@convention(thin) (@in_guaranteed Any) -> () + + + + + +class S { + func classinstanceSVoidToVoid() async + deinit + init() +} + +// CHECK-LL: define hidden swiftcc void @classinstanceSVoidToVoid(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @classinstanceSVoidToVoid : $@async @convention(method) (@guaranteed S) -> () { +bb0(%instance : $S): + %take_s = function_ref @take_S : $@convention(thin) (@guaranteed S) -> () + %result = apply %take_s(%instance) : $@convention(thin) (@guaranteed S) -> () + return %result : $() +} + +sil hidden @take_S : $@convention(thin) (@guaranteed S) -> () { +bb0(%instance : $S): + %any = alloc_stack $Any + strong_retain %instance : $S + %any_addr = init_existential_addr %any : $*Any, $S + store %instance to %any_addr : $*S + %print_any = function_ref @printAny : $@convention(thin) (@in_guaranteed Any) -> () + %result = apply %print_any(%any) : $@convention(thin) (@in_guaranteed Any) -> () + destroy_addr %any_addr : $*S + dealloc_stack %any : $*Any + return %result : $() +} + +sil hidden [exact_self_class] @S_allocating_init : $@convention(method) (@thick S.Type) -> @owned S { +bb0(%0 : $@thick S.Type): + %1 = alloc_ref $S + %2 = function_ref @$S_init : $@convention(method) (@owned S) -> @owned S + %3 = apply %2(%1) : $@convention(method) (@owned S) -> @owned S + return %3 : $S +} + +sil hidden @$S_init : $@convention(method) (@owned S) -> @owned S { +bb0(%0 : $S): + return %0 : $S +} + +sil hidden @$S_deinit : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject { +bb0(%0 : $S): + %2 = unchecked_ref_cast %0 : $S to $Builtin.NativeObject + return %2 : $Builtin.NativeObject +} + +sil hidden @S_deallocating_deinit : $@convention(method) (@owned S) -> () { +bb0(%0 : $S): + %2 = function_ref @$S_deinit : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject + %3 = apply %2(%0) : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject + %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $S + dealloc_ref %4 : $S + %6 = tuple () + return %6 : $() +} + +sil_vtable S { + #S.classinstanceSVoidToVoid: (S) -> () async -> () : @classinstanceSVoidToVoid + #S.init!allocator: (S.Type) -> () -> S : @S_allocating_init + #S.deinit!deallocator: @S_deallocating_deinit +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %s_type = metatype $@thick S.Type + %allocating_init = function_ref @S_allocating_init : $@convention(method) (@thick S.Type) -> @owned S + %instance = apply %allocating_init(%s_type) : $@convention(method) (@thick S.Type) -> @owned S + %classinstanceSVoidToVoid = class_method %instance : $S, #S.classinstanceSVoidToVoid : (S) -> () async -> (), $@convention(method) @async (@guaranteed S) -> () + strong_retain %instance : $S + %result = apply %classinstanceSVoidToVoid(%instance) : $@convention(method) @async (@guaranteed S) -> () // CHECK: main.S + strong_release %instance : $S + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} + + diff --git a/test/IRGen/async/run-call-existential-to-void.sil b/test/IRGen/async/run-call-existential-to-void.sil new file mode 100644 index 0000000000000..f76c750e2ff7b --- /dev/null +++ b/test/IRGen/async/run-call-existential-to-void.sil @@ -0,0 +1,78 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printAny : $@convention(thin) (@in_guaranteed Any) -> () + +public protocol P { +} + +public struct S : P { + @_hasStorage let int: Int64 { get } + init(int: Int64) +} + + +sil_witness_table [serialized] S: P module main { +} + +sil hidden @S_init : $@convention(method) (Int64, @thin S.Type) -> S { +bb0(%int : $Int64, %S_type : $@thin S.Type): + %instance = struct $S (%int : $Int64) + return %instance : $S +} + +// CHECK-LL: define hidden swiftcc void @existentialToVoid(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @existentialToVoid : $@async @convention(thin) (@in_guaranteed P) -> () { +bb0(%existential : $*P): + %existential_addr = open_existential_addr immutable_access %existential : $*P to $*@opened("B2796A9C-FEBE-11EA-84BB-D0817AD71B77") P + %any = alloc_stack $Any + %any_addr = init_existential_addr %any : $*Any, $@opened("B2796A9C-FEBE-11EA-84BB-D0817AD71B77") P + copy_addr %existential_addr to [initialization] %any_addr : $*@opened("B2796A9C-FEBE-11EA-84BB-D0817AD71B77") P + %printAny = function_ref @printAny : $@convention(thin) (@in_guaranteed Any) -> () + %result = apply %printAny(%any) : $@convention(thin) (@in_guaranteed Any) -> () + destroy_addr %any : $*Any + dealloc_stack %any : $*Any + return %result : $() +} + +sil hidden @call : $@convention(thin) () -> () { +bb0: + %S_type = metatype $@thin S.Type + %int_literal = integer_literal $Builtin.Int64, 7384783 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %S_init = function_ref @S_init : $@convention(method) (Int64, @thin S.Type) -> S + %instance = apply %S_init(%int, %S_type) : $@convention(method) (Int64, @thin S.Type) -> S + %existential = alloc_stack $P, let, name "existential" + %existential_addr = init_existential_addr %existential : $*P, $S + store %instance to %existential_addr : $*S + %existentialToVoid = function_ref @existentialToVoid : $@async @convention(thin) (@in_guaranteed P) -> () + %result = apply %existentialToVoid(%existential) : $@async @convention(thin) (@in_guaranteed P) -> () + destroy_addr %existential : $*P + dealloc_stack %existential : $*P + return %result : $() +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %call = function_ref @call : $@convention(thin) () -> () // CHECK: 7384783 + %result = apply %call() : $@convention(thin) () -> () + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 +} + diff --git a/test/IRGen/async/run-call-generic-to-generic.sil b/test/IRGen/async/run-call-generic-to-generic.sil new file mode 100644 index 0000000000000..491d5b610f32d --- /dev/null +++ b/test/IRGen/async/run-call-generic-to-generic.sil @@ -0,0 +1,54 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +// CHECK-LL: define hidden swiftcc void @genericToGeneric(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @genericToGeneric : $@async @convention(thin) (@in_guaranteed T) -> @out T { +bb0(%out : $*T, %in : $*T): + copy_addr %in to [initialization] %out : $*T + %result = tuple () + return %result : $() +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %int_literal = integer_literal $Builtin.Int64, 42 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %int_addr = alloc_stack $Int64 + store %int to %int_addr : $*Int64 + %out_addr = alloc_stack $Int64 + + %genericToGeneric = function_ref @genericToGeneric : $@async @convention(thin) (@in_guaranteed T) -> @out T + %result1 = apply %genericToGeneric(%int_addr, %out_addr) : $@async @convention(thin) (@in_guaranteed T) -> @out T + + %print_int = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %out = load %out_addr : $*Int64 + %result2 = apply %print_int(%out) : $@convention(thin) (Int64) -> () // CHECK: 42 + + dealloc_stack %out_addr : $*Int64 + dealloc_stack %int_addr : $*Int64 + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} + + + + diff --git a/test/IRGen/async/run-call-generic-to-void.sil b/test/IRGen/async/run-call-generic-to-void.sil new file mode 100644 index 0000000000000..d692426cbaf5e --- /dev/null +++ b/test/IRGen/async/run-call-generic-to-void.sil @@ -0,0 +1,45 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + +// CHECK-LL: define hidden swiftcc void @genericToVoid(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @genericToVoid : $@async @convention(thin) (@in_guaranteed T) -> () { +bb0(%instance : $*T): + %print_generic = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %result = apply %print_generic(%instance) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: 922337203685477580 + return %result : $() +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %int_literal = integer_literal $Builtin.Int64, 922337203685477580 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %int_addr = alloc_stack $Int64 + store %int to %int_addr : $*Int64 + %genericToVoid = function_ref @genericToVoid : $@async @convention(thin) (@in_guaranteed T) -> () + %result = apply %genericToVoid(%int_addr) : $@async @convention(thin) (@in_guaranteed T) -> () + dealloc_stack %int_addr : $*Int64 + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} + + + diff --git a/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil b/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil new file mode 100644 index 0000000000000..5b8ae8f9b015c --- /dev/null +++ b/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil @@ -0,0 +1,59 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printBool : $@convention(thin) (Bool) -> () + +// CHECK-LL: define hidden swiftcc void @genericEquatableAndGenericEquatableToBool(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @genericEquatableAndGenericEquatableToBool : $@async @convention(thin) (@in_guaranteed T, @in_guaranteed T) -> Bool { +bb0(%0 : $*T, %1 : $*T): + %4 = metatype $@thick T.Type + %5 = witness_method $T, #Equatable."==" : (Self.Type) -> (Self, Self) -> Bool : $@convention(witness_method: Equatable) <τ_0_0 where τ_0_0 : Equatable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> Bool + %6 = apply %5(%0, %1, %4) : $@convention(witness_method: Equatable) <τ_0_0 where τ_0_0 : Equatable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> Bool + return %6 : $Bool +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %int1_literal = integer_literal $Builtin.Int64, 42 + %int1 = struct $Int64 (%int1_literal : $Builtin.Int64) + %int1_addr = alloc_stack $Int64 + store %int1 to %int1_addr : $*Int64 + + %int2_literal = integer_literal $Builtin.Int64, 99 + %int2 = struct $Int64 (%int2_literal : $Builtin.Int64) + %int2_addr = alloc_stack $Int64 + store %int2 to %int2_addr : $*Int64 + + %genericEquatableAndGenericEquatableToBool = function_ref @genericEquatableAndGenericEquatableToBool : $@async @convention(thin) (@in_guaranteed T, @in_guaranteed T) -> Bool + %false = apply %genericEquatableAndGenericEquatableToBool(%int1_addr, %int2_addr) : $@async @convention(thin) (@in_guaranteed T, @in_guaranteed T) -> Bool + + %print_bool = function_ref @printBool : $@convention(thin) (Bool) -> () + %print_false = apply %print_bool(%false) : $@convention(thin) (Bool) -> () // CHECK: false + + %true = apply %genericEquatableAndGenericEquatableToBool(%int1_addr, %int1_addr) : $@async @convention(thin) (@in_guaranteed T, @in_guaranteed T) -> Bool + + %print_true = apply %print_bool(%true) : $@convention(thin) (Bool) -> () // CHECK: true + + dealloc_stack %int2_addr : $*Int64 + dealloc_stack %int1_addr : $*Int64 + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} + diff --git a/test/IRGen/async/run-call-int64-and-int64-to-void.sil b/test/IRGen/async/run-call-int64-and-int64-to-void.sil new file mode 100644 index 0000000000000..a23df61b548a5 --- /dev/null +++ b/test/IRGen/async/run-call-int64-and-int64-to-void.sil @@ -0,0 +1,44 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @int64AndInt64ToVoid(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil @int64AndInt64ToVoid : $@async @convention(thin) (Int64, Int64) -> () { +entry(%int1: $Int64, %int2: $Int64): + %print = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result1 = apply %print(%int1) : $@convention(thin) (Int64) -> () // CHECK: 42 + %result2 = apply %print(%int2) : $@convention(thin) (Int64) -> () // CHECK: 13 + return %result2 : $() +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %int_literal1 = integer_literal $Builtin.Int64, 42 + %int1 = struct $Int64 (%int_literal1 : $Builtin.Int64) + %int_literal2 = integer_literal $Builtin.Int64, 13 + %int2 = struct $Int64 (%int_literal2 : $Builtin.Int64) + + %int64AndInt64ToVoid = function_ref @int64AndInt64ToVoid : $@async @convention(thin) (Int64, Int64) -> () + %result = apply %int64AndInt64ToVoid(%int1, %int2) : $@async @convention(thin) (Int64, Int64) -> () + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} + diff --git a/test/IRGen/async/run-call-int64-to-void.sil b/test/IRGen/async/run-call-int64-to-void.sil new file mode 100644 index 0000000000000..a0a7775df3a63 --- /dev/null +++ b/test/IRGen/async/run-call-int64-to-void.sil @@ -0,0 +1,39 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @int64ToVoid(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil @int64ToVoid : $@async @convention(thin) (Int64) -> () { +entry(%int: $Int64): + %print = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result = apply %print(%int) : $@convention(thin) (Int64) -> () // CHECK: 42 + return %result : $() +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %int_literal = integer_literal $Builtin.Int64, 42 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %int64ToVoid = function_ref @int64ToVoid : $@async @convention(thin) (Int64) -> () + %result = apply %int64ToVoid(%int) : $@async @convention(thin) (Int64) -> () + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} diff --git a/test/IRGen/async/run-call-structinstance-int64-to-void.sil b/test/IRGen/async/run-call-structinstance-int64-to-void.sil new file mode 100644 index 0000000000000..ae950265fd4d6 --- /dev/null +++ b/test/IRGen/async/run-call-structinstance-int64-to-void.sil @@ -0,0 +1,65 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + +struct S { + @_hasStorage var storage: Int64 { get set } + func structinstanceSInt64ToVoid(_ int: Int64) + init(storage: Int64) +} + +// CHECK-LL: define hidden swiftcc void @structinstanceSInt64ToVoid(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @structinstanceSInt64ToVoid : $@async @convention(method) (Int64, S) -> () { +bb0(%int : $Int64, %self : $S): + %takeSAndInt64 = function_ref @takeSAndInt64 : $@convention(thin) (S, Int64) -> () + %takeSAndInt64_result = apply %takeSAndInt64(%self, %int) : $@convention(thin) (S, Int64) -> () + %out = tuple () + return %out : $() +} + +sil hidden @takeSAndInt64 : $@convention(thin) (S, Int64) -> () { +bb0(%self : $S, %int : $Int64): + %s_addr = alloc_stack $S + store %self to %s_addr : $*S + %printGeneric = function_ref @printGeneric : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %printGeneric_result = apply %printGeneric(%s_addr) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () // CHECK: S(storage: 987654321) + dealloc_stack %s_addr : $*S + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_result = apply %printInt64(%int) : $@convention(thin) (Int64) -> () // CHECK: 123456789 + %out = tuple () + return %out : $() +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %s_type = metatype $@thin S.Type + %storage_literal = integer_literal $Builtin.Int64, 987654321 + %storage = struct $Int64 (%storage_literal : $Builtin.Int64) + %s = struct $S (%storage : $Int64) + %int_literal = integer_literal $Builtin.Int64, 123456789 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %structinstanceSInt64ToVoid = function_ref @structinstanceSInt64ToVoid : $@async @convention(method) (Int64, S) -> () + %result = apply %structinstanceSInt64ToVoid(%int, %s) : $@async @convention(method) (Int64, S) -> () + + %exitcode_literal = integer_literal $Builtin.Int32, 0 + %exitcode = struct $Int32 (%exitcode_literal : $Builtin.Int32) + return %exitcode : $Int32 +} + + + diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing.sil new file mode 100644 index 0000000000000..a2340dcb89146 --- /dev/null +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing.sil @@ -0,0 +1,126 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +sil @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int +sil @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional +sil @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + +struct E : Error { + @_hasStorage let code: Int64 { get } + init(code: Int64) +} + +sil hidden @E_init : $@convention(method) (Int64, @thin E.Type) -> E { +bb0(%int : $Int64, %E_type : $@thin E.Type): + %instance = struct $E (%int : $Int64) + return %instance : $E +} + +sil private [transparent] [thunk] @S_domain_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned String { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + return %2 : $String +} + + +sil private [transparent] [thunk] @S_code_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> Int { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + return %2 : $Int +} + +sil private [transparent] [thunk] @S_userinfo_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned Optional { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + return %2 : $Optional +} + +sil_witness_table hidden E: Error module main { + method #Error._domain!getter: (Self) -> () -> String : @S_domain_getter + method #Error._code!getter: (Self) -> () -> Int : @S_code_getter + method #Error._userInfo!getter: (Self) -> () -> AnyObject? : @S_userinfo_getter +} + + +sil hidden @call : $@convention(thin) () -> () { +bb0: + %voidThrowsToInt = function_ref @voidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) + try_apply %voidThrowsToInt() : $@async @convention(thin) () -> (Int, @error Error), normal bb1, error bb3 + +bb1(%result_int : $Int): + br bb2 + +bb2: + %out = tuple () + return %out : $() + +bb3(%result_error : $Error): + %error = alloc_stack $Error + strong_retain %result_error : $Error + store %result_error to %error : $*Error + %instance = alloc_stack $E + checked_cast_addr_br copy_on_success Error in %error : $*Error to E in %instance : $*E, bb4, bb5 + +bb4: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + %int_addr = struct_element_addr %instance : $*E, #E.code + %int = load %int_addr : $*Int64 + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result = apply %printInt64(%int) : $@convention(thin) (Int64) -> () + br bb2 + +bb5: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + br bb2 +} + +// CHECK-LL: define hidden swiftcc void @voidThrowsToInt(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @voidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) { +bb0: + %e_type = metatype $@thin E.Type + %int_literal = integer_literal $Builtin.Int64, 42 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %E_init = function_ref @E_init : $@convention(method) (Int64, @thin E.Type) -> E + %instance = apply %E_init(%int, %e_type) : $@convention(method) (Int64, @thin E.Type) -> E + %error = alloc_existential_box $Error, $E + %instance_addr = project_existential_box $E in %error : $Error + store %instance to %instance_addr : $*E + %result = builtin "willThrow"(%error : $Error) : $() + throw %error : $Error +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %call = function_ref @call : $@convention(thin) () -> () // CHECK: 42 + %result = apply %call() : $@convention(thin) () -> () + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 +} + + + diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil new file mode 100644 index 0000000000000..1441327807b9f --- /dev/null +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil @@ -0,0 +1,152 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +sil @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int +sil @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional +sil @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + +struct E : Error { + @_hasStorage let code: Int64 { get } + init(code: Int64) +} + +sil hidden @E_init : $@convention(method) (Int64, @thin E.Type) -> E { +bb0(%int : $Int64, %E_type : $@thin E.Type): + %instance = struct $E (%int : $Int64) + return %instance : $E +} + +sil private [transparent] [thunk] @S_domain_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned String { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + return %2 : $String +} + + +sil private [transparent] [thunk] @S_code_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> Int { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + return %2 : $Int +} + +sil private [transparent] [thunk] @S_userinfo_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned Optional { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + return %2 : $Optional +} + +sil_witness_table hidden E: Error module main { + method #Error._domain!getter: (Self) -> () -> String : @S_domain_getter + method #Error._code!getter: (Self) -> () -> Int : @S_code_getter + method #Error._userInfo!getter: (Self) -> () -> AnyObject? : @S_userinfo_getter +} + + +sil hidden @call : $@convention(thin) () -> () { +bb0: + %voidThrowsToInt = function_ref @voidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) + try_apply %voidThrowsToInt() : $@async @convention(thin) () -> (Int64, @error Error), normal bb1, error bb3 + +bb1(%result_int : $Int64): + br bb2 + +bb2: + %out = tuple () + return %out : $() + +bb3(%result_error : $Error): + %error = alloc_stack $Error + strong_retain %result_error : $Error + store %result_error to %error : $*Error + %instance = alloc_stack $E + checked_cast_addr_br copy_on_success Error in %error : $*Error to E in %instance : $*E, bb4, bb5 + +bb4: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + %int_addr = struct_element_addr %instance : $*E, #E.code + %Int64 = load %int_addr : $*Int64 + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result = apply %printInt64(%Int64) : $@convention(thin) (Int64) -> () + br bb2 + +bb5: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + br bb2 +} + +// CHECK-LL: define hidden swiftcc void @voidThrowsToInt(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @voidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) { +entry: + %asyncVoidThrowsToInt = function_ref @asyncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) + try_apply %asyncVoidThrowsToInt() : $@async @convention(thin) () -> (Int64, @error Error), normal success1, error error1 +success1(%out1 : $Int64): + %syncVoidThrowsToInt = function_ref @syncVoidThrowsToInt : $@convention(thin) () -> (Int64, @error Error) + try_apply %syncVoidThrowsToInt() : $@convention(thin) () -> (Int64, @error Error), normal success2, error error2 +success2(%out : $Int64): + return %out : $Int64 +error1(%error1 : $Error): + br error(%error1 : $Error) +error2(%error2 : $Error): + br error(%error2 : $Error) +error(%error : $Error): + throw %error : $Error +} + +sil hidden @syncVoidThrowsToInt : $@convention(thin) () -> (Int64, @error Error) { +bb0: + %e_type = metatype $@thin E.Type + %int_literal = integer_literal $Builtin.Int64, 42 + %Int64 = struct $Int64 (%int_literal : $Builtin.Int64) + %E_init = function_ref @E_init : $@convention(method) (Int64, @thin E.Type) -> E + %instance = apply %E_init(%Int64, %e_type) : $@convention(method) (Int64, @thin E.Type) -> E + %error = alloc_existential_box $Error, $E + %instance_addr = project_existential_box $E in %error : $Error + store %instance to %instance_addr : $*E + %result = builtin "willThrow"(%error : $Error) : $() + throw %error : $Error +} + +sil hidden @asyncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) { +bb0: + %int_literal = integer_literal $Builtin.Int64, 1 // user: %2 + %Int64 = struct $Int64 (%int_literal : $Builtin.Int64) // user: %3 + return %Int64 : $Int64 +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %call = function_ref @call : $@convention(thin) () -> () // CHECK: 42 + %result = apply %call() : $@convention(thin) () -> () + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 +} + + + + + + diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil new file mode 100644 index 0000000000000..14a92c6272992 --- /dev/null +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil @@ -0,0 +1,138 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +sil @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int +sil @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional +sil @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + +struct E : Error { + @_hasStorage let code: Int64 { get } + init(code: Int64) +} + +sil hidden @E_init : $@convention(method) (Int64, @thin E.Type) -> E { +bb0(%int : $Int64, %E_type : $@thin E.Type): + %instance = struct $E (%int : $Int64) + return %instance : $E +} + +sil private [transparent] [thunk] @S_domain_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned String { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + return %2 : $String +} + + +sil private [transparent] [thunk] @S_code_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> Int { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + return %2 : $Int +} + +sil private [transparent] [thunk] @S_userinfo_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned Optional { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + return %2 : $Optional +} + +sil_witness_table hidden E: Error module main { + method #Error._domain!getter: (Self) -> () -> String : @S_domain_getter + method #Error._code!getter: (Self) -> () -> Int : @S_code_getter + method #Error._userInfo!getter: (Self) -> () -> AnyObject? : @S_userinfo_getter +} + + +sil hidden @call : $@convention(thin) () -> () { +bb0: + %voidThrowsToInt = function_ref @voidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) + try_apply %voidThrowsToInt() : $@async @convention(thin) () -> (Int, @error Error), normal bb1, error bb3 + +bb1(%result_int : $Int): + br bb2 + +bb2: + %out = tuple () + return %out : $() + +bb3(%result_error : $Error): + %error = alloc_stack $Error + strong_retain %result_error : $Error + store %result_error to %error : $*Error + %instance = alloc_stack $E + checked_cast_addr_br copy_on_success Error in %error : $*Error to E in %instance : $*E, bb4, bb5 + +bb4: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + %int_addr = struct_element_addr %instance : $*E, #E.code + %int = load %int_addr : $*Int64 + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result = apply %printInt64(%int) : $@convention(thin) (Int64) -> () + br bb2 + +bb5: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + br bb2 +} + +// CHECK-LL: define hidden swiftcc void @voidThrowsToInt(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @voidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) { +bb0: + %asyncVoidThrowsToInt = function_ref @asyncVoidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) + try_apply %asyncVoidThrowsToInt() : $@async @convention(thin) () -> (Int, @error Error), normal bb1, error bb2 +bb1(%out : $Int): + return %out : $Int +bb2(%error : $Error): + throw %error : $Error +} + +sil hidden @asyncVoidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) { +bb0: + %e_type = metatype $@thin E.Type + %int_literal = integer_literal $Builtin.Int64, 42 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %E_init = function_ref @E_init : $@convention(method) (Int64, @thin E.Type) -> E + %instance = apply %E_init(%int, %e_type) : $@convention(method) (Int64, @thin E.Type) -> E + %error = alloc_existential_box $Error, $E + %instance_addr = project_existential_box $E in %error : $Error + store %instance to %instance_addr : $*E + %result = builtin "willThrow"(%error : $Error) : $() + throw %error : $Error +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %call = function_ref @call : $@convention(thin) () -> () // CHECK: 42 + %result = apply %call() : $@convention(thin) () -> () + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 +} + + + + + diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil new file mode 100644 index 0000000000000..49daf094c4e4b --- /dev/null +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil @@ -0,0 +1,153 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +sil @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int +sil @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional +sil @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + +struct E : Error { + @_hasStorage let code: Int64 { get } + init(code: Int64) +} + +sil hidden @E_init : $@convention(method) (Int64, @thin E.Type) -> E { +bb0(%int : $Int64, %E_type : $@thin E.Type): + %instance = struct $E (%int : $Int64) + return %instance : $E +} + +sil private [transparent] [thunk] @S_domain_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned String { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + return %2 : $String +} + + +sil private [transparent] [thunk] @S_code_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> Int { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + return %2 : $Int +} + +sil private [transparent] [thunk] @S_userinfo_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned Optional { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + return %2 : $Optional +} + +sil_witness_table hidden E: Error module main { + method #Error._domain!getter: (Self) -> () -> String : @S_domain_getter + method #Error._code!getter: (Self) -> () -> Int : @S_code_getter + method #Error._userInfo!getter: (Self) -> () -> AnyObject? : @S_userinfo_getter +} + + +sil hidden @call : $@convention(thin) () -> () { +bb0: + %voidThrowsToInt = function_ref @voidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) + try_apply %voidThrowsToInt() : $@async @convention(thin) () -> (Int64, @error Error), normal bb1, error bb3 + +bb1(%result_int : $Int64): + br bb2 + +bb2: + %out = tuple () + return %out : $() + +bb3(%result_error : $Error): + %error = alloc_stack $Error + strong_retain %result_error : $Error + store %result_error to %error : $*Error + %instance = alloc_stack $E + checked_cast_addr_br copy_on_success Error in %error : $*Error to E in %instance : $*E, bb4, bb5 + +bb4: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + %int_addr = struct_element_addr %instance : $*E, #E.code + %Int64 = load %int_addr : $*Int64 + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result = apply %printInt64(%Int64) : $@convention(thin) (Int64) -> () + br bb2 + +bb5: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + br bb2 +} + +// CHECK-LL: define hidden swiftcc void @voidThrowsToInt(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @voidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) { +entry: + %syncVoidThrowsToInt = function_ref @syncVoidThrowsToInt : $@convention(thin) () -> (Int64, @error Error) + try_apply %syncVoidThrowsToInt() : $@convention(thin) () -> (Int64, @error Error), normal success1, error error1 +success1(%out1 : $Int64): + %asyncVoidThrowsToInt = function_ref @asyncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) + try_apply %asyncVoidThrowsToInt() : $@async @convention(thin) () -> (Int64, @error Error), normal success2, error error2 +success2(%out : $Int64): + return %out : $Int64 +error1(%error1 : $Error): + br error(%error1 : $Error) +error2(%error2 : $Error): + br error(%error2 : $Error) +error(%error : $Error): + throw %error : $Error +} + +sil hidden @syncVoidThrowsToInt : $@convention(thin) () -> (Int64, @error Error) { +bb0: + %int_literal = integer_literal $Builtin.Int64, 1 // user: %2 + %Int64 = struct $Int64 (%int_literal : $Builtin.Int64) // user: %3 + return %Int64 : $Int64 +} + +sil hidden @asyncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) { +bb0: + %e_type = metatype $@thin E.Type + %int_literal = integer_literal $Builtin.Int64, 42 + %Int64 = struct $Int64 (%int_literal : $Builtin.Int64) + %E_init = function_ref @E_init : $@convention(method) (Int64, @thin E.Type) -> E + %instance = apply %E_init(%Int64, %e_type) : $@convention(method) (Int64, @thin E.Type) -> E + %error = alloc_existential_box $Error, $E + %instance_addr = project_existential_box $E in %error : $Error + store %instance to %instance_addr : $*E + %result = builtin "willThrow"(%error : $Error) : $() + throw %error : $Error +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %call = function_ref @call : $@convention(thin) () -> () // CHECK: 42 + %result = apply %call() : $@convention(thin) () -> () + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 +} + + + + + + diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil new file mode 100644 index 0000000000000..95c083a9558a9 --- /dev/null +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil @@ -0,0 +1,137 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +sil @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int +sil @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional +sil @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + +struct E : Error { + @_hasStorage let code: Int64 { get } + init(code: Int64) +} + +sil hidden @E_init : $@convention(method) (Int64, @thin E.Type) -> E { +bb0(%int : $Int64, %E_type : $@thin E.Type): + %instance = struct $E (%int : $Int64) + return %instance : $E +} + +sil private [transparent] [thunk] @S_domain_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned String { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE7_domainSSvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned String + return %2 : $String +} + + +sil private [transparent] [thunk] @S_code_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> Int { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE5_codeSivg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> Int + return %2 : $Int +} + +sil private [transparent] [thunk] @S_userinfo_getter : $@convention(witness_method: Error) (@in_guaranteed E) -> @owned Optional { +bb0(%0 : $*E): + %1 = function_ref @$ss5ErrorPsE9_userInfoyXlSgvg : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + %2 = apply %1(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> @owned Optional + return %2 : $Optional +} + +sil_witness_table hidden E: Error module main { + method #Error._domain!getter: (Self) -> () -> String : @S_domain_getter + method #Error._code!getter: (Self) -> () -> Int : @S_code_getter + method #Error._userInfo!getter: (Self) -> () -> AnyObject? : @S_userinfo_getter +} + + +sil hidden @call : $@convention(thin) () -> () { +bb0: + %voidThrowsToInt = function_ref @voidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) + try_apply %voidThrowsToInt() : $@async @convention(thin) () -> (Int, @error Error), normal bb1, error bb3 + +bb1(%result_int : $Int): + br bb2 + +bb2: + %out = tuple () + return %out : $() + +bb3(%result_error : $Error): + %error = alloc_stack $Error + strong_retain %result_error : $Error + store %result_error to %error : $*Error + %instance = alloc_stack $E + checked_cast_addr_br copy_on_success Error in %error : $*Error to E in %instance : $*E, bb4, bb5 + +bb4: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + %int_addr = struct_element_addr %instance : $*E, #E.code + %int = load %int_addr : $*Int64 + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result = apply %printInt64(%int) : $@convention(thin) (Int64) -> () + br bb2 + +bb5: + dealloc_stack %instance : $*E + destroy_addr %error : $*Error + dealloc_stack %error : $*Error + br bb2 +} + +// CHECK-LL: define hidden swiftcc void @voidThrowsToInt(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @voidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) { +bb0: + %syncVoidThrowsToInt = function_ref @syncVoidThrowsToInt : $@convention(thin) () -> (Int, @error Error) + try_apply %syncVoidThrowsToInt() : $@convention(thin) () -> (Int, @error Error), normal bb1, error bb2 +bb1(%out : $Int): + return %out : $Int +bb2(%error : $Error): + throw %error : $Error +} + +sil hidden @syncVoidThrowsToInt : $@convention(thin) () -> (Int, @error Error) { +bb0: + %e_type = metatype $@thin E.Type + %int_literal = integer_literal $Builtin.Int64, 42 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %E_init = function_ref @E_init : $@convention(method) (Int64, @thin E.Type) -> E + %instance = apply %E_init(%int, %e_type) : $@convention(method) (Int64, @thin E.Type) -> E + %error = alloc_existential_box $Error, $E + %instance_addr = project_existential_box $E in %error : $Error + store %instance to %instance_addr : $*E + %result = builtin "willThrow"(%error : $Error) : $() + throw %error : $Error +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %call = function_ref @call : $@convention(thin) () -> () // CHECK: 42 + %result = apply %call() : $@convention(thin) () -> () + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 +} + + + + diff --git a/test/IRGen/async/run-call-void-to-existential.sil b/test/IRGen/async/run-call-void-to-existential.sil new file mode 100644 index 0000000000000..fc58a6c644689 --- /dev/null +++ b/test/IRGen/async/run-call-void-to-existential.sil @@ -0,0 +1,83 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printAny : $@convention(thin) (@in_guaranteed Any) -> () + +public protocol P { +} + +public struct S : P { + @_hasStorage let int: Int64 { get } + init(int: Int64) +} + +sil_witness_table [serialized] S: P module main { +} + +sil hidden @S_init : $@convention(method) (Int64, @thin S.Type) -> S { +bb0(%int : $Int64, %S_type : $@thin S.Type): + %instance = struct $S (%int : $Int64) + return %instance : $S +} + +// CHECK-LL: define hidden swiftcc void @voidToExistential(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @voidToExistential : $@async @convention(thin) () -> @out P { +bb0(%out : $*P): + %S_type = metatype $@thin S.Type + %int_literal = integer_literal $Builtin.Int64, 42 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %S_init = function_ref @S_init : $@convention(method) (Int64, @thin S.Type) -> S + %instance = apply %S_init(%int, %S_type) : $@convention(method) (Int64, @thin S.Type) -> S + %existential = alloc_stack $P + %existential_addr = init_existential_addr %existential : $*P, $S + store %instance to %existential_addr : $*S + copy_addr %existential to [initialization] %out : $*P + destroy_addr %existential : $*P + dealloc_stack %existential : $*P + %result = tuple () + return %result : $() +} + +sil hidden @call : $@convention(thin) () -> () { +bb0: + %existential = alloc_stack $P + %voidToExistential = function_ref @voidToExistential : $@async @convention(thin) () -> @out P + %voidToExistential_result = apply %voidToExistential(%existential) : $@async @convention(thin) () -> @out P + %existential_addr = open_existential_addr immutable_access %existential : $*P to $*@opened("1819CC6E-FEC6-11EA-876D-D0817AD71B77") P + %any = alloc_stack $Any + %any_addr = init_existential_addr %any : $*Any, $@opened("1819CC6E-FEC6-11EA-876D-D0817AD71B77") P + copy_addr %existential_addr to [initialization] %any_addr : $*@opened("1819CC6E-FEC6-11EA-876D-D0817AD71B77") P + %printAny = function_ref @printAny : $@convention(thin) (@in_guaranteed Any) -> () + %result = apply %printAny(%any) : $@convention(thin) (@in_guaranteed Any) -> () + destroy_addr %any : $*Any + dealloc_stack %any : $*Any + destroy_addr %existential : $*P + dealloc_stack %existential : $*P + return %result : $() +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %call = function_ref @call : $@convention(thin) () -> () + %result = apply %call() : $@convention(thin) () -> () // CHECK: S(int: 42) + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 +} + + diff --git a/test/IRGen/async/run-call-void-to-int64-and-int64.sil b/test/IRGen/async/run-call-void-to-int64-and-int64.sil new file mode 100644 index 0000000000000..ebfd51c0b067c --- /dev/null +++ b/test/IRGen/async/run-call-void-to-int64-and-int64.sil @@ -0,0 +1,47 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @voidToInt64AndInt64(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil @voidToInt64AndInt64 : $@async @convention(thin) () -> (Int64, Int64) { + %int_literal1 = integer_literal $Builtin.Int64, 42 + %int1 = struct $Int64 (%int_literal1 : $Builtin.Int64) + %int_literal2 = integer_literal $Builtin.Int64, 13 + %int2 = struct $Int64 (%int_literal2 : $Builtin.Int64) + %tuple = tuple (%int1 : $Int64, %int2 : $Int64) + return %tuple : $(Int64, Int64) +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %voidToInt64AndInt64 = function_ref @voidToInt64AndInt64 : $@async @convention(thin) () -> (Int64, Int64) + %tuple = apply %voidToInt64AndInt64() : $@async @convention(thin) () -> (Int64, Int64) + %int1 = tuple_extract %tuple : $(Int64, Int64), 0 + %int2 = tuple_extract %tuple : $(Int64, Int64), 1 + + %print = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result1 = apply %print(%int1) : $@convention(thin) (Int64) -> () // CHECK: 42 + %result2 = apply %print(%int2) : $@convention(thin) (Int64) -> () // CHECK: 13 + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} + + diff --git a/test/IRGen/async/run-call-void-to-int64.sil b/test/IRGen/async/run-call-void-to-int64.sil new file mode 100644 index 0000000000000..168bca6875fc1 --- /dev/null +++ b/test/IRGen/async/run-call-void-to-int64.sil @@ -0,0 +1,40 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + + +import Builtin +import Swift +import PrintShims + +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @voidToInt64(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil @voidToInt64 : $@async @convention(thin) () -> (Int64) { + %int_literal = integer_literal $Builtin.Int64, 42 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + return %int : $Int64 +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + + %voidToInt64 = function_ref @voidToInt64 : $@async @convention(thin) () -> Int64 + %int = apply %voidToInt64() : $@async @convention(thin) () -> Int64 + + %print = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result = apply %print(%int) : $@convention(thin) (Int64) -> () // CHECK: 42 + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} + diff --git a/test/IRGen/async/run-call-void-to-struct_large.sil b/test/IRGen/async/run-call-void-to-struct_large.sil new file mode 100644 index 0000000000000..abb78dacaf223 --- /dev/null +++ b/test/IRGen/async/run-call-void-to-struct_large.sil @@ -0,0 +1,132 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + +struct Big { + @_hasStorage let i1: Int64 { get } + @_hasStorage let i2: Int64 { get } + @_hasStorage let i3: Int64 { get } + @_hasStorage let i4: Int64 { get } + @_hasStorage let i5: Int64 { get } + @_hasStorage let i6: Int64 { get } + @_hasStorage let i7: Int64 { get } + @_hasStorage let i8: Int64 { get } + @_hasStorage let i9: Int64 { get } + @_hasStorage let i0: Int64 { get } + init() +} + +sil hidden @Big_init : $@convention(method) (@thin Big.Type) -> Big { +// %0 "$metatype" +bb0(%0 : $@thin Big.Type): + %1 = alloc_stack $Big, var, name "self" + %2 = integer_literal $Builtin.Int64, 1 + %3 = struct $Int64 (%2 : $Builtin.Int64) + %4 = begin_access [modify] [static] %1 : $*Big + %5 = struct_element_addr %4 : $*Big, #Big.i1 + store %3 to %5 : $*Int64 + end_access %4 : $*Big + %8 = integer_literal $Builtin.Int64, 2 + %9 = struct $Int64 (%8 : $Builtin.Int64) + %10 = begin_access [modify] [static] %1 : $*Big + %11 = struct_element_addr %10 : $*Big, #Big.i2 + store %9 to %11 : $*Int64 + end_access %10 : $*Big + %14 = integer_literal $Builtin.Int64, 3 + %15 = struct $Int64 (%14 : $Builtin.Int64) + %16 = begin_access [modify] [static] %1 : $*Big + %17 = struct_element_addr %16 : $*Big, #Big.i3 + store %15 to %17 : $*Int64 + end_access %16 : $*Big + %20 = integer_literal $Builtin.Int64, 4 + %21 = struct $Int64 (%20 : $Builtin.Int64) + %22 = begin_access [modify] [static] %1 : $*Big + %23 = struct_element_addr %22 : $*Big, #Big.i4 + store %21 to %23 : $*Int64 + end_access %22 : $*Big + %26 = integer_literal $Builtin.Int64, 5 + %27 = struct $Int64 (%26 : $Builtin.Int64) + %28 = begin_access [modify] [static] %1 : $*Big + %29 = struct_element_addr %28 : $*Big, #Big.i5 + store %27 to %29 : $*Int64 + end_access %28 : $*Big + %32 = integer_literal $Builtin.Int64, 6 + %33 = struct $Int64 (%32 : $Builtin.Int64) + %34 = begin_access [modify] [static] %1 : $*Big + %35 = struct_element_addr %34 : $*Big, #Big.i6 + store %33 to %35 : $*Int64 + end_access %34 : $*Big + %38 = integer_literal $Builtin.Int64, 7 + %39 = struct $Int64 (%38 : $Builtin.Int64) + %40 = begin_access [modify] [static] %1 : $*Big + %41 = struct_element_addr %40 : $*Big, #Big.i7 + store %39 to %41 : $*Int64 + end_access %40 : $*Big + %44 = integer_literal $Builtin.Int64, 8 + %45 = struct $Int64 (%44 : $Builtin.Int64) + %46 = begin_access [modify] [static] %1 : $*Big + %47 = struct_element_addr %46 : $*Big, #Big.i8 + store %45 to %47 : $*Int64 + end_access %46 : $*Big + %50 = integer_literal $Builtin.Int64, 9 + %51 = struct $Int64 (%50 : $Builtin.Int64) + %52 = begin_access [modify] [static] %1 : $*Big + %53 = struct_element_addr %52 : $*Big, #Big.i9 + store %51 to %53 : $*Int64 + end_access %52 : $*Big + %56 = integer_literal $Builtin.Int64, 0 + %57 = struct $Int64 (%56 : $Builtin.Int64) + %58 = begin_access [modify] [static] %1 : $*Big + %59 = struct_element_addr %58 : $*Big, #Big.i0 + store %57 to %59 : $*Int64 + end_access %58 : $*Big + %62 = struct $Big (%3 : $Int64, %9 : $Int64, %15 : $Int64, %21 : $Int64, %27 : $Int64, %33 : $Int64, %39 : $Int64, %45 : $Int64, %51 : $Int64, %57 : $Int64) + dealloc_stack %1 : $*Big + return %62 : $Big +} + +// CHECK-LL: define hidden swiftcc void @getBig(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @getBig : $@async @convention(thin) () -> Big { +bb0: + %0 = metatype $@thin Big.Type + // function_ref Big.init() + %1 = function_ref @Big_init : $@convention(method) (@thin Big.Type) -> Big + %2 = apply %1(%0) : $@convention(method) (@thin Big.Type) -> Big + return %2 : $Big +} + +sil hidden @printBig : $@convention(thin) () -> () { + %getBig = function_ref @getBig : $@async @convention(thin) () -> Big + %big = apply %getBig() : $@async @convention(thin) () -> Big + %big_addr = alloc_stack $Big + store %big to %big_addr : $*Big + %print = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %result = apply %print(%big_addr) : $@convention(thin) (@in_guaranteed T) -> () + dealloc_stack %big_addr : $*Big + %out = tuple () + return %out : $() +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %printBig = function_ref @printBig : $@convention(thin) () -> () + %result = apply %printBig() : $@convention(thin) () -> () // CHECK: Big(i1: 1, i2: 2, i3: 3, i4: 4, i5: 5, i6: 6, i7: 7, i8: 8, i9: 9, i0: 0) + + %2 = integer_literal $Builtin.Int32, 0 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 // id: %4 +} diff --git a/test/Inputs/print-shims.swift b/test/Inputs/print-shims.swift new file mode 100644 index 0000000000000..eebfb80c2c7cb --- /dev/null +++ b/test/Inputs/print-shims.swift @@ -0,0 +1,19 @@ +@_silgen_name("printInt64") +public func printInt64(_ int: Int64) { + print(int) +} + +@_silgen_name("printGeneric") +public func printGeneric(_ t: T) { + print(t) +} + +@_silgen_name("printBool") +public func printBool(_ bool: Bool) { + print(bool) +} + +@_silgen_name("printAny") +public func printAny(_ any: Any) { + print(any) +} From 147f9e6c90f66068f567b5b7a14e7eb7abdafa29 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 5 Oct 2020 21:29:57 -0700 Subject: [PATCH 220/745] Generalize test --- test/Misc/stats_dir_tracer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Misc/stats_dir_tracer.swift b/test/Misc/stats_dir_tracer.swift index 98ee6efcf7166..6f91c24f864fe 100644 --- a/test/Misc/stats_dir_tracer.swift +++ b/test/Misc/stats_dir_tracer.swift @@ -2,7 +2,7 @@ // RUN: %target-swiftc_driver -o %t/main -module-name main -stats-output-dir %t %s -trace-stats-events // RUN: %FileCheck -input-file %t/*.csv %s -// CHECK-DAG: {{[0-9]+,[0-9]+,"exit","check-conformance","Sema.NumConstraintScopes",[0-9]+,[0-9]+,"Bar: Proto module main",".*stats_dir_tracer.swift.*"}} +// CHECK-DAG: {{[0-9]+,[0-9]+,"exit","check-conformance","Frontend.NumInstructionsExecuted",[0-9]+,[0-9]+,"Bar: Proto module main",".*stats_dir_tracer.swift.*"}} // CHECK-DAG: {{[0-9]+,[0-9]+,"exit","SuperclassDeclRequest","Sema.SuperclassDeclRequest",[0-9]+,[0-9]+,"Bar","\[.*stats_dir_tracer.swift.*\]"}} public func foo() { From f26bce350d41ec1ffefa8cae61f759985244b055 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 5 Oct 2020 22:26:28 -0700 Subject: [PATCH 221/745] [Sema] NFC: Move `ContextualTypePurpose` to constraint system header --- lib/Sema/ConstraintSystem.h | 40 +++++++++++++++++++++++++++++++++++ lib/Sema/TypeChecker.h | 42 ------------------------------------- 2 files changed, 40 insertions(+), 42 deletions(-) diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index bcbc630b4d3ea..de3fce31d833a 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -66,6 +66,46 @@ void *operator new(size_t bytes, swift::constraints::ConstraintSystem& cs, namespace swift { +/// This specifies the purpose of the contextual type, when specified to +/// typeCheckExpression. This is used for diagnostic generation to produce more +/// specified error messages when the conversion fails. +/// +enum ContextualTypePurpose { + CTP_Unused, ///< No contextual type is specified. + CTP_Initialization, ///< Pattern binding initialization. + CTP_ReturnStmt, ///< Value specified to a 'return' statement. + CTP_ReturnSingleExpr, ///< Value implicitly returned from a function. + CTP_YieldByValue, ///< By-value yield operand. + CTP_YieldByReference, ///< By-reference yield operand. + CTP_ThrowStmt, ///< Value specified to a 'throw' statement. + CTP_EnumCaseRawValue, ///< Raw value specified for "case X = 42" in enum. + CTP_DefaultParameter, ///< Default value in parameter 'foo(a : Int = 42)'. + + /// Default value in @autoclosure parameter + /// 'foo(a : @autoclosure () -> Int = 42)'. + CTP_AutoclosureDefaultParameter, + + CTP_CalleeResult, ///< Constraint is placed on the result of a callee. + CTP_CallArgument, ///< Call to function or operator requires type. + CTP_ClosureResult, ///< Closure result expects a specific type. + CTP_ArrayElement, ///< ArrayExpr wants elements to have a specific type. + CTP_DictionaryKey, ///< DictionaryExpr keys should have a specific type. + CTP_DictionaryValue, ///< DictionaryExpr values should have a specific type. + CTP_CoerceOperand, ///< CoerceExpr operand coerced to specific type. + CTP_AssignSource, ///< AssignExpr source operand coerced to result type. + CTP_SubscriptAssignSource, ///< AssignExpr source operand coerced to subscript + ///< result type. + CTP_Condition, ///< Condition expression of various statements e.g. + ///< `if`, `for`, `while` etc. + CTP_ForEachStmt, ///< "expression/sequence" associated with 'for-in' loop + ///< is expected to conform to 'Sequence' protocol. + CTP_WrappedProperty, ///< Property type expected to match 'wrappedValue' type + CTP_ComposedPropertyWrapper, ///< Composed wrapper type expected to match + ///< former 'wrappedValue' type + + CTP_CannotFail, ///< Conversion can never fail. abort() if it does. +}; + namespace constraints { /// Describes the algorithm to use for trailing closure matching. diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 6d21f393431d5..c6124b4912c70 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -112,48 +112,6 @@ class LookupTypeResult { } }; -/// This specifies the purpose of the contextual type, when specified to -/// typeCheckExpression. This is used for diagnostic generation to produce more -/// specified error messages when the conversion fails. -/// -enum ContextualTypePurpose { - CTP_Unused, ///< No contextual type is specified. - CTP_Initialization, ///< Pattern binding initialization. - CTP_ReturnStmt, ///< Value specified to a 'return' statement. - CTP_ReturnSingleExpr, ///< Value implicitly returned from a function. - CTP_YieldByValue, ///< By-value yield operand. - CTP_YieldByReference, ///< By-reference yield operand. - CTP_ThrowStmt, ///< Value specified to a 'throw' statement. - CTP_EnumCaseRawValue, ///< Raw value specified for "case X = 42" in enum. - CTP_DefaultParameter, ///< Default value in parameter 'foo(a : Int = 42)'. - - /// Default value in @autoclosure parameter - /// 'foo(a : @autoclosure () -> Int = 42)'. - CTP_AutoclosureDefaultParameter, - - CTP_CalleeResult, ///< Constraint is placed on the result of a callee. - CTP_CallArgument, ///< Call to function or operator requires type. - CTP_ClosureResult, ///< Closure result expects a specific type. - CTP_ArrayElement, ///< ArrayExpr wants elements to have a specific type. - CTP_DictionaryKey, ///< DictionaryExpr keys should have a specific type. - CTP_DictionaryValue, ///< DictionaryExpr values should have a specific type. - CTP_CoerceOperand, ///< CoerceExpr operand coerced to specific type. - CTP_AssignSource, ///< AssignExpr source operand coerced to result type. - CTP_SubscriptAssignSource, ///< AssignExpr source operand coerced to subscript - ///< result type. - CTP_Condition, ///< Condition expression of various statements e.g. - ///< `if`, `for`, `while` etc. - CTP_ForEachStmt, ///< "expression/sequence" associated with 'for-in' loop - ///< is expected to conform to 'Sequence' protocol. - CTP_WrappedProperty, ///< Property type expected to match 'wrappedValue' type - CTP_ComposedPropertyWrapper, ///< Composed wrapper type expected to match - ///< former 'wrappedValue' type - - CTP_CannotFail, ///< Conversion can never fail. abort() if it does. -}; - - - /// Flags that can be used to control type checking. enum class TypeCheckExprFlags { /// Whether we know that the result of the expression is discarded. This From 588d42c56440db5e6166c1e979c13c8ae3343a60 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 5 Oct 2020 23:45:02 -0700 Subject: [PATCH 222/745] [Sema] NFC: Move `FreeTypeVariableBinding` to constraint system header --- lib/Sema/ConstraintSystem.h | 11 +++++++++++ lib/Sema/TypeChecker.h | 11 ----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index de3fce31d833a..d9662107d1d48 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -106,6 +106,17 @@ enum ContextualTypePurpose { CTP_CannotFail, ///< Conversion can never fail. abort() if it does. }; +/// Specify how we handle the binding of underconstrained (free) type variables +/// within a solution to a constraint system. +enum class FreeTypeVariableBinding { + /// Disallow any binding of such free type variables. + Disallow, + /// Allow the free type variables to persist in the solution. + Allow, + /// Bind the type variables to UnresolvedType to represent the ambiguity. + UnresolvedType +}; + namespace constraints { /// Describes the algorithm to use for trailing closure matching. diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index c6124b4912c70..63a336eb239b8 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -181,17 +181,6 @@ enum class Comparison { Worse }; -/// Specify how we handle the binding of underconstrained (free) type variables -/// within a solution to a constraint system. -enum class FreeTypeVariableBinding { - /// Disallow any binding of such free type variables. - Disallow, - /// Allow the free type variables to persist in the solution. - Allow, - /// Bind the type variables to UnresolvedType to represent the ambiguity. - UnresolvedType -}; - /// A conditional conformance that implied some other requirements. That is, \c /// ConformingType conforming to \c Protocol may have required additional /// requirements to be satisfied. From 141b032e7c6ad24133905b35a6080f872feb7dac Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Tue, 6 Oct 2020 10:59:45 -0700 Subject: [PATCH 223/745] Enable --build-sil-debugging-stdlib for all of swift/stdlib/public (#34197) Previously it was enable only for swift/stdlib/public/core --- stdlib/public/CMakeLists.txt | 6 +++++- stdlib/public/core/CMakeLists.txt | 4 ---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stdlib/public/CMakeLists.txt b/stdlib/public/CMakeLists.txt index 978322b95a1b0..1f4d27c04d9ad 100644 --- a/stdlib/public/CMakeLists.txt +++ b/stdlib/public/CMakeLists.txt @@ -15,7 +15,11 @@ if(SWIFT_RUNTIME_USE_SANITIZERS) endif() endif() -list(APPEND SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS "-Xfrontend" "-verify-syntax-tree") +list(APPEND SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS "-Xfrontend" "-verify-syntax-tree") + +if(SWIFT_STDLIB_SIL_DEBUGGING) + list(APPEND SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS "-Xfrontend" "-gsil") +endif() # Build the runtime with -Wall to catch, e.g., uninitialized variables # warnings. diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index ad74e0e699039..4002836ff5cb1 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -284,10 +284,6 @@ option(SWIFT_CHECK_ESSENTIAL_STDLIB "Check core standard library layering by linking its essential subset" FALSE) -if(SWIFT_STDLIB_SIL_DEBUGGING) - list(APPEND swift_stdlib_compile_flags "-Xfrontend" "-gsil") -endif() - if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel") list(APPEND swift_stdlib_compile_flags "-Xllvm" "-sil-inline-generics") list(APPEND swift_stdlib_compile_flags "-Xllvm" "-sil-partial-specialization") From c6fc53e844ac4fd542adb0669ac7038c2919d283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Tue, 28 Jul 2020 10:58:12 -0700 Subject: [PATCH 224/745] [Sema] Define availability via compiler flag Introduce availability macros defined by a frontend flag. This feature makes it possible to set the availability versions at the moment of compilation instead of having it hard coded in the sources. It can be used by projects with a need to change the availability depending on the compilation context while using the same sources. The availability macro is defined with the `-define-availability` flag: swift MyLib.swift -define-availability "_iOS8Aligned:macOS 10.10, iOS 8.0" .. The macro can be used in code instead of a platform name and version: @available(_iOS8Aligned, *) public func foo() {} rdar://problem/65612624 --- include/swift/AST/DiagnosticsParse.def | 16 +++ include/swift/Basic/LangOptions.h | 3 + include/swift/Option/Options.td | 5 + include/swift/Parse/Parser.h | 49 +++++++- lib/Frontend/CompilerInvocation.cpp | 4 + lib/Parse/ParseDecl.cpp | 112 +++++++++++++++++- lib/Parse/ParseStmt.cpp | 86 ++++++++++++-- .../availability-expansion.swift | 29 +++++ test/Sema/availability_define.swift | 60 ++++++++++ test/Sema/availability_define_parsing.swift | 31 +++++ 10 files changed, 379 insertions(+), 16 deletions(-) create mode 100644 test/ModuleInterface/availability-expansion.swift create mode 100644 test/Sema/availability_define.swift create mode 100644 test/Sema/availability_define_parsing.swift diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index a9da96cd96304..e1e1de631d027 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1431,6 +1431,22 @@ WARNING(attr_availability_nonspecific_platform_unexpected_version,none, "unexpected version number in '%0' attribute for non-specific platform " "'*'", (StringRef)) +// availability macro +ERROR(attr_availability_wildcard_in_macro, none, + "future platforms identified by '*' cannot be used in " + "an availability macro definition", ()) +ERROR(attr_availability_missing_macro_name,none, + "expected an identifier to begin an availability macro definition", ()) +ERROR(attr_availability_expected_colon_macro,none, + "expected ':' after '%0' in availability macro definition", + (StringRef)) +ERROR(attr_availability_unknown_version,none, + "reference to undefined version '%0' for availability macro '%1'", + (StringRef, StringRef)) +ERROR(attr_availability_duplicate,none, + "duplicate definition of availability macro '%0' for version '%1'", + (StringRef, StringRef)) + // originallyDefinedIn ERROR(originally_defined_in_missing_rparen,none, "expected ')' in @_originallyDefinedIn argument list", ()) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 40d8c719a02d1..f8a1c8f362cc0 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -111,6 +111,9 @@ namespace swift { /// when using RequireExplicitAvailability. std::string RequireExplicitAvailabilityTarget; + // Availability macros definitions to be expanded at parsing. + SmallVector AvailabilityMacros; + /// If false, '#file' evaluates to the full path rather than a /// human-readable string. bool EnableConcisePoundFile = false; diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index a1c2b34fc1f59..e6f9ab61edce6 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -414,6 +414,11 @@ def require_explicit_availability_target : Separate<["-"], "require-explicit-ava HelpText<"Suggest fix-its adding @available(, *) to public declarations without availability">, MetaVarName<"">; +def define_availability : Separate<["-"], "define-availability">, + Flags<[FrontendOption, NoInteractiveOption]>, + HelpText<"Define an availability macro in the format 'macroName : iOS 13.0, macOS 10.15'">, + MetaVarName<"">; + def module_name : Separate<["-"], "module-name">, Flags<[FrontendOption, ModuleInterfaceOption]>, HelpText<"Name of the module to build">; diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 3cae352ef73dd..20c4cf720dff8 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -397,6 +397,22 @@ class Parser { /// Current syntax parsing context where call backs should be directed to. SyntaxParsingContext *SyntaxContext; + /// Maps of macro name and version to availability specifications. + typedef llvm::DenseMap> + AvailabilityMacroVersionMap; + typedef llvm::DenseMap + AvailabilityMacroMap; + + /// Cache of the availability macros parsed from the command line arguments. + /// Organized as two nested \c DenseMap keyed first on the macro name then + /// the macro version. This structure allows to peek at macro names before + /// parsing a version tuple. + AvailabilityMacroMap AvailabilityMacros; + + /// Has \c AvailabilityMacros been computed? + bool AvailabilityMacrosComputed = false; + public: Parser(unsigned BufferID, SourceFile &SF, DiagnosticEngine* LexerDiags, SILParserStateBase *SIL, PersistentParserState *PersistentState, @@ -1682,9 +1698,38 @@ class Parser { //===--------------------------------------------------------------------===// // Availability Specification Parsing - /// Parse a comma-separated list of availability specifications. + /// Parse a comma-separated list of availability specifications. Try to + /// expand availability macros when /p ParsingMacroDefinition is false. + ParserStatus + parseAvailabilitySpecList(SmallVectorImpl &Specs, + bool ParsingMacroDefinition = false); + + /// Does the current matches an argument macro name? Parsing compiler + /// arguments as required without consuming tokens from the source file + /// parser. + bool peekAvailabilityMacroName(); + + /// Try to parse a reference to an availability macro and append its result + /// to \p Specs. If the current token doesn't match a macro name, return + /// a success without appending anything to \c Specs. + ParserStatus + parseAvailabilityMacro(SmallVectorImpl &Specs); + + /// Parse the availability macros definitions passed as arguments. + void parseAllAvailabilityMacroArguments(); + + /// Result of parsing an availability macro definition. + struct AvailabilityMacroDefinition { + StringRef Name; + llvm::VersionTuple Version; + SmallVector Specs; + }; + + /// Parse an availability macro definition from a command line argument. + /// This function should be called on a Parser set up on the command line + /// argument code. ParserStatus - parseAvailabilitySpecList(SmallVectorImpl &Specs); + parseAvailabilityMacroDefinition(AvailabilityMacroDefinition &Result); ParserResult parseAvailabilitySpec(); ParserResult diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index c4f1c698ac7bd..6c237593988a0 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -504,6 +504,10 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, } } + for (const Arg *A : Args.filtered(OPT_define_availability)) { + Opts.AvailabilityMacros.push_back(A->getValue()); + } + if (const Arg *A = Args.getLastArg(OPT_value_recursion_threshold)) { unsigned threshold; if (StringRef(A->getValue()).getAsInteger(10, threshold)) { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index da749109d0fd9..79fb31bdc9626 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1372,6 +1372,115 @@ void Parser::parseObjCSelector(SmallVector &Names, } } +bool Parser::peekAvailabilityMacroName() { + parseAllAvailabilityMacroArguments(); + AvailabilityMacroMap Map = AvailabilityMacros; + + StringRef MacroName = Tok.getText(); + return Map.find(MacroName) != Map.end(); +} + +ParserStatus +Parser::parseAvailabilityMacro(SmallVectorImpl &Specs) { + // Get the macros from the compiler arguments. + parseAllAvailabilityMacroArguments(); + AvailabilityMacroMap Map = AvailabilityMacros; + + StringRef MacroName = Tok.getText(); + auto NameMatch = Map.find(MacroName); + if (NameMatch == Map.end()) + return makeParserSuccess(); // No match, it could be a standard platform. + + consumeToken(); + + llvm::VersionTuple Version; + SourceRange VersionRange; + if (Tok.isAny(tok::integer_literal, tok::floating_literal)) { + if (parseVersionTuple(Version, VersionRange, + diag::avail_query_expected_version_number)) + return makeParserError(); + } + + auto VersionMatch = NameMatch->getSecond().find(Version); + if (VersionMatch == NameMatch->getSecond().end()) { + diagnose(PreviousLoc, diag::attr_availability_unknown_version, + Version.getAsString(), MacroName); + return makeParserError(); // Failed to match the version, that's an error. + } + + Specs.append(VersionMatch->getSecond().begin(), + VersionMatch->getSecond().end()); + + return makeParserSuccess(); +} + +void Parser::parseAllAvailabilityMacroArguments() { + + if (AvailabilityMacrosComputed) return; + + AvailabilityMacroMap Map; + + SourceManager &SM = Context.SourceMgr; + const LangOptions &LangOpts = Context.LangOpts; + + for (StringRef macro: LangOpts.AvailabilityMacros) { + + // Create temporary parser. + int bufferID = SM.addMemBufferCopy(macro, + "-define-availability argument"); + swift::ParserUnit PU(SM, + SourceFileKind::Main, bufferID, + LangOpts, + TypeCheckerOptions(), "unknown"); + + ForwardingDiagnosticConsumer PDC(Context.Diags); + PU.getDiagnosticEngine().addConsumer(PDC); + + // Parse the argument. + AvailabilityMacroDefinition ParsedMacro; + ParserStatus Status = + PU.getParser().parseAvailabilityMacroDefinition(ParsedMacro); + if (Status.isError()) + continue; + + // Copy the Specs to the requesting ASTContext from the temporary context + // that parsed the argument. + auto SpecsCopy = SmallVector(); + for (auto *Spec : ParsedMacro.Specs) + if (auto *PlatformVersionSpec = + dyn_cast(Spec)) { + auto SpecCopy = + new (Context) PlatformVersionConstraintAvailabilitySpec( + *PlatformVersionSpec); + SpecsCopy.push_back(SpecCopy); + } + + ParsedMacro.Specs = SpecsCopy; + + // Find the macro info by name. + AvailabilityMacroVersionMap MacroDefinition; + auto NameMatch = Map.find(ParsedMacro.Name); + if (NameMatch != Map.end()) { + MacroDefinition = NameMatch->getSecond(); + } + + // Set the macro info by version. + auto PreviousEntry = + MacroDefinition.insert({ParsedMacro.Version, ParsedMacro.Specs}); + if (!PreviousEntry.second) { + diagnose(PU.getParser().PreviousLoc, diag::attr_availability_duplicate, + ParsedMacro.Name, ParsedMacro.Version.getAsString()); + } + + // Save back the macro spec. + Map.erase(ParsedMacro.Name); + Map.insert({ParsedMacro.Name, MacroDefinition}); + } + + AvailabilityMacros = Map; + AvailabilityMacrosComputed = true; +} + bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, DeclAttrKind DK) { // Ok, it is a valid attribute, eat it, and then process it. @@ -1975,7 +2084,8 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, StringRef Platform = Tok.getText(); if (Platform != "*" && - peekToken().isAny(tok::integer_literal, tok::floating_literal)) { + (peekToken().isAny(tok::integer_literal, tok::floating_literal) || + peekAvailabilityMacroName())) { // We have the short form of available: @available(iOS 8.0.1, *) SmallVector Specs; ParserStatus Status = parseAvailabilitySpecList(Specs); diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 090c05110529d..e08559aa82473 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1183,8 +1183,10 @@ static void parseGuardedPattern(Parser &P, GuardedPattern &result, /// Validate availability spec list, emitting diagnostics if necessary and removing /// specs for unrecognized platforms. -static void validateAvailabilitySpecList(Parser &P, - SmallVectorImpl &Specs) { +static void +validateAvailabilitySpecList(Parser &P, + SmallVectorImpl &Specs, + bool ParsingMacroDefinition) { llvm::SmallSet Platforms; bool HasOtherPlatformSpec = false; @@ -1232,8 +1234,13 @@ static void validateAvailabilitySpecList(Parser &P, } } - if (!HasOtherPlatformSpec) { - SourceLoc InsertWildcardLoc = Specs.back()->getSourceRange().End; + if (ParsingMacroDefinition) { + if (HasOtherPlatformSpec) { + SourceLoc InsertWildcardLoc = P.PreviousLoc; + P.diagnose(InsertWildcardLoc, diag::attr_availability_wildcard_in_macro); + } + } else if (!HasOtherPlatformSpec) { + SourceLoc InsertWildcardLoc = P.PreviousLoc; P.diagnose(InsertWildcardLoc, diag::availability_query_wildcard_required) .fixItInsertAfter(InsertWildcardLoc, ", *"); } @@ -1285,7 +1292,40 @@ ParserResult Parser::parseStmtConditionPoundAvailable() { } ParserStatus -Parser::parseAvailabilitySpecList(SmallVectorImpl &Specs) { +Parser::parseAvailabilityMacroDefinition(AvailabilityMacroDefinition &Result) { + + // Prime the lexer. + if (Tok.is(tok::NUM_TOKENS)) + consumeTokenWithoutFeedingReceiver(); + + if (!Tok.isIdentifierOrUnderscore()) { + diagnose(Tok, diag::attr_availability_missing_macro_name); + return makeParserError(); + } + + Result.Name = Tok.getText(); + consumeToken(); + + if (Tok.isAny(tok::integer_literal, tok::floating_literal)) { + SourceRange VersionRange; + if (parseVersionTuple(Result.Version, VersionRange, + diag::avail_query_expected_version_number)) { + return makeParserError(); + } + } + + if (!consumeIf(tok::colon)) { + diagnose(Tok, diag::attr_availability_expected_colon_macro, Result.Name); + return makeParserError(); + } + + return parseAvailabilitySpecList(Result.Specs, + /*ParsingMacroDefinition=*/true); +} + +ParserStatus +Parser::parseAvailabilitySpecList(SmallVectorImpl &Specs, + bool ParsingMacroDefinition) { SyntaxParsingContext AvailabilitySpecContext( SyntaxContext, SyntaxKind::AvailabilitySpecList); ParserStatus Status = makeParserSuccess(); @@ -1295,14 +1335,34 @@ Parser::parseAvailabilitySpecList(SmallVectorImpl &Specs) { while (1) { SyntaxParsingContext AvailabilityEntryContext( SyntaxContext, SyntaxKind::AvailabilityArgument); - auto SpecResult = parseAvailabilitySpec(); - if (auto *Spec = SpecResult.getPtrOrNull()) { - Specs.push_back(Spec); - } else { - if (SpecResult.hasCodeCompletion()) { - return makeParserCodeCompletionStatus(); + + // First look for a macro as we need Specs for the expansion. + bool MatchedAMacro = false; + if (!ParsingMacroDefinition && Tok.is(tok::identifier)) { + SmallVector MacroSpecs; + ParserStatus MacroStatus = parseAvailabilityMacro(MacroSpecs); + + if (MacroStatus.isError()) { + // There's a parsing error if the platform name matches a macro + // but something goes wrong after. + Status.setIsParseError(); + MatchedAMacro = true; + } else { + MatchedAMacro = !MacroSpecs.empty(); + Specs.append(MacroSpecs.begin(), MacroSpecs.end()); + } + } + + if (!MatchedAMacro) { + auto SpecResult = parseAvailabilitySpec(); + if (auto *Spec = SpecResult.getPtrOrNull()) { + Specs.push_back(Spec); + } else { + if (SpecResult.hasCodeCompletion()) { + return makeParserCodeCompletionStatus(); + } + Status.setIsParseError(); } - Status.setIsParseError(); } // We don't allow binary operators to combine specs. @@ -1362,7 +1422,7 @@ Parser::parseAvailabilitySpecList(SmallVectorImpl &Specs) { } if (Status.isSuccess() && !Status.hasCodeCompletion()) - validateAvailabilitySpecList(*this, Specs); + validateAvailabilitySpecList(*this, Specs, ParsingMacroDefinition); return Status; } diff --git a/test/ModuleInterface/availability-expansion.swift b/test/ModuleInterface/availability-expansion.swift new file mode 100644 index 0000000000000..7f42f1c14f3bb --- /dev/null +++ b/test/ModuleInterface/availability-expansion.swift @@ -0,0 +1,29 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -swift-version 5 -enable-library-evolution -typecheck -module-name Test -emit-module-interface-path %t/Test.swiftinterface %s -define-availability "_iOS8Aligned:macOS 10.10, iOS 8.0" -define-availability "_iOS9Aligned:macOS 10.11, iOS 9.0" -define-availability "_iOS9:iOS 9.0" -define-availability "_macOS10_11:macOS 10.11" -define-availability "_myProject 1.0:macOS 10.11" -define-availability "_myProject 2.5:macOS 10.12" +// RUN: %FileCheck %s < %t/Test.swiftinterface + +@available(_iOS8Aligned, *) +public func onMacOS10_10() {} +// CHECK: @available(macOS 10.10, iOS 8.0, *) +// CHECK-NEXT: public func onMacOS10_10 + +@available(_iOS9Aligned, *) +public func onMacOS10_11() {} +// CHECK: @available(macOS 10.11, iOS 9.0, *) +// CHECK-NEXT: public func onMacOS10_11() + +@available(_iOS9, _macOS10_11, tvOS 11.0, *) +public func composed() {} +// CHECK: @available(iOS 9.0, macOS 10.11, tvOS 11.0, *) +// CHECK-NEXT: public func composed() + +@available(_myProject 1.0, *) +public func onMyProjectV1() {} +// CHECK: @available(macOS 10.11, *) +// CHECK-NEXT: public func onMyProjectV1 + +@available(_myProject 2.5, *) +public func onMyProjectV2_5() {} +// CHECK: @available(macOS 10.12, *) +// CHECK-NEXT: public func onMyProjectV2_5 diff --git a/test/Sema/availability_define.swift b/test/Sema/availability_define.swift new file mode 100644 index 0000000000000..f4f62ce5385ab --- /dev/null +++ b/test/Sema/availability_define.swift @@ -0,0 +1,60 @@ +// RUN: %target-typecheck-verify-swift \ +// RUN: -define-availability "_iOS8Aligned:macOS 10.10, iOS 8.0" \ +// RUN: -define-availability "_iOS9Aligned:macOS 10.11, iOS 9.0" \ +// RUN: -define-availability "_iOS9:iOS 9.0" \ +// RUN: -define-availability "_macOS10_11:macOS 10.11" \ +// RUN: -define-availability "_myProject 1.0:macOS 10.11" \ +// RUN: -define-availability "_myProject 2.5:macOS 10.12" +// REQUIRES: OS=macosx + +@available(_iOS8Aligned, *) +public func onMacOS10_10() {} + +@available(_iOS9Aligned, *) +public func onMacOS10_11() {} + +@available(_iOS9, _macOS10_11, tvOS 11.0, *) +public func composed() {} + +@available(_iOS8Aligned, *) +@available(macOS, deprecated: 10.10) +public func onMacOSDeprecated() {} + +@available(_myProject, *) // expected-error {{expected declaration}} +// expected-error @-1 {{reference to undefined version '0' for availability macro '_myProject'}} +public func onMyProject() {} + +@available(_myProject 1.0, *) +public func onMyProjectV1() {} + +@available(_myProject 2.5, *) +public func onMyProjectV2_5() {} + +@available(_myProject 3.0, *)// expected-error {{expected declaration}} +// expected-error @-1 {{reference to undefined version '3.0' for availability macro '_myProject'}} +public func onMyProjectV3() {} + +@available(_myProject 3_0, *)// expected-error {{expected declaration}} +// expected-error @-1 {{expected version number}} +public func brokenVersion() {} + +@available(_unkownMacro, *) // expected-error {{expected declaration}} +// expected-error @-1 {{expected 'available' option such as 'unavailable', 'introduced', 'deprecated', 'obsoleted', 'message', or 'renamed'}} +public func unkownMacro() {} + +@available(_iOS9) // expected-error {{must handle potential future platforms with '*'}} +public func noOtherOSes() {} + +@available(_iOS8Aligned, *) +func client() { + onMacOS10_10() + onMacOS10_11() // expected-error {{is only available in macOS 10.11 or newer}} + // expected-note @-1 {{add 'if #available' version check}} + onMacOSDeprecated() + + if #available(_iOS9Aligned, *) { + onMacOS10_11() + } + + if #available(_unknownMacro, *) { } // expected-error {{expected version number}} +} diff --git a/test/Sema/availability_define_parsing.swift b/test/Sema/availability_define_parsing.swift new file mode 100644 index 0000000000000..291515794b47a --- /dev/null +++ b/test/Sema/availability_define_parsing.swift @@ -0,0 +1,31 @@ +// RUN: not %target-swift-frontend -typecheck %s \ +// RUN: -define-availability "_brokenParse:a b c d" \ +// RUN: -define-availability ":a b c d" \ +// RUN: -define-availability "_justAName" \ +// RUN: -define-availability "_brokenPlatforms:spaceOS 10.11" \ +// RUN: -define-availability "_refuseWildcard:iOS 13.0, *" \ +// RUN: -define-availability "_duplicateVersion 1.0:iOS 13.0" \ +// RUN: -define-availability "_duplicateVersion 1.0:iOS 13.0" \ +// RUN: 2>&1 | %FileCheck %s + +// Force reading the argument macros. +@available(_brokenPlatforms) +public func brokenPlaforms() {} + +// CHECK: -define-availability argument:1:16: error: expected version number +// CHECK-NEXT: _brokenParse:a b c d + +// CHECK: -define-availability argument:1:1: error: expected an identifier to begin an availability macro definition +// CHECK-NEXT: :a b c d + +// CHECK: -define-availability argument:1:11: error: expected ':' after '_justAName' in availability macro definition +// CHECK-NEXT: _justAName + +// CHECK: -define-availability argument:1:31: warning: unrecognized platform name 'spaceOS' +// CHECK-NEXT: _brokenPlatforms:spaceOS 10.11 + +// CHECK: -define-availability argument:1:27: error: future platforms identified by '*' cannot be used in an availability macro +// CHECK-NEXT: _refuseWildcard + +// CHECK: duplicate definition of availability macro '_duplicateVersion' for version '1.0' +// CHECK-NEXT: _duplicateVersion From 5ed261683d5973ef4dd45922b5ac77ba7cefcf43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Mon, 5 Oct 2020 17:28:47 -0700 Subject: [PATCH 225/745] [Sema] Report availability macros in inlinable code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Availability macros can’t be used in inlinable code as inlinable is copied textually in the generated swiftinterface files. Further would could lift this limitation. --- include/swift/AST/AvailabilitySpec.h | 7 +++++++ include/swift/AST/DiagnosticsSema.def | 4 ++++ lib/Parse/ParseDecl.cpp | 14 ++++++++++++-- lib/Sema/TypeCheckStmt.cpp | 18 ++++++++++++++++++ test/Sema/availability_define.swift | 14 ++++++++++++++ 5 files changed, 55 insertions(+), 2 deletions(-) diff --git a/include/swift/AST/AvailabilitySpec.h b/include/swift/AST/AvailabilitySpec.h index 86b0342419a1a..46197b01259d3 100644 --- a/include/swift/AST/AvailabilitySpec.h +++ b/include/swift/AST/AvailabilitySpec.h @@ -85,6 +85,9 @@ class PlatformVersionConstraintAvailabilitySpec : public AvailabilitySpec { SourceRange VersionSrcRange; + // Location of the macro expanded to create this spec. + SourceLoc MacroLoc; + public: PlatformVersionConstraintAvailabilitySpec(PlatformKind Platform, SourceLoc PlatformLoc, @@ -117,6 +120,10 @@ class PlatformVersionConstraintAvailabilitySpec : public AvailabilitySpec { SourceRange getSourceRange() const; + // Location of the macro expanded to create this spec. + SourceLoc getMacroLoc() const { return MacroLoc; } + void setMacroLoc(SourceLoc loc) { MacroLoc = loc; } + void print(raw_ostream &OS, unsigned Indent) const; static bool classof(const AvailabilitySpec *Spec) { diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index e27bdb0847cd8..f2d25de8a5cde 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4922,6 +4922,10 @@ WARNING(public_decl_needs_availability, none, "public declarations should have an availability attribute when building " "with -require-explicit-availability", ()) +ERROR(availability_macro_in_inlinable, none, + "availability macro cannot be used in inlinable %0", + (DescriptiveDeclKind)) + // This doesn't display as an availability diagnostic, but it's // implemented there and fires when these subscripts are marked // unavailable, so it seems appropriate to put it here. diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 79fb31bdc9626..489bf6ac96ed4 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1408,8 +1408,18 @@ Parser::parseAvailabilityMacro(SmallVectorImpl &Specs) { return makeParserError(); // Failed to match the version, that's an error. } - Specs.append(VersionMatch->getSecond().begin(), - VersionMatch->getSecond().end()); + // Make a copy of the specs to add the macro source location + // for the diagnostic about the use of macros in inlinable code. + SourceLoc MacroLoc = Tok.getLoc(); + for (auto *Spec : VersionMatch->getSecond()) + if (auto *PlatformVersionSpec = + dyn_cast(Spec)) { + auto SpecCopy = + new (Context) PlatformVersionConstraintAvailabilitySpec( + *PlatformVersionSpec); + SpecCopy->setMacroLoc(MacroLoc); + Specs.push_back(SpecCopy); + } return makeParserSuccess(); } diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 06860ed87c7ca..b5e1cfe63a6f7 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -465,6 +465,24 @@ static bool typeCheckConditionForStatement(LabeledConditionalStmt *stmt, for (auto &elt : cond) { if (elt.getKind() == StmtConditionElement::CK_Availability) { hadAnyFalsable = true; + + // Reject inlinable code using availability macros. + PoundAvailableInfo *info = elt.getAvailability(); + if (auto *decl = dc->getAsDecl()) { + if (decl->getAttrs().hasAttribute() || + decl->getAttrs().hasAttribute()) + for (auto queries : info->getQueries()) + if (auto availSpec = + dyn_cast(queries)) + if (availSpec->getMacroLoc().isValid()) { + Context.Diags.diagnose( + availSpec->getMacroLoc(), + swift::diag::availability_macro_in_inlinable, + decl->getDescriptiveKind()); + break; + } + } + continue; } diff --git a/test/Sema/availability_define.swift b/test/Sema/availability_define.swift index f4f62ce5385ab..d00abe95a2880 100644 --- a/test/Sema/availability_define.swift +++ b/test/Sema/availability_define.swift @@ -58,3 +58,17 @@ func client() { if #available(_unknownMacro, *) { } // expected-error {{expected version number}} } + +@inlinable +public func forbidMacrosInInlinableCode() { + if #available(_iOS9Aligned, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} + if #available(_iOS9, _macOS10_11, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} + if #available(iOS 9.0, _macOS10_11, tvOS 9.0, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} +} + +@_alwaysEmitIntoClient +public func forbidMacrosInInlinableCode1() { + if #available(_iOS9Aligned, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} + if #available(_iOS9, _macOS10_11, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} + if #available(iOS 9.0, _macOS10_11, tvOS 9.0, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} +} From d695acfed113e0e1d59ee103862e43de3e65b1f0 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 5 Oct 2020 18:56:53 -0700 Subject: [PATCH 226/745] Document 'standalone' Parameters --- .../swift/AST/FineGrainedDependencyFormat.h | 25 +++++++++- lib/AST/FineGrainedDependencyFormat.cpp | 46 +++++++++++++------ .../FineGrainedDependencyDriverGraph.cpp | 2 +- lib/Serialization/Serialization.cpp | 2 +- 4 files changed, 57 insertions(+), 18 deletions(-) diff --git a/include/swift/AST/FineGrainedDependencyFormat.h b/include/swift/AST/FineGrainedDependencyFormat.h index 78b95e3346a45..9a3030d3dc10f 100644 --- a/include/swift/AST/FineGrainedDependencyFormat.h +++ b/include/swift/AST/FineGrainedDependencyFormat.h @@ -135,9 +135,32 @@ bool writeFineGrainedDependencyGraphToPath(DiagnosticEngine &diags, llvm::StringRef path, const SourceFileDepGraph &g); +/// Enumerates the supported set of purposes for writing out or reading in +/// swift dependency information into a file. These can be used to influence +/// the structure of the resulting data that is produced by the serialization +/// machinery defined here. +enum class Purpose : bool { + /// Write out fine grained dependency metadata suitable for embedding in + /// \c .swiftmodule file. + /// + /// The resulting metadata does not contain the usual block descriptor header + /// nor does it contain a leading magic signature, which would otherwise + /// disrupt clients and tools that do not expect them to be present such as + /// llvm-bcanalyzer. + ForSwiftModule = false, + /// Write out fine grained dependency metadata suitable for a standalone + /// \c .swiftdeps file. + /// + /// The resulting metadata will contain a leading magic signature and block + /// descriptor header. + ForSwiftDeps = true, +}; + +/// Tries to write out the given dependency graph with the given +/// bitstream writer. void writeFineGrainedDependencyGraph(llvm::BitstreamWriter &Out, const SourceFileDepGraph &g, - bool standalone); + Purpose purpose); } // namespace fine_grained_dependencies } // namespace swift diff --git a/lib/AST/FineGrainedDependencyFormat.cpp b/lib/AST/FineGrainedDependencyFormat.cpp index d7570d414e85a..35e4ab062c982 100644 --- a/lib/AST/FineGrainedDependencyFormat.cpp +++ b/lib/AST/FineGrainedDependencyFormat.cpp @@ -44,7 +44,7 @@ class Deserializer { public: Deserializer(llvm::MemoryBufferRef Data) : Cursor(Data) {} - bool readFineGrainedDependencyGraph(SourceFileDepGraph &g, bool standalone); + bool readFineGrainedDependencyGraph(SourceFileDepGraph &g, Purpose purpose); bool readFineGrainedDependencyGraphFromSwiftModule(SourceFileDepGraph &g); }; @@ -152,14 +152,23 @@ static llvm::Optional getDeclAspect(unsigned declAspect) { } bool Deserializer::readFineGrainedDependencyGraph(SourceFileDepGraph &g, - bool standalone) { + Purpose purpose) { using namespace record_block; - if (standalone && readSignature()) - return true; + switch (purpose) { + case Purpose::ForSwiftDeps: + if (readSignature()) + return true; - if (standalone && enterTopLevelBlock()) - return true; + if (enterTopLevelBlock()) + return true; + LLVM_FALLTHROUGH; + case Purpose::ForSwiftModule: + // N.B. Incremental metadata embedded in swiftmodule files does not have + // a leading signature, and its top-level block has already been + // consumed by the time we get here. + break; + } if (readMetadata()) return true; @@ -261,7 +270,7 @@ bool Deserializer::readFineGrainedDependencyGraph(SourceFileDepGraph &g, bool swift::fine_grained_dependencies::readFineGrainedDependencyGraph( llvm::MemoryBuffer &buffer, SourceFileDepGraph &g) { Deserializer deserializer(buffer.getMemBufferRef()); - return deserializer.readFineGrainedDependencyGraph(g, /*standalone*/true); + return deserializer.readFineGrainedDependencyGraph(g, Purpose::ForSwiftDeps); } bool swift::fine_grained_dependencies::readFineGrainedDependencyGraph( @@ -324,7 +333,7 @@ class Serializer { public: void writeFineGrainedDependencyGraph(const SourceFileDepGraph &g, - bool standalone); + Purpose purpose); }; } // end namespace @@ -385,13 +394,19 @@ void Serializer::writeMetadata() { void Serializer::writeFineGrainedDependencyGraph(const SourceFileDepGraph &g, - bool standalone) { - auto blockID = INCREMENTAL_INFORMATION_BLOCK_ID; - if (standalone) { + Purpose purpose) { + unsigned blockID = 0; + switch (purpose) { + case Purpose::ForSwiftDeps: writeSignature(); writeBlockInfoBlock(); blockID = RECORD_BLOCK_ID; + break; + case Purpose::ForSwiftModule: + blockID = INCREMENTAL_INFORMATION_BLOCK_ID; + break; } + llvm::BCBlockRAII restoreBlock(Out, blockID, 8); using namespace record_block; @@ -474,9 +489,10 @@ unsigned Serializer::getIdentifier(StringRef str) { } void swift::fine_grained_dependencies::writeFineGrainedDependencyGraph( - llvm::BitstreamWriter &Out, const SourceFileDepGraph &g, bool standalone) { + llvm::BitstreamWriter &Out, const SourceFileDepGraph &g, + Purpose purpose) { Serializer serializer{Out}; - serializer.writeFineGrainedDependencyGraph(g, standalone); + serializer.writeFineGrainedDependencyGraph(g, purpose); } bool swift::fine_grained_dependencies::writeFineGrainedDependencyGraphToPath( @@ -486,7 +502,7 @@ bool swift::fine_grained_dependencies::writeFineGrainedDependencyGraphToPath( return withOutputFile(diags, path, [&](llvm::raw_ostream &out) { SmallVector Buffer; llvm::BitstreamWriter Writer{Buffer}; - writeFineGrainedDependencyGraph(Writer, g, /*standalone*/ true); + writeFineGrainedDependencyGraph(Writer, g, Purpose::ForSwiftDeps); out.write(Buffer.data(), Buffer.size()); out.flush(); return false; @@ -578,7 +594,7 @@ bool Deserializer::readFineGrainedDependencyGraphFromSwiftModule( consumeError(std::move(Err)); return false; } - if (readFineGrainedDependencyGraph(g, /*standalone*/ false)) { + if (readFineGrainedDependencyGraph(g, Purpose::ForSwiftModule)) { break; } diff --git a/lib/Driver/FineGrainedDependencyDriverGraph.cpp b/lib/Driver/FineGrainedDependencyDriverGraph.cpp index f6e260611a014..f45950a234b8b 100644 --- a/lib/Driver/FineGrainedDependencyDriverGraph.cpp +++ b/lib/Driver/FineGrainedDependencyDriverGraph.cpp @@ -120,7 +120,7 @@ ModuleDepGraph::Changes ModuleDepGraph::loadFromSwiftModuleBuffer( SourceFileDepGraph::loadFromSwiftModuleBuffer(buffer); if (!sourceFileDepGraph) return None; - jobsBySwiftDeps.insert(std::make_pair(buffer.getBufferIdentifier(), Cmd)); + jobsBySwiftDeps[buffer.getBufferIdentifier().str()] = Cmd; auto changes = integrate(*sourceFileDepGraph, buffer.getBufferIdentifier()); if (verifyFineGrainedDependencyGraphAfterEveryImport) verify(); diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 78eb2b7eda821..ad6e002234df8 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -5240,7 +5240,7 @@ void Serializer::writeToStream( S.writeAST(DC); if (options.ExperimentalCrossModuleIncrementalInfo && DepGraph) { fine_grained_dependencies::writeFineGrainedDependencyGraph( - S.Out, *DepGraph, /*standalone*/ false); + S.Out, *DepGraph, fine_grained_dependencies::Purpose::ForSwiftModule); } } From d2fc2c1e35dc53870d511555e8d12f20bd32cdae Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Mon, 5 Oct 2020 13:19:40 -0700 Subject: [PATCH 227/745] [IRGen] Silenced unused variable warning. --- lib/IRGen/GenCall.cpp | 29 ++++++++++------------------- lib/IRGen/GenCall.h | 4 ++++ 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index d4db4e9e228d7..8c24de2aa5379 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -1970,29 +1970,21 @@ class AsyncCallEmission final : public CallEmission { llArgs.add(selfValue); } auto layout = getAsyncContextLayout(); - auto params = fnConv.getParameters(); - for (auto index : indices(params)) { - Optional fieldLayout; - if (selfValue && index == params.size() - 1) { - fieldLayout = layout.getLocalContextLayout(); - } else { - fieldLayout = layout.getArgumentLayout(index); - } + for (unsigned index = 0, count = layout.getArgumentCount(); index < count; + ++index) { + auto fieldLayout = layout.getArgumentLayout(index); Address fieldAddr = - fieldLayout->project(IGF, context, /*offsets*/ llvm::None); - auto &ti = cast(fieldLayout->getType()); + fieldLayout.project(IGF, context, /*offsets*/ llvm::None); + auto &ti = cast(fieldLayout.getType()); ti.initialize(IGF, llArgs, fieldAddr, isOutlined); } - unsigned index = 0; - for (auto indirectResult : fnConv.getIndirectSILResultTypes( - IGF.IGM.getMaximalTypeExpansionContext())) { - (void)indirectResult; + for (unsigned index = 0, count = layout.getIndirectReturnCount(); + index < count; ++index) { auto fieldLayout = layout.getIndirectReturnLayout(index); Address fieldAddr = fieldLayout.project(IGF, context, /*offsets*/ llvm::None); cast(fieldLayout.getType()) .initialize(IGF, llArgs, fieldAddr, isOutlined); - ++index; } if (layout.hasBindings()) { auto bindingLayout = layout.getBindingsLayout(); @@ -2024,15 +2016,14 @@ class AsyncCallEmission final : public CallEmission { Explosion nativeExplosion; auto layout = getAsyncContextLayout(); auto dataAddr = layout.emitCastTo(IGF, context); - int index = layout.getFirstDirectReturnIndex(); - for (auto result : fnConv.getDirectSILResults()) { - auto &fieldLayout = layout.getElement(index); + for (unsigned index = 0, count = layout.getDirectReturnCount(); + index < count; ++index) { + auto fieldLayout = layout.getDirectReturnLayout(index); Address fieldAddr = fieldLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); auto &fieldTI = fieldLayout.getType(); cast(fieldTI).loadAsTake(IGF, fieldAddr, nativeExplosion); - ++index; } out = nativeSchema.mapFromNative(IGF.IGM, IGF, nativeExplosion, resultType); diff --git a/lib/IRGen/GenCall.h b/lib/IRGen/GenCall.h index 86dd7a1afcee4..91e440d7925c3 100644 --- a/lib/IRGen/GenCall.h +++ b/lib/IRGen/GenCall.h @@ -183,6 +183,10 @@ namespace irgen { } unsigned getFirstDirectReturnIndex() { return getIndexAfterArguments(); } + unsigned getDirectReturnCount() { return directReturnInfos.size(); } + ElementLayout getDirectReturnLayout(unsigned index) { + return getElement(getFirstDirectReturnIndex() + index); + } AsyncContextLayout(IRGenModule &IGM, LayoutStrategy strategy, ArrayRef fieldTypes, From 15d90d8f5bae9ad4cc8949b05f017f55a93431f9 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Mon, 5 Oct 2020 16:21:52 -0700 Subject: [PATCH 228/745] [IRGen] Pull async polymorphic params from explosion. Previously, the polymorphic arguments were being discarded. Here, that situation is improved by pulling the polymorphic arguments out of the explosion when having the polymorphic parameters via the NecessaryBindings instance. In order to eanble that, an overload of NecessaryBindings::save is added which takes an explosion and asserts that the polymorphic parameter pulled from the explosion matches with the polymorphic parameter in the NecessaryBindings instance. --- lib/IRGen/GenCall.cpp | 15 ++++++---- lib/IRGen/GenProto.cpp | 55 ++++++++++++++++++++++++----------- lib/IRGen/NecessaryBindings.h | 4 +++ 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 8c24de2aa5379..018c5d65a425f 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -1989,13 +1989,16 @@ class AsyncCallEmission final : public CallEmission { if (layout.hasBindings()) { auto bindingLayout = layout.getBindingsLayout(); auto bindingsAddr = bindingLayout.project(IGF, context, /*offsets*/ None); - layout.getBindings().save(IGF, bindingsAddr); + layout.getBindings().save(IGF, bindingsAddr, llArgs); } - // At this point, llArgs contains the arguments that are being passed along - // via the async context. We can safely drop them on the floor. - (void)llArgs.claimAll(); - // TODO: Validation: we should be able to check that the contents of llArgs - // matches what is expected by the layout. + if (selfValue) { + auto fieldLayout = layout.getLocalContextLayout(); + Address fieldAddr = + fieldLayout.project(IGF, context, /*offsets*/ llvm::None); + auto &ti = cast(fieldLayout.getType()); + ti.initialize(IGF, llArgs, fieldAddr, isOutlined); + } + } } void emitCallToUnmappedExplosion(llvm::CallInst *call, Explosion &out) override { SILFunctionConventions fnConv(getCallee().getSubstFunctionType(), diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 706e64cf6d74e..80b280740361f 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -2728,24 +2728,45 @@ void NecessaryBindings::restore(IRGenFunction &IGF, Address buffer, [&](CanType type) { return type;}); } +template +static void save(const NecessaryBindings &bindings, IRGenFunction &IGF, + Address buffer, Transform transform) { + emitInitOfGenericRequirementsBuffer( + IGF, bindings.getRequirements().getArrayRef(), buffer, + [&](GenericRequirement requirement) -> llvm::Value * { + CanType type = requirement.TypeParameter; + if (auto protocol = requirement.Protocol) { + if (auto archetype = dyn_cast(type)) { + auto wtable = + emitArchetypeWitnessTableRef(IGF, archetype, protocol); + return transform(requirement, wtable); + } else { + auto conformance = bindings.getConformance(requirement); + auto wtable = emitWitnessTableRef(IGF, type, conformance); + return transform(requirement, wtable); + } + } else { + auto metadata = IGF.emitTypeMetadataRef(type); + return transform(requirement, metadata); + } + }); +}; + +void NecessaryBindings::save(IRGenFunction &IGF, Address buffer, + Explosion &source) const { + ::save(*this, IGF, buffer, + [&](GenericRequirement requirement, + llvm::Value *expected) -> llvm::Value * { + auto *value = source.claimNext(); + assert(value == expected); + return value; + }); +} + void NecessaryBindings::save(IRGenFunction &IGF, Address buffer) const { - emitInitOfGenericRequirementsBuffer(IGF, Requirements.getArrayRef(), buffer, - [&](GenericRequirement requirement) -> llvm::Value* { - CanType type = requirement.TypeParameter; - if (auto protocol = requirement.Protocol) { - if (auto archetype = dyn_cast(type)) { - auto wtable = emitArchetypeWitnessTableRef(IGF, archetype, protocol); - return wtable; - } else { - auto conformance = getConformance(requirement); - auto wtable = emitWitnessTableRef(IGF, type, conformance); - return wtable; - } - } else { - auto metadata = IGF.emitTypeMetadataRef(type); - return metadata; - } - }); + ::save(*this, IGF, buffer, + [](GenericRequirement requirement, + llvm::Value *value) -> llvm::Value * { return value; }); } void NecessaryBindings::addTypeMetadata(CanType type) { diff --git a/lib/IRGen/NecessaryBindings.h b/lib/IRGen/NecessaryBindings.h index df73cae8621ca..00b8085cc000a 100644 --- a/lib/IRGen/NecessaryBindings.h +++ b/lib/IRGen/NecessaryBindings.h @@ -25,6 +25,8 @@ #include "llvm/ADT/SetVector.h" #include "swift/AST/Types.h" +#include "Explosion.h" + namespace swift { class CanType; enum class MetadataState : size_t; @@ -94,6 +96,8 @@ class NecessaryBindings { /// Save the necessary bindings to the given buffer. void save(IRGenFunction &IGF, Address buffer) const; + void save(IRGenFunction &IGF, Address buffer, Explosion &source) const; + /// Restore the necessary bindings from the given buffer. void restore(IRGenFunction &IGF, Address buffer, MetadataState state) const; From 1a106d329fc4718a8ff5746ab174309a642d315f Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Mon, 5 Oct 2020 16:26:01 -0700 Subject: [PATCH 229/745] [NFC] Tweaked name of NecessaryBindings factory method. --- lib/IRGen/GenCall.cpp | 2 +- lib/IRGen/GenProto.cpp | 2 +- lib/IRGen/NecessaryBindings.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 018c5d65a425f..192f9320d0772 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -153,7 +153,7 @@ AsyncContextLayout irgen::getAsyncContextLayout( } // ArgTypes formalArguments...; - auto bindings = NecessaryBindings::forAsyncFunctionInvocations( + auto bindings = NecessaryBindings::forAsyncFunctionInvocation( IGF.IGM, originalType, substitutionMap); if (!bindings.empty()) { auto bindingsSize = bindings.getBufferSize(IGF.IGM); diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 80b280740361f..1280c19e3281f 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -3042,7 +3042,7 @@ void EmitPolymorphicArguments::emit(SubstitutionMap subs, } } -NecessaryBindings NecessaryBindings::forAsyncFunctionInvocations( +NecessaryBindings NecessaryBindings::forAsyncFunctionInvocation( IRGenModule &IGM, CanSILFunctionType origType, SubstitutionMap subs) { return computeBindings(IGM, origType, subs, false /*forPartialApplyForwarder*/); diff --git a/lib/IRGen/NecessaryBindings.h b/lib/IRGen/NecessaryBindings.h index 00b8085cc000a..eea62e66033a1 100644 --- a/lib/IRGen/NecessaryBindings.h +++ b/lib/IRGen/NecessaryBindings.h @@ -57,8 +57,8 @@ class NecessaryBindings { /// Collect the necessary bindings to invoke a function with the given /// signature. static NecessaryBindings - forAsyncFunctionInvocations(IRGenModule &IGM, CanSILFunctionType origType, - SubstitutionMap subs); + forAsyncFunctionInvocation(IRGenModule &IGM, CanSILFunctionType origType, + SubstitutionMap subs); static NecessaryBindings forPartialApplyForwarder(IRGenModule &IGM, CanSILFunctionType origType, SubstitutionMap subs, From 72051efa034a1cd28bdbe3b7d09dcfeaeb7b3584 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Mon, 5 Oct 2020 16:27:36 -0700 Subject: [PATCH 230/745] [Concurrency] Async CC for protocol extension methods. Use the TypeInfo for the argument lowering type of the self parameter rather than for the self parameter's type itself. --- lib/IRGen/GenCall.cpp | 7 +- ...otocolextension_instance-void-to-int64.sil | 82 +++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 192f9320d0772..a544328dc0193 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -138,7 +138,11 @@ AsyncContextLayout irgen::getAsyncContextLayout( SILType ty = IGF.IGM.silConv.getSILType(localContextParameter, substitutedType, IGF.IGM.getMaximalTypeExpansionContext()); - auto &ti = IGF.getTypeInfoForLowered(ty.getASTType()); + auto argumentLoweringType = + getArgumentLoweringType(ty.getASTType(), localContextParameter, + /*isNoEscape*/ true); + + auto &ti = IGF.getTypeInfoForLowered(argumentLoweringType); valTypes.push_back(ty); typeInfos.push_back(&ti); localContextInfo = {ty, localContextParameter.getConvention()}; @@ -1999,7 +2003,6 @@ class AsyncCallEmission final : public CallEmission { ti.initialize(IGF, llArgs, fieldAddr, isOutlined); } } - } void emitCallToUnmappedExplosion(llvm::CallInst *call, Explosion &out) override { SILFunctionConventions fnConv(getCallee().getSubstFunctionType(), IGF.getSILModule()); diff --git a/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil b/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil new file mode 100644 index 0000000000000..10badd65e163d --- /dev/null +++ b/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil @@ -0,0 +1,82 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +protocol P { + func printMe() -> Int64 +} + +extension P { + func callPrintMe() async -> Int64 +} + +struct I : P { + @_hasStorage let int: Int64 { get } + func printMe() -> Int64 + init(int: Int64) +} + +// CHECK-LL: define hidden swiftcc void @callPrintMe(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @callPrintMe : $@async @convention(method) (@in_guaranteed Self) -> Int64 { +bb0(%self : $*Self): + %P_printMe = witness_method $Self, #P.printMe : (Self) -> () -> Int64 : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + %result = apply %P_printMe(%self) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + return %result : $Int64 +} + +sil hidden @I_printMe : $@convention(method) (I) -> Int64 { +bb0(%self : $I): + %self_addr = alloc_stack $I + store %self to %self_addr : $*I + %printGeneric = function_ref @printGeneric : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %printGeneric_result = apply %printGeneric(%self_addr) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %self_addr : $*I + %result = struct_extract %self : $I, #I.int + return %result : $Int64 +} + +sil private [transparent] [thunk] @I_P_printMe : $@convention(witness_method: P) (@in_guaranteed I) -> Int64 { +bb0(%self_addr : $*I): + %self = load %self_addr : $*I + %I_printMe = function_ref @I_printMe : $@convention(method) (I) -> Int64 + %result = apply %I_printMe(%self) : $@convention(method) (I) -> Int64 + return %result : $Int64 +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %i_type = metatype $@thin I.Type + %i_int_literal = integer_literal $Builtin.Int64, 99 + %i_int = struct $Int64 (%i_int_literal : $Builtin.Int64) + %i = struct $I (%i_int : $Int64) + %i_addr = alloc_stack $I + store %i to %i_addr : $*I + %callPrintMe = function_ref @callPrintMe : $@async @convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + %result = apply %callPrintMe(%i_addr) : $@async @convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 // CHECK: I(int: 99) + dealloc_stack %i_addr : $*I + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_result = apply %printInt64(%result) : $@convention(thin) (Int64) -> () // CHECK: 99 + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} + +sil_witness_table hidden I: P module main { + method #P.printMe: (Self) -> () -> Int64 : @I_P_printMe +} From b6fb435fbfbb6c0d22fa1706b9bc6c287acb8e02 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 6 Oct 2020 11:56:28 -0700 Subject: [PATCH 231/745] Invert an error return to match the documentation --- lib/AST/FineGrainedDependencies.cpp | 2 +- lib/AST/FineGrainedDependencyFormat.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/AST/FineGrainedDependencies.cpp b/lib/AST/FineGrainedDependencies.cpp index 5a16f674b704e..594b78931eaac 100644 --- a/lib/AST/FineGrainedDependencies.cpp +++ b/lib/AST/FineGrainedDependencies.cpp @@ -61,7 +61,7 @@ SourceFileDepGraph::loadFromBuffer(llvm::MemoryBuffer &buffer) { Optional SourceFileDepGraph::loadFromSwiftModuleBuffer(llvm::MemoryBuffer &buffer) { SourceFileDepGraph fg; - if (!swift::fine_grained_dependencies:: + if (swift::fine_grained_dependencies:: readFineGrainedDependencyGraphFromSwiftModule(buffer, fg)) return None; return Optional(std::move(fg)); diff --git a/lib/AST/FineGrainedDependencyFormat.cpp b/lib/AST/FineGrainedDependencyFormat.cpp index 35e4ab062c982..659bc3b0a3001 100644 --- a/lib/AST/FineGrainedDependencyFormat.cpp +++ b/lib/AST/FineGrainedDependencyFormat.cpp @@ -571,17 +571,17 @@ bool Deserializer::readFineGrainedDependencyGraphFromSwiftModule( SourceFileDepGraph &g) { if (!checkModuleSignature(Cursor, {0xE2, 0x9C, 0xA8, 0x0E}) || !enterTopLevelModuleBlock(Cursor, llvm::bitc::FIRST_APPLICATION_BLOCKID, false)) { - return false; + return true; } llvm::BitstreamEntry topLevelEntry; - bool ReadFineGrainedDependencies = false; + bool DidNotReadFineGrainedDependencies = true; while (!Cursor.AtEndOfStream()) { llvm::Expected maybeEntry = Cursor.advance(llvm::BitstreamCursor::AF_DontPopBlockAtEnd); if (!maybeEntry) { consumeError(maybeEntry.takeError()); - return false; + return true; } topLevelEntry = maybeEntry.get(); if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock) @@ -592,13 +592,13 @@ bool Deserializer::readFineGrainedDependencyGraphFromSwiftModule( if (llvm::Error Err = Cursor.EnterSubBlock(INCREMENTAL_INFORMATION_BLOCK_ID)) { consumeError(std::move(Err)); - return false; + return true; } if (readFineGrainedDependencyGraph(g, Purpose::ForSwiftModule)) { break; } - ReadFineGrainedDependencies = true; + DidNotReadFineGrainedDependencies = false; break; } @@ -606,11 +606,11 @@ bool Deserializer::readFineGrainedDependencyGraphFromSwiftModule( // Unknown top-level block, possibly for use by a future version of the // module format. if (Cursor.SkipBlock()) { - return false; + return true; } break; } } - return ReadFineGrainedDependencies; + return DidNotReadFineGrainedDependencies; } From b90cab6f1b5e7cb2ccb8b123e7fa5a00d022182c Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 6 Oct 2020 10:34:03 -0700 Subject: [PATCH 232/745] [NFC] Made indexing on AsyncContextLayout private. Previously the methods for getting the index into the layout were public and were being used to directly access the underlying buffer. Here, that abstraction leakage is fixed and field access is forced to go through the appropriate methods. --- lib/IRGen/GenCall.h | 56 ++++++++++++++++++++---------------------- lib/IRGen/IRGenSIL.cpp | 13 +++------- 2 files changed, 31 insertions(+), 38 deletions(-) diff --git a/lib/IRGen/GenCall.h b/lib/IRGen/GenCall.h index 91e440d7925c3..6cda0007299f9 100644 --- a/lib/IRGen/GenCall.h +++ b/lib/IRGen/GenCall.h @@ -109,26 +109,42 @@ namespace irgen { NecessaryBindings bindings; SmallVector argumentInfos; + unsigned getErrorIndex() { return (unsigned)FixedIndex::Error; } + unsigned getFirstIndirectReturnIndex() { + return getErrorIndex() + getErrorCount(); + } + unsigned getLocalContextIndex() { + assert(hasLocalContext()); + return getFirstIndirectReturnIndex() + getIndirectReturnCount(); + } + unsigned getIndexAfterLocalContext() { + return getFirstIndirectReturnIndex() + getIndirectReturnCount() + + (hasLocalContext() ? 1 : 0); + } + unsigned getBindingsIndex() { + assert(hasBindings()); + return getIndexAfterLocalContext(); + } + unsigned getFirstArgumentIndex() { + return getIndexAfterLocalContext() + (hasBindings() ? 1 : 0); + } + unsigned getIndexAfterArguments() { + return getFirstArgumentIndex() + getArgumentCount(); + } + unsigned getFirstDirectReturnIndex() { return getIndexAfterArguments(); } + public: bool canHaveError() { return canHaveValidError; } - unsigned getErrorIndex() { return (unsigned)FixedIndex::Error; } ElementLayout getErrorLayout() { return getElement(getErrorIndex()); } unsigned getErrorCount() { return (unsigned)FixedCount::Error; } SILType getErrorType() { return errorType; } - unsigned getFirstIndirectReturnIndex() { - return getErrorIndex() + getErrorCount(); - } ElementLayout getIndirectReturnLayout(unsigned index) { return getElement(getFirstIndirectReturnIndex() + index); } unsigned getIndirectReturnCount() { return indirectReturnInfos.size(); } bool hasLocalContext() { return (bool)localContextInfo; } - unsigned getLocalContextIndex() { - assert(hasLocalContext()); - return getFirstIndirectReturnIndex() + getIndirectReturnCount(); - } ElementLayout getLocalContextLayout() { assert(hasLocalContext()); return getElement(getLocalContextIndex()); @@ -141,48 +157,30 @@ namespace irgen { assert(hasLocalContext()); return localContextInfo->type; } - unsigned getIndexAfterLocalContext() { - return getFirstIndirectReturnIndex() + getIndirectReturnCount() + - (hasLocalContext() ? 1 : 0); - } bool hasBindings() const { return !bindings.empty(); } - unsigned getBindingsIndex() { - assert(hasBindings()); - return getIndexAfterLocalContext(); - } ElementLayout getBindingsLayout() { assert(hasBindings()); return getElement(getBindingsIndex()); } - ParameterConvention getBindingsConvention() { - return ParameterConvention::Direct_Unowned; - } const NecessaryBindings &getBindings() const { return bindings; } - unsigned getFirstArgumentIndex() { - return getIndexAfterLocalContext() + (hasBindings() ? 1 : 0); - } ElementLayout getArgumentLayout(unsigned index) { return getElement(getFirstArgumentIndex() + index); } - ParameterConvention getArgumentConvention(unsigned index) { - return argumentInfos[index].convention; - } SILType getArgumentType(unsigned index) { return argumentInfos[index].type; } + // Returns the type of a parameter of the substituted function using the + // indexing of the function parameters, *not* the indexing of + // AsyncContextLayout. SILType getParameterType(unsigned index) { SILFunctionConventions origConv(substitutedType, IGF.getSILModule()); return origConv.getSILArgumentType( index, IGF.IGM.getMaximalTypeExpansionContext()); } unsigned getArgumentCount() { return argumentInfos.size(); } - unsigned getIndexAfterArguments() { - return getFirstArgumentIndex() + getArgumentCount(); - } - unsigned getFirstDirectReturnIndex() { return getIndexAfterArguments(); } unsigned getDirectReturnCount() { return directReturnInfos.size(); } ElementLayout getDirectReturnLayout(unsigned index) { return getElement(getFirstDirectReturnIndex() + index); diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index dc95725adfa14..666c747cf371f 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1261,9 +1261,7 @@ class AsyncNativeCCEntryPointArgumentEmission final } llvm::Value *getIndirectResult(unsigned index) override { Address dataAddr = layout.emitCastTo(IGF, context); - unsigned baseIndirectReturnIndex = layout.getFirstIndirectReturnIndex(); - unsigned elementIndex = baseIndirectReturnIndex + index; - auto &fieldLayout = layout.getElement(elementIndex); + auto fieldLayout = layout.getIndirectReturnLayout(index); Address fieldAddr = fieldLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); return IGF.Builder.CreateLoad(fieldAddr); @@ -3101,16 +3099,13 @@ static void emitReturnInst(IRGenSILFunction &IGF, auto layout = getAsyncContextLayout(IGF); Address dataAddr = layout.emitCastTo(IGF, context); - unsigned index = layout.getFirstDirectReturnIndex(); - for (auto r : - IGF.CurSILFn->getLoweredFunctionType()->getDirectFormalResults()) { - (void)r; - auto &fieldLayout = layout.getElement(index); + for (unsigned index = 0, count = layout.getDirectReturnCount(); + index < count; ++index) { + auto fieldLayout = layout.getDirectReturnLayout(index); Address fieldAddr = fieldLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); cast(fieldLayout.getType()) .initialize(IGF, result, fieldAddr, /*isOutlined*/ false); - ++index; } IGF.Builder.CreateRetVoid(); } else { From f6bfd416aba04a75da4ea7ab017e7a07665d4102 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 6 Oct 2020 11:11:05 -0700 Subject: [PATCH 233/745] [NFC] Use TypeInfo for indirect return storage. Previously a raw CreateLoad was used, which happened to be fine. Here, a TI is used, explicitly clarifying that the indirect return is taken. --- lib/IRGen/IRGenSIL.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 666c747cf371f..06e4ee7fddcf1 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1260,11 +1260,13 @@ class AsyncNativeCCEntryPointArgumentEmission final "indirected through the context"); } llvm::Value *getIndirectResult(unsigned index) override { - Address dataAddr = layout.emitCastTo(IGF, context); auto fieldLayout = layout.getIndirectReturnLayout(index); Address fieldAddr = fieldLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); - return IGF.Builder.CreateLoad(fieldAddr); + auto &ti = cast(fieldLayout.getType()); + Explosion explosion; + ti.loadAsTake(IGF, fieldAddr, explosion); + return explosion.claimNext(); }; llvm::Value *getSelfWitnessTable() override { llvm_unreachable("unimplemented"); From d5d65b0dff6a5a027653a84a846baae33c123f55 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 6 Oct 2020 11:26:33 -0700 Subject: [PATCH 234/745] [NFC] Removed unused method. --- lib/IRGen/IRGenSIL.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 06e4ee7fddcf1..9237f6fa967b4 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1220,18 +1220,6 @@ class AsyncNativeCCEntryPointArgumentEmission final Address addr = errorLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); return addr.getAddress(); } - llvm::Value *getErrorResultAddrForCall() { - auto errorLayout = layout.getErrorLayout(); - auto &ti = cast(errorLayout.getType()); - auto allocaAddr = ti.allocateStack(IGF, layout.getErrorType(), "arg"); - auto addrInContext = - layout.getErrorLayout().project(IGF, dataAddr, /*offsets*/ llvm::None); - Explosion explosion; - ti.loadAsTake(IGF, addrInContext, explosion); - ti.initialize(IGF, explosion, allocaAddr.getAddress(), - /*isOutlined*/ false); - return allocaAddr.getAddress().getAddress(); - } llvm::Value *getContext() override { auto contextLayout = layout.getLocalContextLayout(); Address addr = contextLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); From d3e6ffdbad9ed2af857508891a4530b7f11d4c6e Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 6 Oct 2020 12:18:44 -0500 Subject: [PATCH 235/745] [lldb-toolbox] Add the ability to disassemble-to-file a specific function from a target without running the target. The routine already supports dumping the assembly to file if we are at a breakpoint using the current frame. This adds a -n option so one can without running just dump the assembly to a file of a specific function in a binary. --- utils/lldb/lldbToolBox.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/utils/lldb/lldbToolBox.py b/utils/lldb/lldbToolBox.py index 92399237070ed..8ec2c705c0147 100644 --- a/utils/lldb/lldbToolBox.py +++ b/utils/lldb/lldbToolBox.py @@ -73,12 +73,26 @@ def disassemble_to_file(debugger, command, exec_ctx, result, internal_dict): by the user. """ parser = argparse.ArgumentParser(prog='disassemble-to-file', description=""" - Dump the disassembly of the current frame to the specified file. + Dump the disassembly of the current frame or specified function to the + specified file. """) parser.add_argument('file', type=argparse.FileType('w'), default=sys.stdout) + parser.add_argument('-n', dest='func_name', help=""" + Function name to disassembly. Frame used if unset.""") args = parser.parse_args(shlex.split(command)) - args.file.write(exec_ctx.frame.disassembly) + if args.func_name is None: + args.file.write(exec_ctx.frame.disassembly) + else: + name = args.func_name + result = exec_ctx.target.FindFunctions(name) + if result is None: + raise RuntimeError('No function with name: {}'.format(name)) + if len(result) > 1: + errorStr = 'Matched multiple functions to name: {}' + raise RuntimeError(errorStr.format(name)) + f = result[0].GetFunction() + args.file.write(str(f.GetInstructions(exec_ctx.target)) + "\n") def sequence(debugger, command, exec_ctx, result, internal_dict): From 05b223aa0ba9a48fd787606fb54fd4597bcd15f8 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 6 Oct 2020 12:21:07 -0700 Subject: [PATCH 236/745] [Sema] Switch `typeCheckExpression` to use `ContextualTypeInfo` --- lib/Sema/CSApply.cpp | 6 +++--- lib/Sema/ConstraintSystem.h | 8 ++++++++ lib/Sema/DebuggerTestingTransform.cpp | 4 +++- lib/Sema/InstrumenterSupport.cpp | 2 +- lib/Sema/InstrumenterSupport.h | 2 +- lib/Sema/TypeCheckCodeCompletion.cpp | 4 ++-- lib/Sema/TypeCheckConstraints.cpp | 19 ++++++++++--------- lib/Sema/TypeCheckDecl.cpp | 6 +++--- lib/Sema/TypeCheckStmt.cpp | 25 +++++++++++-------------- lib/Sema/TypeCheckStorage.cpp | 4 +++- lib/Sema/TypeChecker.h | 10 ++++------ 11 files changed, 49 insertions(+), 41 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 8daffa99526cb..d2ddb7e4efe69 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -4296,7 +4296,7 @@ namespace { { Identifier() }); auto resultTy = TypeChecker::typeCheckExpression( - callExpr, cs.DC, valueType, CTP_CannotFail); + callExpr, cs.DC, /*contextualInfo=*/{valueType, CTP_CannotFail}); assert(resultTy && "Conversion cannot fail!"); (void)resultTy; @@ -8026,8 +8026,8 @@ static Optional applySolutionToForEachStmt( Expr *convertElementExpr = elementExpr; if (TypeChecker::typeCheckExpression( convertElementExpr, dc, - optPatternType, - CTP_CoerceOperand).isNull()) { + /*contextualInfo=*/{optPatternType, CTP_CoerceOperand}) + .isNull()) { return None; } elementExpr->setIsPlaceholder(false); diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index d9662107d1d48..a9935c657a533 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -908,6 +908,14 @@ struct ContextualTypeInfo { TypeLoc typeLoc; ContextualTypePurpose purpose; + ContextualTypeInfo() : typeLoc(TypeLoc()), purpose(CTP_Unused) {} + + ContextualTypeInfo(Type contextualTy, ContextualTypePurpose purpose) + : typeLoc(TypeLoc::withoutLoc(contextualTy)), purpose(purpose) {} + + ContextualTypeInfo(TypeLoc typeLoc, ContextualTypePurpose purpose) + : typeLoc(typeLoc), purpose(purpose) {} + Type getType() const { return typeLoc.getType(); } }; diff --git a/lib/Sema/DebuggerTestingTransform.cpp b/lib/Sema/DebuggerTestingTransform.cpp index 61484a57a0d77..172213baa8bd7 100644 --- a/lib/Sema/DebuggerTestingTransform.cpp +++ b/lib/Sema/DebuggerTestingTransform.cpp @@ -29,6 +29,7 @@ #include "swift/Subsystems.h" #include "TypeChecker.h" +#include "ConstraintSystem.h" using namespace swift; @@ -244,7 +245,8 @@ class DebuggerTestingTransform : public ASTWalker { // TODO: typeCheckExpression() seems to assign types to everything here, // but may not be sufficient in some cases. Expr *FinalExpr = ClosureCall; - if (!TypeChecker::typeCheckExpression(FinalExpr, getCurrentDeclContext())) + if (!TypeChecker::typeCheckExpression(FinalExpr, getCurrentDeclContext(), + /*contextualInfo=*/{})) llvm::report_fatal_error("Could not type-check instrumentation"); // Captures have to be computed after the closure is type-checked. This diff --git a/lib/Sema/InstrumenterSupport.cpp b/lib/Sema/InstrumenterSupport.cpp index 1041651892ff2..fae3b3149f152 100644 --- a/lib/Sema/InstrumenterSupport.cpp +++ b/lib/Sema/InstrumenterSupport.cpp @@ -119,7 +119,7 @@ bool InstrumenterBase::doTypeCheckImpl(ASTContext &Ctx, DeclContext *DC, DiagnosticSuppression suppression(Ctx.Diags); ErrorGatherer errorGatherer(Ctx.Diags); - TypeChecker::typeCheckExpression(parsedExpr, DC); + TypeChecker::typeCheckExpression(parsedExpr, DC, /*contextualInfo=*/{}); if (parsedExpr) { ErrorFinder errorFinder; diff --git a/lib/Sema/InstrumenterSupport.h b/lib/Sema/InstrumenterSupport.h index 1197a950eefc5..64a4235ab520e 100644 --- a/lib/Sema/InstrumenterSupport.h +++ b/lib/Sema/InstrumenterSupport.h @@ -16,7 +16,7 @@ //===----------------------------------------------------------------------===// #include "TypeChecker.h" - +#include "ConstraintSystem.h" #include "swift/AST/ASTWalker.h" namespace swift { diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index 789db2a2bb485..749e501064d76 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -962,8 +962,8 @@ bool swift::typeCheckExpression(DeclContext *DC, Expr *&parsedExpr) { parsedExpr = parsedExpr->walk(SanitizeExpr(ctx, /*shouldReusePrecheckedType=*/false)); DiagnosticSuppression suppression(ctx.Diags); - auto resultTy = TypeChecker::typeCheckExpression(parsedExpr, DC, Type(), - CTP_Unused); + auto resultTy = + TypeChecker::typeCheckExpression(parsedExpr, DC, /*contextualInfo=*/{}); return !resultTy; } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 71fb7c5d2a691..9509beac01062 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -300,11 +300,10 @@ void constraints::performSyntacticDiagnosticsForTarget( #pragma mark High-level entry points Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, - Type convertType, - ContextualTypePurpose convertTypePurpose, + ContextualTypeInfo contextualInfo, TypeCheckExprOptions options) { SolutionApplicationTarget target( - expr, dc, convertTypePurpose, convertType, + expr, dc, contextualInfo.purpose, contextualInfo.getType(), options.contains(TypeCheckExprFlags::IsDiscarded)); auto resultTarget = typeCheckExpression(target, options); if (!resultTarget) { @@ -422,9 +421,10 @@ Type TypeChecker::typeCheckParameterDefault(Expr *&defaultValue, DeclContext *DC, Type paramType, bool isAutoClosure) { assert(paramType && !paramType->hasError()); - return typeCheckExpression( - defaultValue, DC, paramType, - isAutoClosure ? CTP_AutoclosureDefaultParameter : CTP_DefaultParameter); + return typeCheckExpression(defaultValue, DC, /*contextualInfo=*/ + {paramType, isAutoClosure + ? CTP_AutoclosureDefaultParameter + : CTP_DefaultParameter}); } bool TypeChecker::typeCheckBinding( @@ -593,7 +593,8 @@ bool TypeChecker::typeCheckCondition(Expr *&expr, DeclContext *dc) { // If this expression is already typechecked and has type Bool, then just // re-typecheck it. if (expr->getType() && expr->getType()->isBool()) { - auto resultTy = TypeChecker::typeCheckExpression(expr, dc); + auto resultTy = + TypeChecker::typeCheckExpression(expr, dc, /*contextualInfo=*/{}); return !resultTy; } @@ -602,8 +603,8 @@ bool TypeChecker::typeCheckCondition(Expr *&expr, DeclContext *dc) { return true; auto resultTy = TypeChecker::typeCheckExpression( - expr, dc, boolDecl->getDeclaredInterfaceType(), - CTP_Condition); + expr, dc, + /*contextualInfo=*/{boolDecl->getDeclaredInterfaceType(), CTP_Condition}); return !resultTy; } diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 4bec788c5daea..b2d3e4da5b6a9 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -1199,9 +1199,9 @@ EnumRawValuesRequest::evaluate(Evaluator &eval, EnumDecl *ED, { Expr *exprToCheck = prevValue; - if (TypeChecker::typeCheckExpression(exprToCheck, ED, - rawTy, - CTP_EnumCaseRawValue)) { + if (TypeChecker::typeCheckExpression( + exprToCheck, ED, + /*contextualInfo=*/{rawTy, CTP_EnumCaseRawValue})) { TypeChecker::checkEnumElementEffects(elt, exprToCheck); } } diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index d68b37d5e2de6..104351090ae35 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -781,9 +781,8 @@ class StmtChecker : public StmtVisitor { } } - auto exprTy = TypeChecker::typeCheckExpression(E, DC, - ResultTy, - ctp, options); + auto exprTy = + TypeChecker::typeCheckExpression(E, DC, {ResultTy, ctp}, options); RS->setResult(E); if (!exprTy) { @@ -844,8 +843,7 @@ class StmtChecker : public StmtVisitor { } TypeChecker::typeCheckExpression(exprToCheck, DC, - contextType, - contextTypePurpose); + {contextType, contextTypePurpose}); // Propagate the change into the inout expression we stripped before. if (inout) { @@ -867,10 +865,9 @@ class StmtChecker : public StmtVisitor { Type exnType = getASTContext().getErrorDecl()->getDeclaredInterfaceType(); if (!exnType) return TS; - TypeChecker::typeCheckExpression(E, DC, exnType, - CTP_ThrowStmt); + TypeChecker::typeCheckExpression(E, DC, {exnType, CTP_ThrowStmt}); TS->setSubExpr(E); - + return TS; } @@ -885,7 +882,7 @@ class StmtChecker : public StmtVisitor { TypeChecker::typeCheckDecl(DS->getTempDecl()); Expr *theCall = DS->getCallExpr(); - TypeChecker::typeCheckExpression(theCall, DC); + TypeChecker::typeCheckExpression(theCall, DC, /*contextualInfo=*/{}); DS->setCallExpr(theCall); return DS; @@ -1159,7 +1156,8 @@ class StmtChecker : public StmtVisitor { Stmt *visitSwitchStmt(SwitchStmt *switchStmt) { // Type-check the subject expression. Expr *subjectExpr = switchStmt->getSubjectExpr(); - auto resultTy = TypeChecker::typeCheckExpression(subjectExpr, DC); + auto resultTy = TypeChecker::typeCheckExpression(subjectExpr, DC, + /*contextualInfo=*/{}); auto limitExhaustivityChecks = !resultTy; if (Expr *newSubjectExpr = TypeChecker::coerceToRValue(getASTContext(), subjectExpr)) @@ -1519,7 +1517,7 @@ void StmtChecker::typeCheckASTNode(ASTNode &node) { } auto resultTy = - TypeChecker::typeCheckExpression(E, DC, Type(), CTP_Unused, options); + TypeChecker::typeCheckExpression(E, DC, /*contextualInfo=*/{}, options); // If a closure expression is unused, the user might have intended to write // "do { ... }". @@ -1619,9 +1617,8 @@ static Expr* constructCallToSuperInit(ConstructorDecl *ctor, r = new (Context) TryExpr(SourceLoc(), r, Type(), /*implicit=*/true); DiagnosticSuppression suppression(ctor->getASTContext().Diags); - auto resultTy = - TypeChecker::typeCheckExpression(r, ctor, Type(), CTP_Unused, - TypeCheckExprFlags::IsDiscarded); + auto resultTy = TypeChecker::typeCheckExpression( + r, ctor, /*contextualInfo=*/{}, TypeCheckExprFlags::IsDiscarded); if (!resultTy) return nullptr; diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 5b3d658a718bd..87120eed8c01b 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -17,6 +17,7 @@ #include "CodeSynthesis.h" #include "TypeChecker.h" +#include "ConstraintSystem.h" #include "TypeCheckAvailability.h" #include "TypeCheckDecl.h" #include "TypeCheckType.h" @@ -916,7 +917,8 @@ static Expr *buildStorageReference(AccessorDecl *accessor, // FIXME: Since we're not resolving overloads or anything, we should be // building fully type-checked AST above; we already have all the // information that we need. - if (!TypeChecker::typeCheckExpression(lookupExpr, accessor)) + if (!TypeChecker::typeCheckExpression(lookupExpr, accessor, + /*contextualInfo=*/{})) return nullptr; // Make sure we produce an lvalue only when desired. diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 63a336eb239b8..df623b721556d 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -51,6 +51,7 @@ namespace constraints { class Solution; class SolutionApplicationTarget; class SolutionResult; + struct ContextualTypeInfo; } /// Special-case type checking semantics for certain declarations. @@ -549,21 +550,18 @@ Expr *findLHS(DeclContext *DC, Expr *E, Identifier name); /// \param expr The expression to type-check, which will be modified in /// place. /// -/// \param convertTypePurpose When convertType is specified, this indicates +/// \param contextualInfo The type that the expression is being converted to, +/// or null if the expression is standalone. When convertType is specified, this indicates /// what the conversion is doing. This allows diagnostics generation to /// produce more specific and helpful error messages when the conversion fails /// to be possible. /// -/// \param convertType The type that the expression is being converted to, -/// or null if the expression is standalone. -/// /// \param options Options that control how type checking is performed. /// /// \returns The type of the top-level expression, or Type() if an /// error occurred. Type typeCheckExpression(Expr *&expr, DeclContext *dc, - Type convertType = Type(), - ContextualTypePurpose convertTypePurpose = CTP_Unused, + constraints::ContextualTypeInfo contextualInfo, TypeCheckExprOptions options = TypeCheckExprOptions()); Optional From d5652fa9bf37b3bb6bfe0ec616ae220de2baff30 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Tue, 6 Oct 2020 12:26:47 -0700 Subject: [PATCH 237/745] =?UTF-8?q?[test]=20Don=E2=80=99t=20check=20for=20?= =?UTF-8?q?new=20behavior=20on=20older=20systems=20that=20don=E2=80=99t=20?= =?UTF-8?q?include=20the=20change?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/stdlib/TestData.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/stdlib/TestData.swift b/test/stdlib/TestData.swift index b3348142cc43e..3acb918a35742 100644 --- a/test/stdlib/TestData.swift +++ b/test/stdlib/TestData.swift @@ -1079,7 +1079,8 @@ class TestData : TestDataSuper { func test_rangeOfDataProtocol() { // https://bugs.swift.org/browse/SR-10689 - + guard #available(macOS 11, iOS 14, watchOS 7, tvOS 14, *) else { return } + let base = Data([0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03]) let subdata = base[10..<13] // [0x02, 0x03, 0x00] From d01e1ddd5399e797aa937bf75f5cd933aeb6e3e4 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 5 Oct 2020 17:55:14 -0400 Subject: [PATCH 238/745] Sema: Fix type of MemberRefExpr for a VarDecl with DynamicSelfType This fixes a regression from 33401ae147a04ce628cf246504b67729063358e5. Fixes . --- lib/Sema/CSApply.cpp | 11 ++++++++--- test/type/self.swift | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 8daffa99526cb..cfd5cc2a4f18e 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -1258,7 +1258,7 @@ namespace { } // For properties, build member references. - if (isa(member)) { + if (auto *varDecl = dyn_cast(member)) { if (isUnboundInstanceMember) { assert(memberLocator.getBaseLocator() && cs.UnevaluatedRootExprs.count( @@ -1271,17 +1271,22 @@ namespace { base->setImplicit(); } + auto hasDynamicSelf = + varDecl->getValueInterfaceType()->hasDynamicSelfType(); + auto memberRefExpr = new (context) MemberRefExpr(base, dotLoc, memberRef, memberLoc, Implicit, semantics); memberRefExpr->setIsSuper(isSuper); + + if (hasDynamicSelf) + refTy = refTy->replaceCovariantResultType(containerTy, 1); cs.setType(memberRefExpr, refTy->castTo()->getResult()); Expr *result = memberRefExpr; closeExistential(result, locator); - if (cast(member)->getValueInterfaceType() - ->hasDynamicSelfType()) { + if (hasDynamicSelf) { if (!baseTy->isEqual(containerTy)) { result = new (context) CovariantReturnConversionExpr( result, simplifyType(openedType)); diff --git a/test/type/self.swift b/test/type/self.swift index c4b13fb93dfcf..5b590d5edd722 100644 --- a/test/type/self.swift +++ b/test/type/self.swift @@ -299,3 +299,18 @@ struct OuterType { return optional.map { `Self`(string: $0) } } } + +// rdar://69804933 - CSApply assigns wrong type to MemberRefExpr for property with +// DynamicSelfType +class HasDynamicSelfProperty { + var me: Self { + return self + } +} + +// SILGen doesn't care about the MemberRefExpr's type, so it's hard to come up with an +// example that actually fails. Here, the rogue 'Self' type was diagnosed as being invalid +// in a stored property initializer. +class UsesDynamicSelfProperty { + var c = HasDynamicSelfProperty().me +} From 750735b6e28c92c3c7b553cfc59d78beaad2c519 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 6 Oct 2020 00:54:46 -0400 Subject: [PATCH 239/745] ASTDumper: Add a couple of hacks to avoid crashes in -dump-parse --- lib/AST/ASTDumper.cpp | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index dd12a1570f09b..5ca3bedb7794c 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -631,7 +631,10 @@ namespace { void visitExtensionDecl(ExtensionDecl *ED) { printCommon(ED, "extension_decl", ExtensionColor); OS << ' '; - ED->getExtendedType().print(OS); + if (ED->hasBeenBound()) + ED->getExtendedType().print(OS); + else + ED->getExtendedTypeRepr()->print(OS); printCommonPost(ED); } @@ -854,20 +857,22 @@ namespace { if (D->isStatic()) PrintWithColorRAII(OS, DeclModifierColor) << " type"; - auto impl = D->getImplInfo(); - PrintWithColorRAII(OS, DeclModifierColor) - << " readImpl=" - << getReadImplKindName(impl.getReadImpl()); - if (!impl.supportsMutation()) { - PrintWithColorRAII(OS, DeclModifierColor) - << " immutable"; - } else { + if (D->hasInterfaceType()) { + auto impl = D->getImplInfo(); PrintWithColorRAII(OS, DeclModifierColor) - << " writeImpl=" - << getWriteImplKindName(impl.getWriteImpl()); - PrintWithColorRAII(OS, DeclModifierColor) - << " readWriteImpl=" - << getReadWriteImplKindName(impl.getReadWriteImpl()); + << " readImpl=" + << getReadImplKindName(impl.getReadImpl()); + if (!impl.supportsMutation()) { + PrintWithColorRAII(OS, DeclModifierColor) + << " immutable"; + } else { + PrintWithColorRAII(OS, DeclModifierColor) + << " writeImpl=" + << getWriteImplKindName(impl.getWriteImpl()); + PrintWithColorRAII(OS, DeclModifierColor) + << " readWriteImpl=" + << getReadWriteImplKindName(impl.getReadWriteImpl()); + } } } From 0e276456bda5db63b201ba372e6866833f22fdda Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 5 Oct 2020 20:36:25 -0400 Subject: [PATCH 240/745] AST: The body of a GuardStmt is always a BraceStmt --- include/swift/AST/Stmt.h | 10 +++++----- lib/AST/ASTWalker.cpp | 2 +- lib/AST/Stmt.cpp | 2 +- lib/Sema/PCMacro.cpp | 4 ++-- lib/Sema/PlaygroundTransform.cpp | 4 ++-- lib/Sema/TypeCheckStmt.cpp | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index 258ebe5e7e4d6..8414cf4fbbd35 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -632,17 +632,17 @@ class IfStmt : public LabeledConditionalStmt { /// class GuardStmt : public LabeledConditionalStmt { SourceLoc GuardLoc; - Stmt *Body; + BraceStmt *Body; public: GuardStmt(SourceLoc GuardLoc, StmtCondition Cond, - Stmt *Body, Optional implicit = None) + BraceStmt *Body, Optional implicit = None) : LabeledConditionalStmt(StmtKind::Guard, getDefaultImplicitFlag(implicit, GuardLoc), LabeledStmtInfo(), Cond), GuardLoc(GuardLoc), Body(Body) {} - GuardStmt(SourceLoc GuardLoc, Expr *Cond, Stmt *Body, + GuardStmt(SourceLoc GuardLoc, Expr *Cond, BraceStmt *Body, Optional implicit, ASTContext &Ctx); SourceLoc getGuardLoc() const { return GuardLoc; } @@ -654,8 +654,8 @@ class GuardStmt : public LabeledConditionalStmt { return Body->getEndLoc(); } - Stmt *getBody() const { return Body; } - void setBody(Stmt *s) { Body = s; } + BraceStmt *getBody() const { return Body; } + void setBody(BraceStmt *s) { Body = s; } // Implement isa/cast/dyncast/etc. static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Guard; } diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 900afa1baf7aa..808e20187f3ec 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -1478,7 +1478,7 @@ Stmt *Traversal::visitGuardStmt(GuardStmt *US) { if (doIt(US->getCond())) return nullptr; - if (Stmt *S2 = doIt(US->getBody())) + if (BraceStmt *S2 = cast_or_null(doIt(US->getBody()))) US->setBody(S2); else return nullptr; diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 98541beee21a4..8966bb6edb87c 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -380,7 +380,7 @@ IfStmt::IfStmt(SourceLoc IfLoc, Expr *Cond, Stmt *Then, SourceLoc ElseLoc, implicit) { } -GuardStmt::GuardStmt(SourceLoc GuardLoc, Expr *Cond, Stmt *Body, +GuardStmt::GuardStmt(SourceLoc GuardLoc, Expr *Cond, BraceStmt *Body, Optional implicit, ASTContext &Ctx) : GuardStmt(GuardLoc, exprToCond(Cond, Ctx), Body, implicit) { diff --git a/lib/Sema/PCMacro.cpp b/lib/Sema/PCMacro.cpp index 8750a9d968a33..75fb221cc3147 100644 --- a/lib/Sema/PCMacro.cpp +++ b/lib/Sema/PCMacro.cpp @@ -187,8 +187,8 @@ class Instrumenter : InstrumenterBase { transformStmtCondition(SC, GS->getStartLoc()); GS->setCond(SC); - if (Stmt *BS = GS->getBody()) - GS->setBody(transformStmt(BS)); + if (BraceStmt *BS = GS->getBody()) + GS->setBody(transformBraceStmt(BS)); return GS; } diff --git a/lib/Sema/PlaygroundTransform.cpp b/lib/Sema/PlaygroundTransform.cpp index 6bf259c4a1aeb..610f48b7e56cc 100644 --- a/lib/Sema/PlaygroundTransform.cpp +++ b/lib/Sema/PlaygroundTransform.cpp @@ -204,8 +204,8 @@ class Instrumenter : InstrumenterBase { } GuardStmt *transformGuardStmt(GuardStmt *GS) { - if (Stmt *BS = GS->getBody()) - GS->setBody(transformStmt(BS)); + if (BraceStmt *BS = GS->getBody()) + GS->setBody(transformBraceStmt(BS)); return GS; } diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index d68b37d5e2de6..5d90ef00d0723 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -912,7 +912,7 @@ class StmtChecker : public StmtVisitor { Stmt *visitGuardStmt(GuardStmt *GS) { typeCheckConditionForStatement(GS, DC); - Stmt *S = GS->getBody(); + BraceStmt *S = GS->getBody(); typeCheckStmt(S); GS->setBody(S); return GS; From faae25a8bbb19a641ef6f286ada3a29ac7991287 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 6 Oct 2020 02:12:55 -0400 Subject: [PATCH 241/745] AST: The body of a CaseStmt is always a BraceStmt --- include/swift/AST/Stmt.h | 10 +++++----- lib/AST/ASTWalker.cpp | 2 +- lib/AST/Stmt.cpp | 4 ++-- lib/Sema/TypeCheckStmt.cpp | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index 8414cf4fbbd35..6fb2fe9e56bc4 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -945,13 +945,13 @@ class CaseStmt final SourceLoc ItemTerminatorLoc; CaseParentKind ParentKind; - llvm::PointerIntPair BodyAndHasFallthrough; + llvm::PointerIntPair BodyAndHasFallthrough; Optional> CaseBodyVariables; CaseStmt(CaseParentKind ParentKind, SourceLoc ItemIntroducerLoc, ArrayRef CaseLabelItems, SourceLoc UnknownAttrLoc, - SourceLoc ItemTerminatorLoc, Stmt *Body, + SourceLoc ItemTerminatorLoc, BraceStmt *Body, Optional> CaseBodyVariables, Optional Implicit, NullablePtr fallthroughStmt); @@ -960,7 +960,7 @@ class CaseStmt final static CaseStmt * create(ASTContext &C, CaseParentKind ParentKind, SourceLoc ItemIntroducerLoc, ArrayRef CaseLabelItems, SourceLoc UnknownAttrLoc, - SourceLoc ItemTerminatorLoc, Stmt *Body, + SourceLoc ItemTerminatorLoc, BraceStmt *Body, Optional> CaseBodyVariables, Optional Implicit = None, NullablePtr fallthroughStmt = nullptr); @@ -997,8 +997,8 @@ class CaseStmt final bool hasFallthroughDest() const { return BodyAndHasFallthrough.getInt(); } - Stmt *getBody() const { return BodyAndHasFallthrough.getPointer(); } - void setBody(Stmt *body) { BodyAndHasFallthrough.setPointer(body); } + BraceStmt *getBody() const { return BodyAndHasFallthrough.getPointer(); } + void setBody(BraceStmt *body) { BodyAndHasFallthrough.setPointer(body); } /// True if the case block declares any patterns with local variable bindings. bool hasBoundDecls() const { return CaseBodyVariables.hasValue(); } diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 808e20187f3ec..4dbbe68af288a 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -1631,7 +1631,7 @@ Stmt *Traversal::visitCaseStmt(CaseStmt *S) { } } - if (Stmt *newBody = doIt(S->getBody())) + if (BraceStmt *newBody = cast_or_null(doIt(S->getBody()))) S->setBody(newBody); else return nullptr; diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 8966bb6edb87c..65fff23a3415a 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -407,7 +407,7 @@ SourceLoc CaseLabelItem::getEndLoc() const { CaseStmt::CaseStmt(CaseParentKind parentKind, SourceLoc itemIntroducerLoc, ArrayRef caseLabelItems, SourceLoc unknownAttrLoc, SourceLoc itemTerminatorLoc, - Stmt *body, + BraceStmt *body, Optional> caseBodyVariables, Optional implicit, NullablePtr fallthroughStmt) @@ -445,7 +445,7 @@ CaseStmt *CaseStmt::create(ASTContext &ctx, CaseParentKind ParentKind, SourceLoc caseLoc, ArrayRef caseLabelItems, SourceLoc unknownAttrLoc, SourceLoc colonLoc, - Stmt *body, + BraceStmt *body, Optional> caseVarDecls, Optional implicit, NullablePtr fallthroughStmt) { diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 5d90ef00d0723..f7f3be9a7d1e8 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1150,7 +1150,7 @@ class StmtChecker : public StmtVisitor { getASTContext(), caseBlock, limitExhaustivityChecks); } - Stmt *body = caseBlock->getBody(); + BraceStmt *body = caseBlock->getBody(); limitExhaustivityChecks |= typeCheckStmt(body); caseBlock->setBody(body); } From da1c5c99a90b8a87e1b92e63c71d5d65d156f4ae Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 6 Oct 2020 00:56:59 -0400 Subject: [PATCH 242/745] Parse: Fix repeat/while error recovery path We were creating an ErrorExpr whose source range overlaps with the following statement, which is now flagged by ASTScope. --- lib/Parse/ParseDecl.cpp | 2 +- lib/Parse/ParseStmt.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 412d3f57890f4..614eaa1ccbd68 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -6000,7 +6000,7 @@ Parser::parseDeclVar(ParseDeclOptions Flags, // up in Decls to be returned to caller. if (topLevelDecl) { PBD->setDeclContext(topLevelDecl); - auto range = PBD->getSourceRange(); + auto range = PBD->getSourceRangeIncludingAttrs(); topLevelDecl->setBody(BraceStmt::create(Context, range.Start, ASTNode(PBD), range.End, true)); Decls.insert(Decls.begin()+NumDeclsInResult, topLevelDecl); diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 090c05110529d..bc856a1c7e7e3 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1876,9 +1876,8 @@ ParserResult Parser::parseStmtRepeat(LabeledStmtInfo labelInfo) { ParserResult condition; if (Tok.is(tok::l_brace)) { - SourceLoc lbraceLoc = Tok.getLoc(); diagnose(whileLoc, diag::missing_condition_after_while); - condition = makeParserErrorResult(new (Context) ErrorExpr(lbraceLoc)); + condition = makeParserErrorResult(new (Context) ErrorExpr(whileLoc)); } else { condition = parseExpr(diag::expected_expr_repeat_while); status |= condition; From 68d72780f11046d5a6eed5818f8fe4d19506fa9e Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 6 Oct 2020 12:45:04 -0700 Subject: [PATCH 243/745] [ConstraintSystem] NFC: Move implementation of `isReadOnlyKeyPathComponent` to .cpp --- lib/Sema/ConstraintSystem.cpp | 37 +++++++++++++++++++++++++++++++++++ lib/Sema/ConstraintSystem.h | 36 +--------------------------------- 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 730de34b54935..53ff93528dbea 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -20,6 +20,7 @@ #include "CSDiagnostics.h" #include "CSFix.h" #include "SolutionResult.h" +#include "TypeChecker.h" #include "TypeCheckType.h" #include "swift/AST/Initializer.h" #include "swift/AST/GenericEnvironment.h" @@ -5215,3 +5216,39 @@ Type ConstraintSystem::getVarType(const VarDecl *var) { return HoleType::get(Context, const_cast(var)); }); } + +bool ConstraintSystem::isReadOnlyKeyPathComponent( + const AbstractStorageDecl *storage, SourceLoc referenceLoc) { + // See whether key paths can store to this component. (Key paths don't + // get any special power from being formed in certain contexts, such + // as the ability to assign to `let`s in initialization contexts, so + // we pass null for the DC to `isSettable` here.) + if (!getASTContext().isSwiftVersionAtLeast(5)) { + // As a source-compatibility measure, continue to allow + // WritableKeyPaths to be formed in the same conditions we did + // in previous releases even if we should not be able to set + // the value in this context. + if (!storage->isSettable(DC)) { + // A non-settable component makes the key path read-only, unless + // a reference-writable component shows up later. + return true; + } + } else if (!storage->isSettable(nullptr) || + !storage->isSetterAccessibleFrom(DC)) { + // A non-settable component makes the key path read-only, unless + // a reference-writable component shows up later. + return true; + } + + // If the setter is unavailable, then the keypath ought to be read-only + // in this context. + if (auto setter = storage->getOpaqueAccessor(AccessorKind::Set)) { + auto maybeUnavail = + TypeChecker::checkDeclarationAvailability(setter, referenceLoc, DC); + if (maybeUnavail.hasValue()) { + return true; + } + } + + return false; +} diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index a9935c657a533..89b7c51d38549 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -4971,41 +4971,7 @@ class ConstraintSystem { llvm::function_ref pred); bool isReadOnlyKeyPathComponent(const AbstractStorageDecl *storage, - SourceLoc referenceLoc) { - // See whether key paths can store to this component. (Key paths don't - // get any special power from being formed in certain contexts, such - // as the ability to assign to `let`s in initialization contexts, so - // we pass null for the DC to `isSettable` here.) - if (!getASTContext().isSwiftVersionAtLeast(5)) { - // As a source-compatibility measure, continue to allow - // WritableKeyPaths to be formed in the same conditions we did - // in previous releases even if we should not be able to set - // the value in this context. - if (!storage->isSettable(DC)) { - // A non-settable component makes the key path read-only, unless - // a reference-writable component shows up later. - return true; - } - } else if (!storage->isSettable(nullptr) || - !storage->isSetterAccessibleFrom(DC)) { - // A non-settable component makes the key path read-only, unless - // a reference-writable component shows up later. - return true; - } - - // If the setter is unavailable, then the keypath ought to be read-only - // in this context. - if (auto setter = storage->getOpaqueAccessor(AccessorKind::Set)) { - auto maybeUnavail = TypeChecker::checkDeclarationAvailability(setter, - referenceLoc, - DC); - if (maybeUnavail.hasValue()) { - return true; - } - } - - return false; - } + SourceLoc referenceLoc); public: // Given a type variable, attempt to find the disjunction of From 00d8e3c2cd9122eeae465ecaa7e428f557041cc1 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 6 Oct 2020 12:57:16 -0700 Subject: [PATCH 244/745] XFAIL Cross-Module Build Tests On Windows It doesn't seem to like the output file maps I've written here. --- test/Incremental/CrossModule/linear.swift | 3 +++ test/Incremental/CrossModule/transitive.swift | 3 +++ 2 files changed, 6 insertions(+) diff --git a/test/Incremental/CrossModule/linear.swift b/test/Incremental/CrossModule/linear.swift index 73ae072d2ff03..eada05f2cc737 100644 --- a/test/Incremental/CrossModule/linear.swift +++ b/test/Incremental/CrossModule/linear.swift @@ -1,6 +1,9 @@ // RUN: %empty-directory(%t) // RUN: cp -r %S/Inputs/linear/* %t +// rdar://problem/70012853 +// XFAIL: OS=windows-msvc + // // This test establishes a "linear" chain of modules that import one another // and ensures that a cross-module incremental build does not needlessly diff --git a/test/Incremental/CrossModule/transitive.swift b/test/Incremental/CrossModule/transitive.swift index f281f1e8e014f..002d4279a7406 100644 --- a/test/Incremental/CrossModule/transitive.swift +++ b/test/Incremental/CrossModule/transitive.swift @@ -1,6 +1,9 @@ // RUN: %empty-directory(%t) // RUN: cp -r %S/Inputs/transitive/* %t +// rdar://problem/70012853 +// XFAIL: OS=windows-msvc + // // This test establishes a "transitive" chain of modules that import one another // and ensures that a cross-module incremental build rebuilds all modules From f94be56468d10ff9d93d8ba3a34263bc958c936c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 6 Oct 2020 13:18:39 -0700 Subject: [PATCH 245/745] [Sema] Decouple ConstraintSystem and TypeChecker headers --- lib/Sema/CSBindings.cpp | 1 + lib/Sema/CSClosure.cpp | 2 ++ lib/Sema/CSDiagnostics.h | 1 + lib/Sema/CSGen.cpp | 1 + lib/Sema/CSRanking.cpp | 1 + lib/Sema/CSSolver.cpp | 1 + lib/Sema/ConstraintSystem.h | 28 +++++++++++++++++++++++++--- 7 files changed, 32 insertions(+), 3 deletions(-) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index a187f8cd51a28..0c6e07e2004a5 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "ConstraintGraph.h" #include "ConstraintSystem.h" +#include "TypeChecker.h" #include "llvm/ADT/SetVector.h" #include diff --git a/lib/Sema/CSClosure.cpp b/lib/Sema/CSClosure.cpp index 228fa555b20e8..529721a921bb5 100644 --- a/lib/Sema/CSClosure.cpp +++ b/lib/Sema/CSClosure.cpp @@ -17,6 +17,8 @@ //===----------------------------------------------------------------------===// #include "ConstraintSystem.h" +#include "TypeChecker.h" + using namespace swift; using namespace swift::constraints; diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 4889dbeab30dc..6a4283e19dc51 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -19,6 +19,7 @@ #include "Constraint.h" #include "ConstraintSystem.h" #include "OverloadChoice.h" +#include "TypeChecker.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" #include "swift/AST/Decl.h" diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 5d15df3ada4f4..e37c5faa13bda 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -16,6 +16,7 @@ #include "ConstraintGraph.h" #include "ConstraintSystem.h" #include "TypeCheckType.h" +#include "TypeChecker.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/Expr.h" diff --git a/lib/Sema/CSRanking.cpp b/lib/Sema/CSRanking.cpp index cc57a75def2c9..2aaf110138b03 100644 --- a/lib/Sema/CSRanking.cpp +++ b/lib/Sema/CSRanking.cpp @@ -15,6 +15,7 @@ // //===----------------------------------------------------------------------===// #include "ConstraintSystem.h" +#include "TypeChecker.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 81c86b550652a..6621372fbd07f 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -18,6 +18,7 @@ #include "ConstraintSystem.h" #include "SolutionResult.h" #include "TypeCheckType.h" +#include "TypeChecker.h" #include "swift/AST/ParameterList.h" #include "swift/AST/TypeWalker.h" #include "llvm/ADT/STLExtras.h" diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 89b7c51d38549..9de342031ac2b 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -24,16 +24,20 @@ #include "ConstraintGraphScope.h" #include "ConstraintLocator.h" #include "OverloadChoice.h" -#include "TypeChecker.h" +#include "SolutionResult.h" +#include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" +#include "swift/AST/AnyFunctionRef.h" +#include "swift/AST/DiagnosticsSema.h" #include "swift/AST/NameLookup.h" #include "swift/AST/PropertyWrappers.h" #include "swift/AST/Types.h" #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/OptionSet.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetOperations.h" @@ -49,15 +53,32 @@ namespace swift { class Expr; +class FuncDecl; +class BraseStmt; +enum class TypeCheckExprFlags; namespace constraints { class ConstraintGraph; class ConstraintGraphNode; class ConstraintSystem; +class SolutionApplicationTarget; } // end namespace constraints +// Forward declare some TypeChecker related functions +// so they could be made friends of ConstraintSystem. +namespace TypeChecker { + +Optional applyFunctionBuilderBodyTransform(FuncDecl *func, + Type builderType); + +Optional +typeCheckExpression(constraints::SolutionApplicationTarget &target, + OptionSet options); + +} // end namespace TypeChecker + } // end namespace swift /// Allocate memory within the given constraint system. @@ -2730,9 +2751,10 @@ class ConstraintSystem { friend Optional swift::TypeChecker::applyFunctionBuilderBodyTransform(FuncDecl *func, Type builderType); + friend Optional - swift::TypeChecker::typeCheckExpression(SolutionApplicationTarget &target, - TypeCheckExprOptions options); + swift::TypeChecker::typeCheckExpression( + SolutionApplicationTarget &target, OptionSet options); /// Emit the fixes computed as part of the solution, returning true if we were /// able to emit an error message, or false if none of the fixits worked out. From 0a21c4d96ff1dab4be29fe4a53b7fdac9d3c6c7d Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Tue, 6 Oct 2020 13:37:05 -0700 Subject: [PATCH 246/745] Fix another use-after-free in SILCombine (#34168) * Fix another use-after-free in SILCombine swift::endLifetimeAtFrontier also needs to use swift::emitDestroyOperation and delete instructions via callbacks that can correctly remove it from the worklist that SILCombine maintains * Add test for use-after-free in SILCombine --- .../swift/SILOptimizer/Utils/ValueLifetime.h | 6 +++-- lib/SILOptimizer/Utils/InstOptUtils.cpp | 8 +++--- .../Utils/PartialApplyCombiner.cpp | 2 +- lib/SILOptimizer/Utils/ValueLifetime.cpp | 9 +++---- test/SILOptimizer/sil_combine_apply.sil | 26 +++++++++++++++++++ 5 files changed, 39 insertions(+), 12 deletions(-) diff --git a/include/swift/SILOptimizer/Utils/ValueLifetime.h b/include/swift/SILOptimizer/Utils/ValueLifetime.h index 92af2083ef62f..9fe60c1bbfb43 100644 --- a/include/swift/SILOptimizer/Utils/ValueLifetime.h +++ b/include/swift/SILOptimizer/Utils/ValueLifetime.h @@ -17,8 +17,9 @@ #ifndef SWIFT_SILOPTIMIZER_UTILS_CFG_H #define SWIFT_SILOPTIMIZER_UTILS_CFG_H -#include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILBuilder.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SILOptimizer/Utils/InstOptUtils.h" namespace swift { @@ -159,7 +160,8 @@ class ValueLifetimeAnalysis { /// destroy_value at each instruction of the \p frontier. void endLifetimeAtFrontier(SILValue valueOrStackLoc, const ValueLifetimeAnalysis::Frontier &frontier, - SILBuilderContext &builderCtxt); + SILBuilderContext &builderCtxt, + InstModCallbacks callbacks); } // end namespace swift diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index 91e75def71ffb..f8691786ebb97 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -1323,7 +1323,8 @@ bool swift::collectDestroys(SingleValueInstruction *inst, /// not be needed anymore with OSSA. static bool keepArgsOfPartialApplyAlive(PartialApplyInst *pai, ArrayRef paiUsers, - SILBuilderContext &builderCtxt) { + SILBuilderContext &builderCtxt, + InstModCallbacks callbacks) { SmallVector argsToHandle; getConsumedPartialApplyArgs(pai, argsToHandle, /*includeTrivialAddrArgs*/ false); @@ -1357,7 +1358,7 @@ static bool keepArgsOfPartialApplyAlive(PartialApplyInst *pai, // Delay the destroy of the value (either as SSA value or in the stack- // allocated temporary) at the end of the partial_apply's lifetime. - endLifetimeAtFrontier(tmp, partialApplyFrontier, builderCtxt); + endLifetimeAtFrontier(tmp, partialApplyFrontier, builderCtxt, callbacks); } return true; } @@ -1406,7 +1407,8 @@ bool swift::tryDeleteDeadClosure(SingleValueInstruction *closure, "partial_apply [stack] should have been handled before"); SILBuilderContext builderCtxt(pai->getModule()); if (needKeepArgsAlive) { - if (!keepArgsOfPartialApplyAlive(pai, closureDestroys, builderCtxt)) + if (!keepArgsOfPartialApplyAlive(pai, closureDestroys, builderCtxt, + callbacks)) return false; } else { // A preceeding partial_apply -> apply conversion (done in diff --git a/lib/SILOptimizer/Utils/PartialApplyCombiner.cpp b/lib/SILOptimizer/Utils/PartialApplyCombiner.cpp index d9a36eb051e16..4d662e974d1b8 100644 --- a/lib/SILOptimizer/Utils/PartialApplyCombiner.cpp +++ b/lib/SILOptimizer/Utils/PartialApplyCombiner.cpp @@ -115,7 +115,7 @@ bool PartialApplyCombiner::copyArgsToTemporaries( // Destroy the argument value (either as SSA value or in the stack- // allocated temporary) at the end of the partial_apply's lifetime. - endLifetimeAtFrontier(tmp, partialApplyFrontier, builderCtxt); + endLifetimeAtFrontier(tmp, partialApplyFrontier, builderCtxt, callbacks); } return true; } diff --git a/lib/SILOptimizer/Utils/ValueLifetime.cpp b/lib/SILOptimizer/Utils/ValueLifetime.cpp index 4f90e9cc0bd80..55c0e05625a87 100644 --- a/lib/SILOptimizer/Utils/ValueLifetime.cpp +++ b/lib/SILOptimizer/Utils/ValueLifetime.cpp @@ -327,15 +327,12 @@ void ValueLifetimeAnalysis::dump() const { void swift::endLifetimeAtFrontier( SILValue valueOrStackLoc, const ValueLifetimeAnalysis::Frontier &frontier, - SILBuilderContext &builderCtxt) { + SILBuilderContext &builderCtxt, InstModCallbacks callbacks) { for (SILInstruction *endPoint : frontier) { SILBuilderWithScope builder(endPoint, builderCtxt); SILLocation loc = RegularLocation(endPoint->getLoc().getSourceLoc()); - if (valueOrStackLoc->getType().isObject()) { - builder.emitDestroyValueOperation(loc, valueOrStackLoc); - } else { - assert(isa(valueOrStackLoc)); - builder.createDestroyAddr(loc, valueOrStackLoc); + emitDestroyOperation(builder, loc, valueOrStackLoc, callbacks); + if (isa(valueOrStackLoc)) { builder.createDeallocStack(loc, valueOrStackLoc); } } diff --git a/test/SILOptimizer/sil_combine_apply.sil b/test/SILOptimizer/sil_combine_apply.sil index 05d275d17913a..fcb3d1060684f 100644 --- a/test/SILOptimizer/sil_combine_apply.sil +++ b/test/SILOptimizer/sil_combine_apply.sil @@ -991,3 +991,29 @@ sil @test_partial_apply_method : $@convention(thin) () -> @owned @callee_owned ( // CHECK: [[FN:%.*]] = function_ref @method // CHECK-NEXT: partial_apply [[FN]]() // CHECK-NEXT: return + +sil [noinline] @$foo : $@convention(thin) (@owned { var Int64 }) -> () + +// CHECK-LABEL: sil private [noinline] @$testdeadpartialapply : +// CHECK-NOT: partial_apply +// CHECK-LABEL: } // end sil function '$testdeadpartialapply' +sil private [noinline] @$testdeadpartialapply : $@convention(thin) (@owned { var Int64 }) -> () { +bb0(%0 : ${ var Int64 }): + %3 = function_ref @$foo : $@convention(thin) (@owned { var Int64 }) -> () + %5 = partial_apply [callee_guaranteed] %3(%0) : $@convention(thin) (@owned { var Int64 }) -> () + cond_br undef, bb1, bb2 +bb1: + strong_retain %0 : ${ var Int64 } + strong_retain %5 : $@callee_guaranteed () -> () + unreachable +bb2: + strong_retain %0 : ${ var Int64 } + strong_retain %5 : $@callee_guaranteed () -> () + br bb3 +bb3: + strong_release %5 : $@callee_guaranteed () -> () + strong_release %5 : $@callee_guaranteed () -> () + strong_release %0 : ${ var Int64 } + %rv = tuple() + return %rv : $() +} From e60a68e1eead0c29a178277aec5e504b4374d1a1 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 6 Oct 2020 14:03:56 -0700 Subject: [PATCH 247/745] Generalize test harder --- test/Misc/stats_dir_tracer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Misc/stats_dir_tracer.swift b/test/Misc/stats_dir_tracer.swift index 6f91c24f864fe..e62c19b7900fd 100644 --- a/test/Misc/stats_dir_tracer.swift +++ b/test/Misc/stats_dir_tracer.swift @@ -2,7 +2,7 @@ // RUN: %target-swiftc_driver -o %t/main -module-name main -stats-output-dir %t %s -trace-stats-events // RUN: %FileCheck -input-file %t/*.csv %s -// CHECK-DAG: {{[0-9]+,[0-9]+,"exit","check-conformance","Frontend.NumInstructionsExecuted",[0-9]+,[0-9]+,"Bar: Proto module main",".*stats_dir_tracer.swift.*"}} +// CHECK-DAG: {{[0-9]+,[0-9]+,"exit","SelfBoundsFromWhereClauseRequest","Sema.SelfBoundsFromWhereClauseRequest",[0-9]+,[0-9]+,"Proto",".*stats_dir_tracer.swift.*"}} // CHECK-DAG: {{[0-9]+,[0-9]+,"exit","SuperclassDeclRequest","Sema.SuperclassDeclRequest",[0-9]+,[0-9]+,"Bar","\[.*stats_dir_tracer.swift.*\]"}} public func foo() { From 5c3814e4dd0f3d0cd0a4776dbaebd3751bc8e985 Mon Sep 17 00:00:00 2001 From: Josh Learn Date: Tue, 6 Oct 2020 11:43:13 -0700 Subject: [PATCH 248/745] [OSSignpost] Update apinotes to allow usage of os_signpost ABI entrypoint Currently, the `_os_signpost_emit_with_name_impl` function is not available to be called from Swift. This is the main ABI entrypoint for making os_signpost calls. In order to facilitate more efficient calls to os_signpost in future iterations of the Swift os_signpost API, we need to allow calling this function from Swift. rdar://70015938 --- apinotes/os.apinotes | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apinotes/os.apinotes b/apinotes/os.apinotes index 3b51bfb8cf43e..c0f6364e19575 100644 --- a/apinotes/os.apinotes +++ b/apinotes/os.apinotes @@ -63,5 +63,5 @@ Functions: SwiftPrivate: true NullabilityOfRet: O - Name: _os_signpost_emit_with_name_impl - Availability: nonswift - AvailabilityMsg: 'Use os_signpost' + SwiftPrivate: true + NullabilityOfRet: O From 0b648fb455057eb8c51bff34fc7d87447cd9b62e Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 6 Oct 2020 15:42:40 -0700 Subject: [PATCH 249/745] [TypeChecker] NFC: Include constraint system into TypeChecker header Avoids having to include ConstraintSystem.h in TypeCheck*.cpp files to gain access to ContextualInfo and related classes. --- lib/Sema/DebuggerTestingTransform.cpp | 1 - lib/Sema/InstrumenterSupport.h | 1 - lib/Sema/TypeCheckCodeCompletion.cpp | 3 +-- lib/Sema/TypeCheckConstraints.cpp | 2 +- lib/Sema/TypeCheckDecl.cpp | 1 - lib/Sema/TypeCheckStmt.cpp | 6 ++---- lib/Sema/TypeCheckStorage.cpp | 4 +--- lib/Sema/TypeChecker.h | 4 ++-- 8 files changed, 7 insertions(+), 15 deletions(-) diff --git a/lib/Sema/DebuggerTestingTransform.cpp b/lib/Sema/DebuggerTestingTransform.cpp index 172213baa8bd7..d9057d6cb69d2 100644 --- a/lib/Sema/DebuggerTestingTransform.cpp +++ b/lib/Sema/DebuggerTestingTransform.cpp @@ -29,7 +29,6 @@ #include "swift/Subsystems.h" #include "TypeChecker.h" -#include "ConstraintSystem.h" using namespace swift; diff --git a/lib/Sema/InstrumenterSupport.h b/lib/Sema/InstrumenterSupport.h index 64a4235ab520e..dc009b40f1e99 100644 --- a/lib/Sema/InstrumenterSupport.h +++ b/lib/Sema/InstrumenterSupport.h @@ -16,7 +16,6 @@ //===----------------------------------------------------------------------===// #include "TypeChecker.h" -#include "ConstraintSystem.h" #include "swift/AST/ASTWalker.h" namespace swift { diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index 749e501064d76..08f0c42dc3557 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -962,8 +962,7 @@ bool swift::typeCheckExpression(DeclContext *DC, Expr *&parsedExpr) { parsedExpr = parsedExpr->walk(SanitizeExpr(ctx, /*shouldReusePrecheckedType=*/false)); DiagnosticSuppression suppression(ctx.Diags); - auto resultTy = - TypeChecker::typeCheckExpression(parsedExpr, DC, /*contextualInfo=*/{}); + auto resultTy = TypeChecker::typeCheckExpression(parsedExpr, DC); return !resultTy; } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 9509beac01062..f73bb7fb8feaa 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -594,7 +594,7 @@ bool TypeChecker::typeCheckCondition(Expr *&expr, DeclContext *dc) { // re-typecheck it. if (expr->getType() && expr->getType()->isBool()) { auto resultTy = - TypeChecker::typeCheckExpression(expr, dc, /*contextualInfo=*/{}); + TypeChecker::typeCheckExpression(expr, dc); return !resultTy; } diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index b2d3e4da5b6a9..b58de0fcb7255 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -15,7 +15,6 @@ //===----------------------------------------------------------------------===// #include "CodeSynthesis.h" -#include "ConstraintSystem.h" #include "DerivedConformances.h" #include "TypeChecker.h" #include "TypeCheckAccess.h" diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 104351090ae35..fb4e8507e10b5 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -18,7 +18,6 @@ #include "TypeCheckAvailability.h" #include "TypeCheckType.h" #include "MiscDiagnostics.h" -#include "ConstraintSystem.h" #include "swift/Subsystems.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/ASTWalker.h" @@ -882,7 +881,7 @@ class StmtChecker : public StmtVisitor { TypeChecker::typeCheckDecl(DS->getTempDecl()); Expr *theCall = DS->getCallExpr(); - TypeChecker::typeCheckExpression(theCall, DC, /*contextualInfo=*/{}); + TypeChecker::typeCheckExpression(theCall, DC); DS->setCallExpr(theCall); return DS; @@ -1156,8 +1155,7 @@ class StmtChecker : public StmtVisitor { Stmt *visitSwitchStmt(SwitchStmt *switchStmt) { // Type-check the subject expression. Expr *subjectExpr = switchStmt->getSubjectExpr(); - auto resultTy = TypeChecker::typeCheckExpression(subjectExpr, DC, - /*contextualInfo=*/{}); + auto resultTy = TypeChecker::typeCheckExpression(subjectExpr, DC); auto limitExhaustivityChecks = !resultTy; if (Expr *newSubjectExpr = TypeChecker::coerceToRValue(getASTContext(), subjectExpr)) diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 87120eed8c01b..5b3d658a718bd 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -17,7 +17,6 @@ #include "CodeSynthesis.h" #include "TypeChecker.h" -#include "ConstraintSystem.h" #include "TypeCheckAvailability.h" #include "TypeCheckDecl.h" #include "TypeCheckType.h" @@ -917,8 +916,7 @@ static Expr *buildStorageReference(AccessorDecl *accessor, // FIXME: Since we're not resolving overloads or anything, we should be // building fully type-checked AST above; we already have all the // information that we need. - if (!TypeChecker::typeCheckExpression(lookupExpr, accessor, - /*contextualInfo=*/{})) + if (!TypeChecker::typeCheckExpression(lookupExpr, accessor)) return nullptr; // Make sure we produce an lvalue only when desired. diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index df623b721556d..9b14f3c3f5bc0 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -17,6 +17,7 @@ #ifndef TYPECHECKING_H #define TYPECHECKING_H +#include "ConstraintSystem.h" #include "swift/AST/ASTContext.h" #include "swift/AST/AccessScope.h" #include "swift/AST/AnyFunctionRef.h" @@ -51,7 +52,6 @@ namespace constraints { class Solution; class SolutionApplicationTarget; class SolutionResult; - struct ContextualTypeInfo; } /// Special-case type checking semantics for certain declarations. @@ -561,7 +561,7 @@ Expr *findLHS(DeclContext *DC, Expr *E, Identifier name); /// \returns The type of the top-level expression, or Type() if an /// error occurred. Type typeCheckExpression(Expr *&expr, DeclContext *dc, - constraints::ContextualTypeInfo contextualInfo, + constraints::ContextualTypeInfo contextualInfo = {}, TypeCheckExprOptions options = TypeCheckExprOptions()); Optional From eba39cffe6ee2ad3602ac209359c4ffb5065a5b0 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 6 Oct 2020 16:57:53 -0700 Subject: [PATCH 250/745] [Concurrency] Corrected ordering of indirect results. Previously, the indirect results were claimed from the explosion after the arguments were claimed. That failed to match the order in which arguments actually appear in the explosion. Here the order is reversed. --- lib/IRGen/GenCall.cpp | 16 ++++++++-------- test/IRGen/async/run-call-generic-to-generic.sil | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index a544328dc0193..fbb959c013a3e 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -1974,14 +1974,6 @@ class AsyncCallEmission final : public CallEmission { llArgs.add(selfValue); } auto layout = getAsyncContextLayout(); - for (unsigned index = 0, count = layout.getArgumentCount(); index < count; - ++index) { - auto fieldLayout = layout.getArgumentLayout(index); - Address fieldAddr = - fieldLayout.project(IGF, context, /*offsets*/ llvm::None); - auto &ti = cast(fieldLayout.getType()); - ti.initialize(IGF, llArgs, fieldAddr, isOutlined); - } for (unsigned index = 0, count = layout.getIndirectReturnCount(); index < count; ++index) { auto fieldLayout = layout.getIndirectReturnLayout(index); @@ -1990,6 +1982,14 @@ class AsyncCallEmission final : public CallEmission { cast(fieldLayout.getType()) .initialize(IGF, llArgs, fieldAddr, isOutlined); } + for (unsigned index = 0, count = layout.getArgumentCount(); index < count; + ++index) { + auto fieldLayout = layout.getArgumentLayout(index); + Address fieldAddr = + fieldLayout.project(IGF, context, /*offsets*/ llvm::None); + auto &ti = cast(fieldLayout.getType()); + ti.initialize(IGF, llArgs, fieldAddr, isOutlined); + } if (layout.hasBindings()) { auto bindingLayout = layout.getBindingsLayout(); auto bindingsAddr = bindingLayout.project(IGF, context, /*offsets*/ None); diff --git a/test/IRGen/async/run-call-generic-to-generic.sil b/test/IRGen/async/run-call-generic-to-generic.sil index 491d5b610f32d..812a3cb99936f 100644 --- a/test/IRGen/async/run-call-generic-to-generic.sil +++ b/test/IRGen/async/run-call-generic-to-generic.sil @@ -35,7 +35,7 @@ bb0(%0 : $Int32, %1 : $UnsafeMutablePointer> %out_addr = alloc_stack $Int64 %genericToGeneric = function_ref @genericToGeneric : $@async @convention(thin) (@in_guaranteed T) -> @out T - %result1 = apply %genericToGeneric(%int_addr, %out_addr) : $@async @convention(thin) (@in_guaranteed T) -> @out T + %result1 = apply %genericToGeneric(%out_addr, %int_addr) : $@async @convention(thin) (@in_guaranteed T) -> @out T %print_int = function_ref @printInt64 : $@convention(thin) (Int64) -> () %out = load %out_addr : $*Int64 From 7d74a8614db13b3821f85d77c4fdcf511c8cbf15 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Mon, 5 Oct 2020 16:58:08 -0700 Subject: [PATCH 251/745] [Concurrency] Async CC supports witness methods. Previously, the AsyncContextLayout did not make space for the trailing witness fields (self metadata and self witness table) and the AsyncNativeCCEntryPointArgumentEmission could consequently not vend these fields. Here, the fields are added to the layout. --- lib/IRGen/GenCall.cpp | 37 +++++-- lib/IRGen/GenCall.h | 50 +++++++--- lib/IRGen/GenProto.cpp | 6 +- lib/IRGen/IRGenSIL.cpp | 18 +++- ...protocolwitness_instance-void-to-int64.sil | 73 ++++++++++++++ ..._instance-generic-to-int64-and-generic.sil | 97 +++++++++++++++++++ ...protocolwitness_instance-void-to-int64.sil | 80 +++++++++++++++ 7 files changed, 337 insertions(+), 24 deletions(-) create mode 100644 test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil create mode 100644 test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil create mode 100644 test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index fbb959c013a3e..977f8ac617f4a 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -181,6 +181,28 @@ AsyncContextLayout irgen::getAsyncContextLayout( paramInfos.push_back({ty, parameter.getConvention()}); } + Optional trailingWitnessInfo; + if (originalType->getRepresentation() == + SILFunctionTypeRepresentation::WitnessMethod) { + assert(getTrailingWitnessSignatureLength(IGF.IGM, originalType) == 2); + + // First, the Self metadata. + { + auto ty = SILType(); + auto &ti = IGF.IGM.getTypeMetadataPtrTypeInfo(); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + } + // Then, the Self witness table. + { + auto ty = SILType(); + auto &ti = IGF.IGM.getWitnessTablePtrTypeInfo(); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + } + trailingWitnessInfo = AsyncContextLayout::TrailingWitnessInfo(); + } + // ResultTypes directResults...; auto directResults = fnConv.getDirectSILResults(); for (auto result : directResults) { @@ -192,11 +214,11 @@ AsyncContextLayout irgen::getAsyncContextLayout( directReturnInfos.push_back(result); } - return AsyncContextLayout(IGF.IGM, LayoutStrategy::Optimal, valTypes, - typeInfos, IGF, originalType, substitutedType, - substitutionMap, std::move(bindings), errorType, - canHaveValidError, paramInfos, indirectReturnInfos, - directReturnInfos, localContextInfo); + return AsyncContextLayout( + IGF.IGM, LayoutStrategy::Optimal, valTypes, typeInfos, IGF, originalType, + substitutedType, substitutionMap, std::move(bindings), + trailingWitnessInfo, errorType, canHaveValidError, paramInfos, + indirectReturnInfos, directReturnInfos, localContextInfo); } AsyncContextLayout::AsyncContextLayout( @@ -204,8 +226,8 @@ AsyncContextLayout::AsyncContextLayout( ArrayRef fieldTypeInfos, IRGenFunction &IGF, CanSILFunctionType originalType, CanSILFunctionType substitutedType, SubstitutionMap substitutionMap, NecessaryBindings &&bindings, - SILType errorType, bool canHaveValidError, - ArrayRef argumentInfos, + Optional trailingWitnessInfo, SILType errorType, + bool canHaveValidError, ArrayRef argumentInfos, ArrayRef indirectReturnInfos, ArrayRef directReturnInfos, Optional localContextInfo) @@ -218,6 +240,7 @@ AsyncContextLayout::AsyncContextLayout( indirectReturnInfos(indirectReturnInfos.begin(), indirectReturnInfos.end()), localContextInfo(localContextInfo), bindings(std::move(bindings)), + trailingWitnessInfo(trailingWitnessInfo), argumentInfos(argumentInfos.begin(), argumentInfos.end()) { #ifndef NDEBUG assert(fieldTypeInfos.size() == fieldTypes.size() && diff --git a/lib/IRGen/GenCall.h b/lib/IRGen/GenCall.h index 6cda0007299f9..893e5f5eeaebe 100644 --- a/lib/IRGen/GenCall.h +++ b/lib/IRGen/GenCall.h @@ -89,6 +89,7 @@ namespace irgen { SILType type; ParameterConvention convention; }; + struct TrailingWitnessInfo {}; private: enum class FixedIndex : unsigned { @@ -107,6 +108,7 @@ namespace irgen { SmallVector indirectReturnInfos; Optional localContextInfo; NecessaryBindings bindings; + Optional trailingWitnessInfo; SmallVector argumentInfos; unsigned getErrorIndex() { return (unsigned)FixedIndex::Error; } @@ -125,13 +127,27 @@ namespace irgen { assert(hasBindings()); return getIndexAfterLocalContext(); } - unsigned getFirstArgumentIndex() { + unsigned getIndexAfterBindings() { return getIndexAfterLocalContext() + (hasBindings() ? 1 : 0); } + unsigned getFirstArgumentIndex() { return getIndexAfterBindings(); } unsigned getIndexAfterArguments() { return getFirstArgumentIndex() + getArgumentCount(); } - unsigned getFirstDirectReturnIndex() { return getIndexAfterArguments(); } + unsigned getSelfMetadataIndex() { + assert(hasTrailingWitnesses()); + return getIndexAfterArguments(); + } + unsigned getSelfWitnessTableIndex() { + assert(hasTrailingWitnesses()); + return getIndexAfterArguments() + 1; + } + unsigned getIndexAfterTrailingWitnesses() { + return getIndexAfterArguments() + (hasTrailingWitnesses() ? 2 : 0); + } + unsigned getFirstDirectReturnIndex() { + return getIndexAfterTrailingWitnesses(); + } public: bool canHaveError() { return canHaveValidError; } @@ -180,24 +196,30 @@ namespace irgen { index, IGF.IGM.getMaximalTypeExpansionContext()); } unsigned getArgumentCount() { return argumentInfos.size(); } + bool hasTrailingWitnesses() { return (bool)trailingWitnessInfo; } + ElementLayout getSelfMetadataLayout() { + assert(hasTrailingWitnesses()); + return getElement(getSelfMetadataIndex()); + } + ElementLayout getSelfWitnessTableLayout() { + return getElement(getSelfWitnessTableIndex()); + } unsigned getDirectReturnCount() { return directReturnInfos.size(); } ElementLayout getDirectReturnLayout(unsigned index) { return getElement(getFirstDirectReturnIndex() + index); } - AsyncContextLayout(IRGenModule &IGM, LayoutStrategy strategy, - ArrayRef fieldTypes, - ArrayRef fieldTypeInfos, - IRGenFunction &IGF, CanSILFunctionType originalType, - CanSILFunctionType substitutedType, - SubstitutionMap substitutionMap, - NecessaryBindings &&bindings, SILType errorType, - bool canHaveValidError, - ArrayRef argumentInfos, - ArrayRef directReturnInfos, - ArrayRef indirectReturnInfos, - Optional localContextInfo); + AsyncContextLayout( + IRGenModule &IGM, LayoutStrategy strategy, ArrayRef fieldTypes, + ArrayRef fieldTypeInfos, IRGenFunction &IGF, + CanSILFunctionType originalType, CanSILFunctionType substitutedType, + SubstitutionMap substitutionMap, NecessaryBindings &&bindings, + Optional trailingWitnessInfo, SILType errorType, + bool canHaveValidError, ArrayRef argumentInfos, + ArrayRef directReturnInfos, + ArrayRef indirectReturnInfos, + Optional localContextInfo); }; AsyncContextLayout getAsyncContextLayout(IRGenFunction &IGF, diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 1280c19e3281f..96e387eecccf7 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -3098,7 +3098,11 @@ NecessaryBindings NecessaryBindings::computeBindings( continue; case MetadataSource::Kind::SelfMetadata: - bindings.addTypeMetadata(getSubstSelfType(IGM, origType, subs)); + // Async functions pass the SelfMetadata and SelfWitnessTable parameters + // along explicitly. + if (forPartialApplyForwarder) { + bindings.addTypeMetadata(getSubstSelfType(IGM, origType, subs)); + } continue; case MetadataSource::Kind::SelfWitnessTable: diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 9237f6fa967b4..f5a34fb0ae7e1 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1257,9 +1257,23 @@ class AsyncNativeCCEntryPointArgumentEmission final return explosion.claimNext(); }; llvm::Value *getSelfWitnessTable() override { - llvm_unreachable("unimplemented"); + auto fieldLayout = layout.getSelfWitnessTableLayout(); + Address fieldAddr = + fieldLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); + auto &ti = cast(fieldLayout.getType()); + Explosion explosion; + ti.loadAsTake(IGF, fieldAddr, explosion); + return explosion.claimNext(); + } + llvm::Value *getSelfMetadata() override { + auto fieldLayout = layout.getSelfMetadataLayout(); + Address fieldAddr = + fieldLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); + auto &ti = cast(fieldLayout.getType()); + Explosion explosion; + ti.loadAsTake(IGF, fieldAddr, explosion); + return explosion.claimNext(); } - llvm::Value *getSelfMetadata() override { llvm_unreachable("unimplemented"); } llvm::Value *getCoroutineBuffer() override { llvm_unreachable("unimplemented"); } diff --git a/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil b/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil new file mode 100644 index 0000000000000..046508f05c1eb --- /dev/null +++ b/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil @@ -0,0 +1,73 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +protocol P { + func printMe() async -> Int64 +} + +struct I : P { + @_hasStorage let int: Int64 { get } + func printMe() async -> Int64 + init(int: Int64) +} + +// CHECK-LL-LABEL: define hidden swiftcc void @I_printMe(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @I_printMe : $@async @convention(method) (I) -> Int64 { +bb0(%self : $I): + %self_addr = alloc_stack $I + store %self to %self_addr : $*I + %printGeneric = function_ref @printGeneric : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %printGeneric_result = apply %printGeneric(%self_addr) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %self_addr : $*I + %result = struct_extract %self : $I, #I.int + return %result : $Int64 +} + +// CHECK-LL-LABEL: define internal swiftcc void @I_P_printMe(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil private [transparent] [thunk] @I_P_printMe : $@async @convention(witness_method: P) (@in_guaranteed I) -> Int64 { +bb0(%self_addr : $*I): + %self = load %self_addr : $*I + %I_printMe = function_ref @I_printMe : $@async @convention(method) (I) -> Int64 + %result = apply %I_printMe(%self) : $@async @convention(method) (I) -> Int64 + return %result : $Int64 +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %i_type = metatype $@thin I.Type + %i_int_literal = integer_literal $Builtin.Int64, 99 + %i_int = struct $Int64 (%i_int_literal : $Builtin.Int64) + %i = struct $I (%i_int : $Int64) + %i_addr = alloc_stack $I + store %i to %i_addr : $*I + %P_printMe = witness_method $I, #P.printMe : (Self) -> () async -> Int64 : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + %result = apply %P_printMe(%i_addr) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 // CHECK: I(int: 99) + dealloc_stack %i_addr : $*I + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_result = apply %printInt64(%result) : $@convention(thin) (Int64) -> () // CHECK: 99 + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} + +sil_witness_table hidden I: P module main { + method #P.printMe: (Self) -> () async -> Int64 : @I_P_printMe +} + diff --git a/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil b/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil new file mode 100644 index 0000000000000..b7ae98587e77a --- /dev/null +++ b/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil @@ -0,0 +1,97 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +protocol P { + func printMe(_ t: T) async -> (Int64, T) +} + +extension P { + func callPrintMe(_ t: T) async -> (Int64, T) +} + +struct I : P { + @_hasStorage let int: Int64 { get } + func printMe(_ t: T) async -> (Int64, T) + init(int: Int64) +} + +sil hidden @I_printMe : $@convention(method) @async (@in_guaranteed T, I) -> (Int64, @out T) { +bb0(%out_addr : $*T, %in_addr : $*T, %self : $I): + %self_addr = alloc_stack $I + store %self to %self_addr : $*I + %printGeneric = function_ref @printGeneric : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %printGeneric_result = apply %printGeneric(%self_addr) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %self_addr : $*I + %value = struct_extract %self : $I, #I.int + copy_addr %in_addr to [initialization] %out_addr : $*T + return %value : $Int64 +} + +// CHECK-LL: define internal swiftcc void @I_P_printMe(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil private [transparent] [thunk] @I_P_printMe : $@convention(witness_method: P) @async <τ_0_0> (@in_guaranteed τ_0_0, @in_guaranteed I) -> (Int64, @out τ_0_0) { +bb0(%out_addr : $*τ_0_0, %in_addr : $*τ_0_0, %self_addr : $*I): + %self = load %self_addr : $*I + %I_printMe = function_ref @I_printMe : $@convention(method) @async <τ_0_0> (@in_guaranteed τ_0_0, I) -> (Int64, @out τ_0_0) + %result = apply %I_printMe<τ_0_0>(%out_addr, %in_addr, %self) : $@convention(method) @async <τ_0_0> (@in_guaranteed τ_0_0, I) -> (Int64, @out τ_0_0) + return %result : $Int64 +} + +sil hidden @callPrintMe : $@convention(thin) (@in_guaranteed T, @in_guaranteed U) -> (Int64, @out U) { +bb0(%out_addr : $*U, %self_addr : $*T, %in_addr : $*U): + %I_P_printMe = witness_method $T, #P.printMe : (Self) -> (T) async -> (Int64, T) : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P><τ_1_0> (@in_guaranteed τ_1_0, @in_guaranteed τ_0_0) -> (Int64, @out τ_1_0) + %result = apply %I_P_printMe(%out_addr, %in_addr, %self_addr) : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P><τ_1_0> (@in_guaranteed τ_1_0, @in_guaranteed τ_0_0) -> (Int64, @out τ_1_0) + return %result : $Int64 +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %I_type = metatype $@thin I.Type + %int_literal = integer_literal $Builtin.Int64, 99 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %i = struct $I (%int : $Int64) + %out_addr = alloc_stack $I + %in_addr = alloc_stack $I + store %i to %in_addr : $*I + %i_addr = alloc_stack $I + store %i to %i_addr : $*I + %callPrintMe = function_ref @callPrintMe : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : P> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Int64, @out τ_0_1) + %result = apply %callPrintMe(%out_addr, %in_addr, %i_addr) : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : P> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Int64, @out τ_0_1) + dealloc_stack %i_addr : $*I + dealloc_stack %in_addr : $*I + %out = load %out_addr : $*I + dealloc_stack %out_addr : $*I + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_result = apply %printInt64(%result) : $@convention(thin) (Int64) -> () // CHECK: 99 + %out_addr_2 = alloc_stack $I + store %out to %out_addr_2 : $*I + %printGeneric = function_ref @printGeneric : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %printGeneric_result = apply %printGeneric(%out_addr_2) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () // CHECK: I(int: 99) + dealloc_stack %out_addr_2 : $*I + + + + %returnCode_literal = integer_literal $Builtin.Int32, 0 + %returnCode = struct $Int32 (%returnCode_literal : $Builtin.Int32) + return %returnCode : $Int32 +} + + +sil_witness_table hidden I: P module main { + method #P.printMe: (Self) -> (T) async -> (Int64, T) : @I_P_printMe +} diff --git a/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil b/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil new file mode 100644 index 0000000000000..da00bc58c4bb3 --- /dev/null +++ b/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil @@ -0,0 +1,80 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +protocol P { + func printMe() async -> Int64 +} + +struct I : P { + @_hasStorage let int: Int64 { get } + func printMe() async -> Int64 + init(int: Int64) +} + +// CHECK-LL-LABEL: define hidden swiftcc void @I_printMe(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @I_printMe : $@async @convention(method) (I) -> Int64 { +bb0(%self : $I): + %self_addr = alloc_stack $I + store %self to %self_addr : $*I + %printGeneric = function_ref @printGeneric : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %printGeneric_result = apply %printGeneric(%self_addr) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %self_addr : $*I + %result = struct_extract %self : $I, #I.int + return %result : $Int64 +} + +// CHECK-LL-LABEL: define internal swiftcc void @I_P_printMe(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil private [transparent] [thunk] @I_P_printMe : $@async @convention(witness_method: P) (@in_guaranteed I) -> Int64 { +bb0(%self_addr : $*I): + %self = load %self_addr : $*I + %I_printMe = function_ref @I_printMe : $@async @convention(method) (I) -> Int64 + %result = apply %I_printMe(%self) : $@async @convention(method) (I) -> Int64 + return %result : $Int64 +} + +sil hidden @callPrintMe : $@convention(thin) (@in_guaranteed T) -> Int64 { +bb0(%t : $*T): + %I_P_printMe = witness_method $T, #P.printMe : (Self) -> () async -> Int64 : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + %result = apply %I_P_printMe(%t) : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + return %result : $Int64 +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %i_type = metatype $@thin I.Type + %i_int_literal = integer_literal $Builtin.Int64, 99 + %i_int = struct $Int64 (%i_int_literal : $Builtin.Int64) + %i = struct $I (%i_int : $Int64) + %i_addr = alloc_stack $I + store %i to %i_addr : $*I + %callPrintMe = function_ref @callPrintMe : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + %result = apply %callPrintMe(%i_addr) : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 // users: %13, %11 // CHECK: I(int: 99) + dealloc_stack %i_addr : $*I + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_result = apply %printInt64(%result) : $@convention(thin) (Int64) -> () // CHECK: 99 + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} + +sil_witness_table hidden I: P module main { + method #P.printMe: (Self) -> () async -> Int64 : @I_P_printMe +} + From 0a5df673edce66633e41215cc0d2a44891d3d29d Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 6 Oct 2020 11:31:48 -0700 Subject: [PATCH 252/745] [NFC] Deduped async entry point emission code. Previously the same code was used for loading values from the async context. Here, that same code is extracted into a private method. --- lib/IRGen/IRGenSIL.cpp | 42 ++++++++++++++---------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index f5a34fb0ae7e1..a4c4544e1050f 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1207,6 +1207,14 @@ class AsyncNativeCCEntryPointArgumentEmission final /*const*/ AsyncContextLayout layout; const Address dataAddr; + llvm::Value *loadValue(ElementLayout layout) { + Address addr = layout.project(IGF, dataAddr, /*offsets*/ llvm::None); + auto &ti = cast(layout.getType()); + Explosion explosion; + ti.loadAsTake(IGF, addr, explosion); + return explosion.claimNext(); + } + public: AsyncNativeCCEntryPointArgumentEmission(IRGenSILFunction &IGF, SILBasicBlock &entry, @@ -1222,22 +1230,15 @@ class AsyncNativeCCEntryPointArgumentEmission final } llvm::Value *getContext() override { auto contextLayout = layout.getLocalContextLayout(); - Address addr = contextLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); - auto &ti = cast(contextLayout.getType()); - Explosion explosion; - ti.loadAsTake(IGF, addr, explosion); - return explosion.claimNext(); + return loadValue(contextLayout); } Explosion getArgumentExplosion(unsigned index, unsigned size) override { assert(size > 0); Explosion result; for (unsigned i = index, end = index + size; i < end; ++i) { auto argumentLayout = layout.getArgumentLayout(i); - auto addr = argumentLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); - auto &ti = cast(argumentLayout.getType()); - Explosion explosion; - ti.loadAsTake(IGF, addr, explosion); - result.add(explosion.claimAll()); + auto *value = loadValue(argumentLayout); + result.add(value); } return result; } @@ -1249,30 +1250,15 @@ class AsyncNativeCCEntryPointArgumentEmission final } llvm::Value *getIndirectResult(unsigned index) override { auto fieldLayout = layout.getIndirectReturnLayout(index); - Address fieldAddr = - fieldLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); - auto &ti = cast(fieldLayout.getType()); - Explosion explosion; - ti.loadAsTake(IGF, fieldAddr, explosion); - return explosion.claimNext(); + return loadValue(fieldLayout); }; llvm::Value *getSelfWitnessTable() override { auto fieldLayout = layout.getSelfWitnessTableLayout(); - Address fieldAddr = - fieldLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); - auto &ti = cast(fieldLayout.getType()); - Explosion explosion; - ti.loadAsTake(IGF, fieldAddr, explosion); - return explosion.claimNext(); + return loadValue(fieldLayout); } llvm::Value *getSelfMetadata() override { auto fieldLayout = layout.getSelfMetadataLayout(); - Address fieldAddr = - fieldLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); - auto &ti = cast(fieldLayout.getType()); - Explosion explosion; - ti.loadAsTake(IGF, fieldAddr, explosion); - return explosion.claimNext(); + return loadValue(fieldLayout); } llvm::Value *getCoroutineBuffer() override { llvm_unreachable("unimplemented"); From 6e0d89432533b29311d7f1afcdae360aa81b1f61 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 6 Oct 2020 11:53:16 -0700 Subject: [PATCH 253/745] [NFC] Deduped async call emission code. Previously the same code was used for saving values into the async context. Here, that code is extracted into a private method. --- lib/IRGen/GenCall.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 977f8ac617f4a..eafe49657800e 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -1952,6 +1952,12 @@ class AsyncCallEmission final : public CallEmission { getCallee().getSubstitutions()); } + void saveValue(ElementLayout layout, Explosion &explosion, bool isOutlined) { + Address addr = layout.project(IGF, context, /*offsets*/ llvm::None); + auto &ti = cast(layout.getType()); + ti.initialize(IGF, explosion, addr, isOutlined); + } + public: AsyncCallEmission(IRGenFunction &IGF, llvm::Value *selfValue, Callee &&callee) : CallEmission(IGF, selfValue, std::move(callee)) { @@ -2000,18 +2006,12 @@ class AsyncCallEmission final : public CallEmission { for (unsigned index = 0, count = layout.getIndirectReturnCount(); index < count; ++index) { auto fieldLayout = layout.getIndirectReturnLayout(index); - Address fieldAddr = - fieldLayout.project(IGF, context, /*offsets*/ llvm::None); - cast(fieldLayout.getType()) - .initialize(IGF, llArgs, fieldAddr, isOutlined); + saveValue(fieldLayout, llArgs, isOutlined); } for (unsigned index = 0, count = layout.getArgumentCount(); index < count; ++index) { auto fieldLayout = layout.getArgumentLayout(index); - Address fieldAddr = - fieldLayout.project(IGF, context, /*offsets*/ llvm::None); - auto &ti = cast(fieldLayout.getType()); - ti.initialize(IGF, llArgs, fieldAddr, isOutlined); + saveValue(fieldLayout, llArgs, isOutlined); } if (layout.hasBindings()) { auto bindingLayout = layout.getBindingsLayout(); @@ -2020,10 +2020,7 @@ class AsyncCallEmission final : public CallEmission { } if (selfValue) { auto fieldLayout = layout.getLocalContextLayout(); - Address fieldAddr = - fieldLayout.project(IGF, context, /*offsets*/ llvm::None); - auto &ti = cast(fieldLayout.getType()); - ti.initialize(IGF, llArgs, fieldAddr, isOutlined); + saveValue(fieldLayout, llArgs, isOutlined); } } void emitCallToUnmappedExplosion(llvm::CallInst *call, Explosion &out) override { From 4c298db5297f88f3eba35d03063847494befa41b Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 6 Oct 2020 12:22:08 -0700 Subject: [PATCH 254/745] [NFC] Extracted async call emission loading. Previously the code for loading indirect returns was inline in the member function where it was performed. Here it is pulled out into a private method. --- lib/IRGen/GenCall.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index eafe49657800e..fdfdd3d945b6f 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -1957,6 +1957,11 @@ class AsyncCallEmission final : public CallEmission { auto &ti = cast(layout.getType()); ti.initialize(IGF, explosion, addr, isOutlined); } + void loadValue(ElementLayout layout, Explosion &explosion) { + Address addr = layout.project(IGF, context, /*offsets*/ llvm::None); + auto &ti = layout.getType(); + cast(ti).loadAsTake(IGF, addr, explosion); + } public: AsyncCallEmission(IRGenFunction &IGF, llvm::Value *selfValue, Callee &&callee) @@ -2036,20 +2041,13 @@ class AsyncCallEmission final : public CallEmission { // argument buffer. return; } - assert(call->arg_size() == 1); - auto context = call->arg_begin()->get(); // Gather the values. Explosion nativeExplosion; auto layout = getAsyncContextLayout(); - auto dataAddr = layout.emitCastTo(IGF, context); for (unsigned index = 0, count = layout.getDirectReturnCount(); index < count; ++index) { auto fieldLayout = layout.getDirectReturnLayout(index); - Address fieldAddr = - fieldLayout.project(IGF, dataAddr, /*offsets*/ llvm::None); - auto &fieldTI = fieldLayout.getType(); - cast(fieldTI).loadAsTake(IGF, fieldAddr, - nativeExplosion); + loadValue(fieldLayout, nativeExplosion); } out = nativeSchema.mapFromNative(IGF.IGM, IGF, nativeExplosion, resultType); From 27330f169df5d2dcb018ab2c6615d66be91d04e2 Mon Sep 17 00:00:00 2001 From: Dave Lee Date: Tue, 6 Oct 2020 18:17:24 -0700 Subject: [PATCH 255/745] [build] Remove unused lldb cmake --- utils/build-script-impl | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index b4ef4e120833e..d805f5eeb5285 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -1960,17 +1960,6 @@ for host in "${ALL_HOSTS[@]}"; do "${lldb_cmake_options[@]}" ) - # Figure out if we think this is a buildbot build. - # This will influence the lldb version line. - if [ ! -z "${JENKINS_HOME}" -a ! -z "${JOB_NAME}" -a ! -z "${BUILD_NUMBER}" ]; then - LLDB_IS_BUILDBOT_BUILD=1 - else - LLDB_IS_BUILDBOT_BUILD=0 - fi - - # Get the build date. - LLDB_BUILD_DATE=$(date +%Y-%m-%d) - # Pick the right cache. if [[ "$(uname -s)" == "Darwin" ]] ; then cmake_cache="Apple-lldb-macOS.cmake" @@ -2042,8 +2031,6 @@ for host in "${ALL_HOSTS[@]}"; do -DLLDB_ENABLE_LZMA=OFF -DLLDB_ENABLE_LUA=OFF -DLLDB_PATH_TO_SWIFT_SOURCE:PATH="${SWIFT_SOURCE_DIR}" - -DLLDB_IS_BUILDBOT_BUILD:BOOL="${LLDB_IS_BUILDBOT_BUILD}" - -DLLDB_BUILD_DATE:STRING="\"${LLDB_BUILD_DATE}\"" -DLLDB_INCLUDE_TESTS:BOOL=$(false_true ${BUILD_TOOLCHAIN_ONLY}) -DLLDB_TEST_USER_ARGS="${DOTEST_ARGS}" ) From 2546c2d78d1d7c14958c9f66819796240ae6ca72 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 6 Oct 2020 23:01:47 -0400 Subject: [PATCH 256/745] Fetch associated types abstractly when fetching an associated conformance. By default, emitTypeMetadataRef does a blocking request for complete metadata, which is the right thing to do for most purposes in IRGen. Unfortunately, it's actively dangerous in code that can be called during metadata completion, like an associated conformance accessor, because it can cause artificial dependency cycles that the runtime isn't equipped to detect, much less solve. This is a partial fix for rdar://69901318, which also exposes a bad metadata access path that seems to be causing an artificial problem. --- lib/IRGen/GenProto.cpp | 6 ++++-- test/IRGen/associated_types.swift | 3 ++- test/IRGen/generic_structs.sil | 3 --- test/IRGen/generic_structs_future.sil | 3 --- test/IRGen/same_type_constraints.swift | 2 +- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 706e64cf6d74e..1e20fcb9babc2 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -2567,8 +2567,10 @@ MetadataResponse MetadataPath::followComponent(IRGenFunction &IGF, if (!source) return MetadataResponse(); - auto sourceMetadata = IGF.emitTypeMetadataRef(sourceType); - auto associatedMetadata = IGF.emitTypeMetadataRef(sourceKey.Type); + auto sourceMetadata = + IGF.emitAbstractTypeMetadataRef(sourceType); + auto associatedMetadata = + IGF.emitAbstractTypeMetadataRef(sourceKey.Type); auto sourceWTable = source.getMetadata(); AssociatedConformance associatedConformanceRef(sourceProtocol, diff --git a/test/IRGen/associated_types.swift b/test/IRGen/associated_types.swift index 5d0953f1a0e6c..f5d152beb7d1f 100644 --- a/test/IRGen/associated_types.swift +++ b/test/IRGen/associated_types.swift @@ -75,8 +75,9 @@ func testFastRuncible(_ t: T, u: U) // 1. Get the type metadata for U.RuncerType.Runcee. // 1a. Get the type metadata for U.RuncerType. // Note that we actually look things up in T, which is going to prove unfortunate. -// CHECK: [[T2:%.*]] = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness([[INT]] 0, i8** %T.Runcible, %swift.type* %T, %swift.protocol_requirement* getelementptr{{.*}}i32 12), i32 -1), %swift.protocol_requirement* getelementptr inbounds (<{{.*}}>, <{{.*}}>* @"$s16associated_types8RuncibleMp", i32 0, i32 14)) [[NOUNWIND_READNONE:#.*]] +// CHECK: [[T2:%.*]] = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness([[INT]] 255, i8** %T.Runcible, %swift.type* %T, %swift.protocol_requirement* getelementptr{{.*}}i32 12), i32 -1), %swift.protocol_requirement* getelementptr inbounds (<{{.*}}>, <{{.*}}>* @"$s16associated_types8RuncibleMp", i32 0, i32 14)) [[NOUNWIND_READNONE:#.*]] // CHECK-NEXT: %T.RuncerType = extractvalue %swift.metadata_response [[T2]], 0 +// CHECK-NEXT: extractvalue %swift.metadata_response [[T2]], 1 // 2. Get the witness table for U.RuncerType.Runcee : Speedy // 2a. Get the protocol witness table for U.RuncerType : FastRuncer. // CHECK-NEXT: %T.RuncerType.FastRuncer = call swiftcc i8** @swift_getAssociatedConformanceWitness(i8** %U.FastRuncible, %swift.type* %U, %swift.type* %T.RuncerType diff --git a/test/IRGen/generic_structs.sil b/test/IRGen/generic_structs.sil index 39dd44b5e1243..a90d4f825c479 100644 --- a/test/IRGen/generic_structs.sil +++ b/test/IRGen/generic_structs.sil @@ -246,9 +246,6 @@ struct GenericLayoutWithAssocType { // CHECK-LABEL: define internal swiftcc %swift.metadata_response @"$s15generic_structs26GenericLayoutWithAssocTypeVMr"( -// CHECK: [[T0:%.*]] = call{{( tail)?}} swiftcc %swift.metadata_response @swift_checkMetadataState(i64 0, %swift.type* %T) -// CHECK: [[T_CHECKED:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 - // CHECK: [[T0_GEP:%.*]] = getelementptr inbounds i8*, i8** %T.ParentHasAssociatedType, i32 1 // CHECK: [[T0:%.*]] = load i8*, i8** [[T0_GEP]] // CHECK: [[T1:%.*]] = bitcast i8* [[T0]] to i8** diff --git a/test/IRGen/generic_structs_future.sil b/test/IRGen/generic_structs_future.sil index e9b49c850e0e9..8874a61ba6509 100644 --- a/test/IRGen/generic_structs_future.sil +++ b/test/IRGen/generic_structs_future.sil @@ -247,9 +247,6 @@ struct GenericLayoutWithAssocType { // CHECK-LABEL: define internal swiftcc %swift.metadata_response @"$s22generic_structs_future26GenericLayoutWithAssocTypeVMr"( -// CHECK: [[T0:%.*]] = call{{( tail)?}} swiftcc %swift.metadata_response @swift_checkMetadataState(i64 0, %swift.type* %T) -// CHECK: [[T_CHECKED:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 - // CHECK: [[T0_GEP:%.*]] = getelementptr inbounds i8*, i8** %T.ParentHasAssociatedType, i32 1 // CHECK: [[T0:%.*]] = load i8*, i8** [[T0_GEP]] // CHECK: [[T1:%.*]] = bitcast i8* [[T0]] to i8** diff --git a/test/IRGen/same_type_constraints.swift b/test/IRGen/same_type_constraints.swift index a3ac8a52c7a99..17ba3f5161332 100644 --- a/test/IRGen/same_type_constraints.swift +++ b/test/IRGen/same_type_constraints.swift @@ -67,7 +67,7 @@ where Self : CodingType, print(Self.ValueType.self) } -// OSIZE: define internal swiftcc i8** @"$s21same_type_constraints12GenericKlazzCyxq_GAA1EAA4Data_AA0F4TypePWT"(%swift.type* %"GenericKlazz.Data", %swift.type* nocapture readonly %"GenericKlazz", i8** nocapture readnone %"GenericKlazz.E") [[ATTRS:#[0-9]+]] { +// OSIZE: define internal swiftcc i8** @"$s21same_type_constraints12GenericKlazzCyxq_GAA1EAA4Data_AA0F4TypePWT"(%swift.type* readnone %"GenericKlazz.Data", %swift.type* nocapture readonly %"GenericKlazz", i8** nocapture readnone %"GenericKlazz.E") [[ATTRS:#[0-9]+]] { // OSIZE: [[ATTRS]] = {{{.*}}noinline // Check that same-typing two generic parameters together lowers correctly. From 4b6bc60d23361b0801fd1d969b15b8136f0e22db Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 5 Oct 2020 15:39:37 +0100 Subject: [PATCH 257/745] NFC: fix typo in build_swift/versions.py --- utils/build_swift/build_swift/versions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/build_swift/build_swift/versions.py b/utils/build_swift/build_swift/versions.py index 0db7ec32ce7b9..0d5edabcba722 100644 --- a/utils/build_swift/build_swift/versions.py +++ b/utils/build_swift/build_swift/versions.py @@ -168,7 +168,7 @@ def __init__(self, version, msg=None): @functools.total_ordering class Version(object): - """Similar to the standard distutils.versons.LooseVersion, but with a + """Similar to the standard distutils.version.LooseVersion, but with a little more wiggle-room for alpha characters. """ From 3708e7bbd5f1161121189de7cb201693e2357c8c Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Wed, 7 Oct 2020 14:10:39 +0300 Subject: [PATCH 258/745] WinSDK: extract Security submodule with winscard.h Currently this header gets included into `WinSDK.WinSock2` via windows.h --- stdlib/public/Platform/winsdk.modulemap | 37 +++++++++++++++---------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/stdlib/public/Platform/winsdk.modulemap b/stdlib/public/Platform/winsdk.modulemap index 5307e9b89362a..0eabe169bb150 100644 --- a/stdlib/public/Platform/winsdk.modulemap +++ b/stdlib/public/Platform/winsdk.modulemap @@ -137,13 +137,6 @@ module WinSDK [system] { } } - module AuthZ { - header "AuthZ.h" - export * - - link "AuthZ.Lib" - } - module Controls { module CommCtrl { header "CommCtrl.h" @@ -224,6 +217,29 @@ module WinSDK [system] { link "WinMM.Lib" } + module Security { + module AuthZ { + header "AuthZ.h" + export * + + link "AuthZ.Lib" + } + + module SmartCard { + header "winscard.h" + export * + + link "winscard.lib" + } + + module WinCrypt { + header "wincrypt.h" + export * + + link "Crypt32.Lib" + } + } + module Shell { header "ShlObj.h" export * @@ -307,13 +323,6 @@ module WinSDK [system] { export * } - module WinCrypt { - header "wincrypt.h" - export * - - link "Crypt32.Lib" - } - module WinDNS { header "WinDNS.h" export * From 824ecdd0aa9da889c13a3266b6f31d8194062707 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 6 Oct 2020 00:54:21 -0400 Subject: [PATCH 259/745] ASTScope: Fix AttachedPropertyWrapperScope source range The source range needs to contain the argument expression. --- include/swift/AST/ASTScope.h | 13 +------------ lib/AST/ASTScopeSourceRange.cpp | 2 +- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index c6f6c2a46cfdc..779e486185689 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -966,19 +966,8 @@ class AttachedPropertyWrapperScope final : public ASTScopeImpl { CustomAttr *attr; VarDecl *decl; - /// Because we have to avoid request cycles, we approximate the test for an - /// AttachedPropertyWrapper with one based on source location. We might get - /// false positives, that that doesn't hurt anything. However, the result of - /// the conservative source range computation doesn't seem to be stable. So - /// keep the original here, and use it for source range queries. - const SourceRange sourceRangeWhenCreated; - AttachedPropertyWrapperScope(CustomAttr *attr, VarDecl *decl) - : attr(attr), decl(decl), - sourceRangeWhenCreated(attr->getTypeRepr()->getSourceRange()) { - ASTScopeAssert(sourceRangeWhenCreated.isValid(), - "VarDecls must have ranges to be looked-up"); - } + : attr(attr), decl(decl) {} virtual ~AttachedPropertyWrapperScope() {} protected: diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index 1d93bdd762b84..89acd79aacddb 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -448,7 +448,7 @@ SourceRange ClosureParametersScope::getSourceRangeOfThisASTNode( SourceRange AttachedPropertyWrapperScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - return sourceRangeWhenCreated; + return attr->getRange(); } SourceRange GuardStmtScope::getSourceRangeOfThisASTNode( From 66873b94c9c4adada9cdef3fa4866325f1050b35 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 6 Oct 2020 00:56:31 -0400 Subject: [PATCH 260/745] ASTScope: Fix SubscriptDeclScope source range The source range needs to contain any attributes on the subscript declaration, since those might create child scopes (@_specialize, property wrappers, etc). --- lib/AST/ASTScopeSourceRange.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index 89acd79aacddb..c377febaef6e7 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -226,7 +226,7 @@ SourceRange TopLevelCodeScope::getSourceRangeOfThisASTNode( SourceRange SubscriptDeclScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - return decl->getSourceRange(); + return decl->getSourceRangeIncludingAttrs(); } SourceRange From 2e67c135fd286c9b942c79524aad739720f1cf3c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 2 Oct 2020 02:43:21 -0400 Subject: [PATCH 261/745] ASTScope: Rework ConditionalClauseScopes The top-level scope for a conditional clause with a pattern is now ConditionalClausePatternUseScope, which introduces the pattern's bindings. Since the patterns are not visible in their own initializer, a new ConditionalClauseInitializerScope is used for the initializer. While it is nested inside of the ConditionalClausePatternUseScope, it's lookup parent skips one level, giving us the desired behavior. --- include/swift/AST/ASTScope.h | 80 +++++------- lib/AST/ASTScope.cpp | 13 +- lib/AST/ASTScopeCreation.cpp | 117 ++++++++++++------ lib/AST/ASTScopeLookup.cpp | 28 +++-- lib/AST/ASTScopePrinting.cpp | 7 +- lib/AST/ASTScopeSourceRange.cpp | 33 ++--- .../NameLookup/scope_map-astscopelookup.swift | 2 +- test/NameLookup/scope_map_top_level.swift | 12 +- 8 files changed, 146 insertions(+), 146 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 779e486185689..222b66a2167a8 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -1082,45 +1082,27 @@ class PatternEntryInitializerScope final : public AbstractPatternEntryScope { bool lookupLocalsOrMembers(DeclConsumer) const override; }; -/// The scope introduced by a conditional clause in an if/guard/while -/// statement. -/// Since there may be more than one "let foo = ..." in (e.g.) an "if", -/// we allocate a matrushka of these. -class ConditionalClauseScope final : public ASTScopeImpl { +/// The scope introduced by a conditional clause initializer in an +/// if/while/guard statement. +class ConditionalClauseInitializerScope final : public ASTScopeImpl { public: - LabeledConditionalStmt *const stmt; - const unsigned index; - const SourceLoc endLoc; // cannot get it from the stmt - - ConditionalClauseScope(LabeledConditionalStmt *stmt, unsigned index, - SourceLoc endLoc) - : stmt(stmt), index(index), endLoc(endLoc) {} - - virtual ~ConditionalClauseScope() {} - -protected: - ASTScopeImpl *expandSpecifically(ScopeCreator &scopeCreator) override; + Expr *const initializer; + const SourceRange bodyRange; -private: - AnnotatedInsertionPoint - expandAScopeThatCreatesANewInsertionPoint(ScopeCreator &); - -public: - std::string getClassName() const override; - -protected: - void printSpecifics(llvm::raw_ostream &out) const override; + ConditionalClauseInitializerScope(Expr *initializer) + : initializer(initializer) {} -public: + virtual ~ConditionalClauseInitializerScope() {} SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; + std::string getClassName() const override; private: - ArrayRef getCond() const; - const StmtConditionElement &getStmtConditionElement() const; + void expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator &); protected: - bool isLabeledStmtLookupTerminator() const override; + ASTScopeImpl *expandSpecifically(ScopeCreator &scopeCreator) override; + NullablePtr getLookupParent() const override; }; /// If, while, & guard statements all start with a conditional clause, then some @@ -1128,17 +1110,21 @@ class ConditionalClauseScope final : public ASTScopeImpl { /// the normal lookup rule to pass the lookup scope into the deepest conditional /// clause. class ConditionalClausePatternUseScope final : public ASTScopeImpl { - Pattern *const pattern; - const SourceLoc startLoc; + StmtConditionElement sec; + SourceLoc endLoc; public: - ConditionalClausePatternUseScope(Pattern *pattern, SourceLoc startLoc) - : pattern(pattern), startLoc(startLoc) {} + ConditionalClausePatternUseScope(StmtConditionElement sec, SourceLoc endLoc) + : sec(sec), endLoc(endLoc) {} SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; std::string getClassName() const override; +private: + AnnotatedInsertionPoint + expandAScopeThatCreatesANewInsertionPoint(ScopeCreator &); + protected: ASTScopeImpl *expandSpecifically(ScopeCreator &) override; bool lookupLocalsOrMembers(DeclConsumer) const override; @@ -1350,7 +1336,7 @@ class LabeledConditionalStmtScope : public AbstractStmtScope { protected: /// Return the lookupParent required to search these. ASTScopeImpl *createNestedConditionalClauseScopes(ScopeCreator &, - const Stmt *afterConds); + SourceLoc); }; class IfStmtScope final : public LabeledConditionalStmtScope { @@ -1408,28 +1394,24 @@ class GuardStmtScope final : public LabeledConditionalStmtScope { getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; }; -/// A scope after a guard statement that follows lookups into the conditions -/// Also for: -/// The insertion point of the last statement of an active clause in an #if -/// must be the lookup parent -/// of any following scopes. But the active clause may not be the last clause. -/// In short, this is another case where the lookup parent cannot follow the same -/// nesting as the source order. IfConfigUseScope implements this twist. It -/// follows the IfConfig, wraps all subsequent scopes, and redirects the lookup. -class LookupParentDiversionScope final : public ASTScopeImpl { +/// A scope for the body of a guard statement. Lookups from the body must +/// skip the parent scopes for introducing pattern bindings, since they're +/// not visible in the guard body, only after the body ends. +class GuardStmtBodyScope final : public ASTScopeImpl { public: ASTScopeImpl *const lookupParent; - const SourceLoc startLoc; - const SourceLoc endLoc; + BraceStmt *const body; - LookupParentDiversionScope(ASTScopeImpl *lookupParent, - SourceLoc startLoc, SourceLoc endLoc) - : lookupParent(lookupParent), startLoc(startLoc), endLoc(endLoc) {} + GuardStmtBodyScope(ASTScopeImpl *lookupParent, BraceStmt *body) + : lookupParent(lookupParent), body(body) {} SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; std::string getClassName() const override; +private: + void expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator &); + protected: ASTScopeImpl *expandSpecifically(ScopeCreator &) override; NullablePtr getLookupParent() const override { diff --git a/lib/AST/ASTScope.cpp b/lib/AST/ASTScope.cpp index 82e109614025b..ec6868c2c4412 100644 --- a/lib/AST/ASTScope.cpp +++ b/lib/AST/ASTScope.cpp @@ -137,8 +137,8 @@ DEFINE_GET_CLASS_NAME(DefaultArgumentInitializerScope) DEFINE_GET_CLASS_NAME(AttachedPropertyWrapperScope) DEFINE_GET_CLASS_NAME(PatternEntryDeclScope) DEFINE_GET_CLASS_NAME(PatternEntryInitializerScope) -DEFINE_GET_CLASS_NAME(ConditionalClauseScope) DEFINE_GET_CLASS_NAME(ConditionalClausePatternUseScope) +DEFINE_GET_CLASS_NAME(ConditionalClauseInitializerScope) DEFINE_GET_CLASS_NAME(CaptureListScope) DEFINE_GET_CLASS_NAME(ClosureParametersScope) DEFINE_GET_CLASS_NAME(TopLevelCodeScope) @@ -149,7 +149,7 @@ DEFINE_GET_CLASS_NAME(EnumElementScope) DEFINE_GET_CLASS_NAME(IfStmtScope) DEFINE_GET_CLASS_NAME(WhileStmtScope) DEFINE_GET_CLASS_NAME(GuardStmtScope) -DEFINE_GET_CLASS_NAME(LookupParentDiversionScope) +DEFINE_GET_CLASS_NAME(GuardStmtBodyScope) DEFINE_GET_CLASS_NAME(RepeatWhileScope) DEFINE_GET_CLASS_NAME(DoStmtScope) DEFINE_GET_CLASS_NAME(DoCatchStmtScope) @@ -199,15 +199,6 @@ void ASTScopeImpl::postOrderDo(function_ref fn) { fn(this); } -ArrayRef ConditionalClauseScope::getCond() const { - return stmt->getCond(); -} - -const StmtConditionElement & -ConditionalClauseScope::getStmtConditionElement() const { - return getCond()[index]; -} - unsigned ASTScopeImpl::countDescendants() const { unsigned count = 0; const_cast(this)->preOrderDo( diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index b7324c68013c7..a1673a0856016 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -687,6 +687,9 @@ class NodeAdder NullablePtr visitBraceStmt(BraceStmt *bs, ASTScopeImpl *p, ScopeCreator &scopeCreator) { + if (bs->empty()) + return p; + SmallVector localFuncsAndTypes; SmallVector localVars; @@ -941,17 +944,18 @@ ASTScopeImpl *ASTScopeImpl::expandAndBeCurrent(ScopeCreator &scopeCreator) { ASTScopeImpl *Scope::expandSpecifically(ScopeCreator &) { return this; } CREATES_NEW_INSERTION_POINT(ASTSourceFileScope) -CREATES_NEW_INSERTION_POINT(ConditionalClauseScope) CREATES_NEW_INSERTION_POINT(GuardStmtScope) CREATES_NEW_INSERTION_POINT(PatternEntryDeclScope) CREATES_NEW_INSERTION_POINT(GenericTypeOrExtensionScope) CREATES_NEW_INSERTION_POINT(BraceStmtScope) CREATES_NEW_INSERTION_POINT(TopLevelCodeScope) +CREATES_NEW_INSERTION_POINT(ConditionalClausePatternUseScope) NO_NEW_INSERTION_POINT(FunctionBodyScope) NO_NEW_INSERTION_POINT(AbstractFunctionDeclScope) NO_NEW_INSERTION_POINT(AttachedPropertyWrapperScope) NO_NEW_INSERTION_POINT(EnumElementScope) +NO_NEW_INSERTION_POINT(GuardStmtBodyScope) NO_NEW_INSERTION_POINT(ParameterListScope) NO_NEW_INSERTION_POINT(PatternEntryInitializerScope) @@ -959,6 +963,7 @@ NO_NEW_INSERTION_POINT(CaptureListScope) NO_NEW_INSERTION_POINT(CaseStmtScope) NO_NEW_INSERTION_POINT(CaseLabelItemScope) NO_NEW_INSERTION_POINT(CaseStmtBodyScope) +NO_NEW_INSERTION_POINT(ConditionalClauseInitializerScope) NO_NEW_INSERTION_POINT(ClosureParametersScope) NO_NEW_INSERTION_POINT(DefaultArgumentInitializerScope) NO_NEW_INSERTION_POINT(DoStmtScope) @@ -974,8 +979,6 @@ NO_NEW_INSERTION_POINT(WhileStmtScope) NO_EXPANSION(GenericParamScope) NO_EXPANSION(SpecializeAttributeScope) NO_EXPANSION(DifferentiableAttributeScope) -NO_EXPANSION(ConditionalClausePatternUseScope) -NO_EXPANSION(LookupParentDiversionScope) #undef CREATES_NEW_INSERTION_POINT #undef NO_NEW_INSERTION_POINT @@ -1057,42 +1060,49 @@ PatternEntryInitializerScope::expandAScopeThatDoesNotCreateANewInsertionPoint( this); } + AnnotatedInsertionPoint -ConditionalClauseScope::expandAScopeThatCreatesANewInsertionPoint( +ConditionalClausePatternUseScope::expandAScopeThatCreatesANewInsertionPoint( ScopeCreator &scopeCreator) { - const StmtConditionElement &sec = getStmtConditionElement(); - switch (sec.getKind()) { - case StmtConditionElement::CK_Availability: - return {this, "No introduced variables"}; - case StmtConditionElement::CK_Boolean: - scopeCreator.addToScopeTree(sec.getBoolean(), this); - return {this, "No introduced variables"}; - case StmtConditionElement::CK_PatternBinding: - scopeCreator.addToScopeTree(sec.getInitializer(), this); - auto *const ccPatternUseScope = - scopeCreator.constructExpandAndInsertUncheckable< - ConditionalClausePatternUseScope>(this, sec.getPattern(), endLoc); - return {ccPatternUseScope, - "Succeeding code must be in scope of conditional variables"}; - } - ASTScope_unreachable("Unhandled StmtConditionKind in switch"); + auto *initializer = sec.getInitializer(); + if (!isa(initializer)) { + scopeCreator + .constructExpandAndInsertUncheckable( + this, initializer); + } + + return {this, + "Succeeding code must be in scope of conditional clause pattern bindings"}; +} + +void +ConditionalClauseInitializerScope::expandAScopeThatDoesNotCreateANewInsertionPoint( + ScopeCreator &scopeCreator) { + scopeCreator.addToScopeTree(ASTNode(initializer), this); +} + +void +GuardStmtBodyScope::expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator & + scopeCreator) { + scopeCreator.addToScopeTree(ASTNode(body), this); } AnnotatedInsertionPoint GuardStmtScope::expandAScopeThatCreatesANewInsertionPoint(ScopeCreator & scopeCreator) { - ASTScopeImpl *conditionLookupParent = - createNestedConditionalClauseScopes(scopeCreator, stmt->getBody()); + createNestedConditionalClauseScopes(scopeCreator, endLoc); + // Add a child for the 'guard' body, which always exits. - // Parent is whole guard stmt scope, NOT the cond scopes - scopeCreator.addToScopeTree(stmt->getBody(), this); + // The lookup parent is whole guard stmt scope, NOT the cond scopes + auto *body = stmt->getBody(); + if (!body->empty()) { + scopeCreator + .constructExpandAndInsertUncheckable( + conditionLookupParent, this, stmt->getBody()); + } - auto *const lookupParentDiversionScope = - scopeCreator - .constructExpandAndInsertUncheckable( - this, conditionLookupParent, stmt->getEndLoc(), endLoc); - return {lookupParentDiversionScope, + return {conditionLookupParent, "Succeeding code must be in scope of guard variables"}; } @@ -1188,20 +1198,34 @@ void FunctionBodyScope::expandAScopeThatDoesNotCreateANewInsertionPoint( void IfStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint( ScopeCreator &scopeCreator) { + auto *thenStmt = stmt->getThenStmt(); + auto *elseStmt = stmt->getElseStmt(); + + SourceLoc endLoc = thenStmt->getEndLoc(); ASTScopeImpl *insertionPoint = - createNestedConditionalClauseScopes(scopeCreator, stmt->getThenStmt()); + createNestedConditionalClauseScopes(scopeCreator, endLoc); // The 'then' branch - scopeCreator.addToScopeTree(stmt->getThenStmt(), insertionPoint); + scopeCreator.addToScopeTree(thenStmt, insertionPoint); + + // Result builders can add an 'else' block consisting entirely of + // implicit expressions. In this case, the end location of the + // 'then' block is equal to the start location of the 'else' + // block, and the 'else' block source range is empty. + if (elseStmt && + thenStmt->getEndLoc() == elseStmt->getStartLoc() && + elseStmt->getStartLoc() == elseStmt->getEndLoc()) + return; // Add the 'else' branch, if needed. - scopeCreator.addToScopeTree(stmt->getElseStmt(), this); + scopeCreator.addToScopeTree(elseStmt, this); } void WhileStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint( ScopeCreator &scopeCreator) { + SourceLoc endLoc = stmt->getBody()->getEndLoc(); ASTScopeImpl *insertionPoint = - createNestedConditionalClauseScopes(scopeCreator, stmt->getBody()); + createNestedConditionalClauseScopes(scopeCreator, endLoc); scopeCreator.addToScopeTree(stmt->getBody(), insertionPoint); } @@ -1266,8 +1290,10 @@ void CaseStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint( } } - scopeCreator.constructExpandAndInsertUncheckable( - this, stmt); + if (!stmt->getBody()->empty()) { + scopeCreator.constructExpandAndInsertUncheckable( + this, stmt); + } } void CaseLabelItemScope::expandAScopeThatDoesNotCreateANewInsertionPoint( @@ -1409,14 +1435,23 @@ TypeAliasScope::createTrailingWhereClauseScope(ASTScopeImpl *parent, #pragma mark misc ASTScopeImpl *LabeledConditionalStmtScope::createNestedConditionalClauseScopes( - ScopeCreator &scopeCreator, const Stmt *const afterConds) { + ScopeCreator &scopeCreator, SourceLoc endLoc) { auto *stmt = getLabeledConditionalStmt(); ASTScopeImpl *insertionPoint = this; - for (unsigned i = 0; i < stmt->getCond().size(); ++i) { - insertionPoint = - scopeCreator - .constructExpandAndInsertUncheckable( - insertionPoint, stmt, i, afterConds->getStartLoc()); + for (auto &sec : stmt->getCond()) { + switch (sec.getKind()) { + case StmtConditionElement::CK_Availability: + break; + case StmtConditionElement::CK_Boolean: + scopeCreator.addToScopeTree(sec.getBoolean(), insertionPoint); + break; + case StmtConditionElement::CK_PatternBinding: + insertionPoint = + scopeCreator.constructExpandAndInsertUncheckable< + ConditionalClausePatternUseScope>( + insertionPoint, sec, endLoc); + break; + } } return insertionPoint; } diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index c49099bce99d2..396ddf8f658a3 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -272,7 +272,8 @@ bool GenericTypeOrExtensionScope::areMembersVisibleFromWhereClause() const { NullablePtr PatternEntryInitializerScope::getLookupParent() const { auto parent = getParent().get(); - assert(parent->getClassName() == "PatternEntryDeclScope"); + ASTScopeAssert(parent->getClassName() == "PatternEntryDeclScope", + "PatternEntryInitializerScope in unexpected place"); // Lookups from inside a pattern binding initializer skip the parent // scope that introduces bindings bound by the pattern, since we @@ -285,6 +286,23 @@ PatternEntryInitializerScope::getLookupParent() const { return parent->getLookupParent(); } +NullablePtr +ConditionalClauseInitializerScope::getLookupParent() const { + auto parent = getParent().get(); + ASTScopeAssert(parent->getClassName() == "ConditionalClausePatternUseScope", + "ConditionalClauseInitializerScope in unexpected place"); + + // Lookups from inside a conditional clause initializer skip the parent + // scope that introduces bindings bound by the pattern, since we + // want this to work: + // + // func f(x: Int?) { + // guard let x = x else { return } + // print(x) + // } + return parent->getLookupParent(); +} + #pragma mark looking in locals or members - locals bool GenericParamScope::lookupLocalsOrMembers(DeclConsumer consumer) const { @@ -411,7 +429,7 @@ bool ClosureParametersScope::lookupLocalsOrMembers( bool ConditionalClausePatternUseScope::lookupLocalsOrMembers( DeclConsumer consumer) const { - return lookupLocalBindingsInPattern(pattern, consumer); + return lookupLocalBindingsInPattern(sec.getPattern(), consumer); } bool ASTScopeImpl::lookupLocalBindingsInPattern(const Pattern *p, @@ -488,11 +506,7 @@ bool ASTScopeImpl::isLabeledStmtLookupTerminator() const { return true; } -bool LookupParentDiversionScope::isLabeledStmtLookupTerminator() const { - return false; -} - -bool ConditionalClauseScope::isLabeledStmtLookupTerminator() const { +bool GuardStmtBodyScope::isLabeledStmtLookupTerminator() const { return false; } diff --git a/lib/AST/ASTScopePrinting.cpp b/lib/AST/ASTScopePrinting.cpp index 2dafad62d3946..d300d24ded763 100644 --- a/lib/AST/ASTScopePrinting.cpp +++ b/lib/AST/ASTScopePrinting.cpp @@ -176,18 +176,13 @@ void AbstractPatternEntryScope::printSpecifics(llvm::raw_ostream &out) const { }); } -void ConditionalClauseScope::printSpecifics(llvm::raw_ostream &out) const { - ASTScopeImpl::printSpecifics(out); - out << "index " << index; -} - void SubscriptDeclScope::printSpecifics(llvm::raw_ostream &out) const { decl->dumpRef(out); } void ConditionalClausePatternUseScope::printSpecifics( llvm::raw_ostream &out) const { - pattern->print(out); + sec.getPattern()->print(out); } bool GenericTypeOrExtensionScope::doesDeclHaveABody() const { return false; } diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index c377febaef6e7..1fca63ca5bdd8 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -267,20 +267,14 @@ SourceRange PatternEntryInitializerScope::getSourceRangeOfThisASTNode( SourceRange GenericParamScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - auto nOrE = holder; - // A protocol's generic parameter list is not written in source, and - // is visible from the start of the body. - if (auto *protoDecl = dyn_cast(nOrE)) - return SourceRange(protoDecl->getBraces().Start, protoDecl->getEndLoc()); - const auto startLoc = paramList->getSourceRange().Start; - const auto validStartLoc = - startLoc.isValid() ? startLoc : holder->getStartLoc(); - // Since ExtensionScope (whole portion) range doesn't start till after the - // extended nominal, the range here must be pushed back, too. + // We want to ensure the extended type is not part of the generic + // parameter scope. if (auto const *const ext = dyn_cast(holder)) { return SourceRange(getLocAfterExtendedNominal(ext), ext->getEndLoc()); } - return SourceRange(validStartLoc, holder->getEndLoc()); + + // For all other declarations, generic parameters are visible everywhere. + return holder->getSourceRange(); } SourceRange ASTSourceFileScope::getSourceRangeOfThisASTNode( @@ -412,21 +406,14 @@ BraceStmtScope::getSourceRangeOfThisASTNode(const bool omitAssertions) const { return stmt->getSourceRange(); } -SourceRange ConditionalClauseScope::getSourceRangeOfThisASTNode( +SourceRange ConditionalClauseInitializerScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - // From the start of this particular condition to the start of the - // then/body part. - const auto startLoc = getStmtConditionElement().getStartLoc(); - return startLoc.isValid() - ? SourceRange(startLoc, endLoc) - : SourceRange(endLoc); + return initializer->getSourceRange(); } SourceRange ConditionalClausePatternUseScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - // For a guard continuation, the scope extends from the end of the 'else' - // to the end of the continuation. - return SourceRange(startLoc); + return SourceRange(sec.getInitializer()->getStartLoc(), endLoc); } SourceRange @@ -456,9 +443,9 @@ SourceRange GuardStmtScope::getSourceRangeOfThisASTNode( return SourceRange(getStmt()->getStartLoc(), endLoc); } -SourceRange LookupParentDiversionScope::getSourceRangeOfThisASTNode( +SourceRange GuardStmtBodyScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - return SourceRange(startLoc, endLoc); + return body->getSourceRange(); } #pragma mark source range caching diff --git a/test/NameLookup/scope_map-astscopelookup.swift b/test/NameLookup/scope_map-astscopelookup.swift index 7ae9c913028ea..dd72191c6fad2 100644 --- a/test/NameLookup/scope_map-astscopelookup.swift +++ b/test/NameLookup/scope_map-astscopelookup.swift @@ -308,7 +308,7 @@ func HasLabeledDo() { // CHECK-EXPANDED-NEXT: |-BraceStmtScope {{.*}}, [69:53 - 72:3] // CHECK-EXPANDED-NEXT: `-PatternEntryDeclScope {{.*}}, [70:9 - 70:13] entry 0 'c' // CHECK-EXPANDED-NEXT: `-PatternEntryInitializerScope {{.*}}, [70:13 - 70:13] entry 0 'c' -// CHECK-EXPANDED-NEXT: `-LookupParentDiversionScope, [72:3 - 100:38] +// CHECK-EXPANDED-NEXT: `-GuardStmtBodyScope, [72:3 - 100:38] // CHECK-EXPANDED-NEXT: |-WhileStmtScope {{.*}}, [74:3 - 76:3] // CHECK-EXPANDED-NEXT: `-ConditionalClauseScope, [74:9 - 76:3] index 0 // CHECK-EXPANDED-NEXT: `-ConditionalClausePatternUseScope, [74:21 - 76:3] let b3 diff --git a/test/NameLookup/scope_map_top_level.swift b/test/NameLookup/scope_map_top_level.swift index 014c920862e53..f4c22426d66e6 100644 --- a/test/NameLookup/scope_map_top_level.swift +++ b/test/NameLookup/scope_map_top_level.swift @@ -26,7 +26,7 @@ var i: Int = b.my_identity() // CHECK-EXPANDED: ***Complete scope map*** -// CHECK-EXPANDED-NEXT: ASTSourceFileScope {{.*}}, (uncached) [1:1 - 6{{.*}}:1] 'SOURCE_DIR{{[/\\]}}test{{[/\\]}}NameLookup{{[/\\]}}scope_map_top_level.swift' +// CHECK-EXPANDED-NEXT: ASTSourceFileScope {{.*}}, (uncached) [1:1 - 61:1] 'SOURCE_DIR{{[/\\]}}test{{[/\\]}}NameLookup{{[/\\]}}scope_map_top_level.swift' // CHECK-EXPANDED-NEXT: |-NominalTypeDeclScope {{.*}}, [4:1 - 4:13] // CHECK-EXPANDED-NEXT: `-NominalTypeBodyScope {{.*}}, [4:11 - 4:13] // CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [6:1 - 21:28] @@ -36,14 +36,11 @@ var i: Int = b.my_identity() // CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [8:1 - 21:28] // CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [8:1 - 21:28] // CHECK-EXPANDED-NEXT: `-GuardStmtScope {{.*}}, [8:1 - 21:28] -// CHECK-EXPANDED-NEXT: |-ConditionalClauseScope, [8:7 - 8:22] index 0 -// CHECK-EXPANDED-NEXT: `-ConditionalClausePatternUseScope, [8:22 - 8:22] let b{{\??}} -// CHECK-EXPANDED-NEXT: |-BraceStmtScope {{.*}}, [8:22 - 9:1] -// CHECK-EXPANDED-NEXT: `-LookupParentDiversionScope, [9:1 - 21:28] +// CHECK-EXPANDED-NEXT: `-ConditionalClausePatternUseScope, [8:15 - 21:28] let b{{\??}} +// CHECK-EXPANDED-NEXT: |-ConditionalClauseInitializerScope, [8:15 - 8:15] // CHECK-EXPANDED-NEXT: |-AbstractFunctionDeclScope {{.*}}, [11:1 - 11:19] 'foo(x:)' // CHECK-EXPANDED-NEXT: |-ParameterListScope {{.*}}, [11:9 - 11:16] // CHECK-EXPANDED-NEXT: `-FunctionBodyScope {{.*}}, [11:18 - 11:19] -// CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [11:18 - 11:19] // CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [13:1 - 21:28] // CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [13:1 - 21:28] // CHECK-EXPANDED-NEXT: |-PatternEntryDeclScope {{.*}}, [13:5 - 13:9] entry 0 'c' @@ -60,5 +57,4 @@ var i: Int = b.my_identity() // CHECK-EXPANDED-NEXT: `-PatternEntryInitializerScope {{.*}}, [21:14 - 21:28] entry 0 'i' -// REQUIRES: asserts -// absence of assertions can change the "uncached" bit and cause failures + From dac68ca0472de3debebd19a9c6473acfeb395419 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 2 Oct 2020 02:12:16 -0400 Subject: [PATCH 262/745] ASTScope: Fix TopLevelCodeScope source range Top-level code can contain guard statements which introduce bindings until the end of the parent scope, so plumb that through. --- include/swift/AST/ASTScope.h | 16 +++++++++++++--- lib/AST/ASTScopeCreation.cpp | 23 +++++++++++++++++------ lib/AST/ASTScopeSourceRange.cpp | 6 +++--- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 222b66a2167a8..1d4af06aa2ad0 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -1189,8 +1189,10 @@ class ClosureParametersScope final : public ASTScopeImpl { class TopLevelCodeScope final : public ASTScopeImpl { public: TopLevelCodeDecl *const decl; + SourceLoc endLoc; - TopLevelCodeScope(TopLevelCodeDecl *e) : decl(e) {} + TopLevelCodeScope(TopLevelCodeDecl *e, SourceLoc endLoc) + : decl(e), endLoc(endLoc) {} virtual ~TopLevelCodeScope() {} protected: @@ -1643,13 +1645,21 @@ class BraceStmtScope final : public AbstractStmtScope { /// definition. SmallVector localVars; + /// The end location for bindings introduced in this scope. This can + /// extend past the actual end of the BraceStmt in top-level code, + /// where every TopLevelCodeDecl introduces a new scope through the + /// end of the buffer. + SourceLoc endLoc; + public: BraceStmtScope(BraceStmt *e, SmallVector localFuncsAndTypes, - SmallVector localVars) + SmallVector localVars, + SourceLoc endLoc) : stmt(e), localFuncsAndTypes(localFuncsAndTypes), - localVars(localVars) {} + localVars(localVars), + endLoc(endLoc) {} virtual ~BraceStmtScope() {} protected: diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index a1673a0856016..1bb9fff0639a7 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -662,8 +662,9 @@ class NodeAdder NullablePtr visitTopLevelCodeDecl(TopLevelCodeDecl *d, ASTScopeImpl *p, ScopeCreator &scopeCreator) { - return scopeCreator.ifUniqueConstructExpandAndInsert(p, - d); + ASTScopeAssert(endLoc.hasValue(), "TopLevelCodeDecl in wrong place?"); + return scopeCreator.ifUniqueConstructExpandAndInsert( + p, d, *endLoc); } #pragma mark special-case creation @@ -708,9 +709,14 @@ class NodeAdder } } + SourceLoc endLocForBraceStmt = bs->getEndLoc(); + if (endLoc.hasValue()) + endLocForBraceStmt = *endLoc; + auto maybeBraceScope = scopeCreator.ifUniqueConstructExpandAndInsert( - p, bs, std::move(localFuncsAndTypes), std::move(localVars)); + p, bs, std::move(localFuncsAndTypes), std::move(localVars), + endLocForBraceStmt); if (auto *s = scopeCreator.getASTContext().Stats) ++s->getFrontendCounters().NumBraceStmtASTScopes; @@ -989,12 +995,17 @@ ASTSourceFileScope::expandAScopeThatCreatesANewInsertionPoint( ASTScopeAssert(SF, "Must already have a SourceFile."); ArrayRef decls = SF->getTopLevelDecls(); // Assume that decls are only added at the end, in source order + Optional endLoc = None; + if (!decls.empty()) + endLoc = decls.back()->getEndLoc(); + std::vector newNodes(decls.begin(), decls.end()); insertionPoint = scopeCreator.addSiblingsToScopeTree(insertionPoint, scopeCreator.sortBySourceRange( scopeCreator.cull(newNodes)), - None); + endLoc); + // Too slow to perform all the time: // ASTScopeAssert(scopeCreator->containsAllDeclContextsFromAST(), // "ASTScope tree missed some DeclContexts or made some up"); @@ -1123,7 +1134,7 @@ BraceStmtScope::expandAScopeThatCreatesANewInsertionPoint( scopeCreator.sortBySourceRange( scopeCreator.cull( stmt->getElements())), - stmt->getEndLoc()); + endLoc); if (auto *s = scopeCreator.getASTContext().Stats) ++s->getFrontendCounters().NumBraceStmtASTScopeExpansions; return { @@ -1137,7 +1148,7 @@ TopLevelCodeScope::expandAScopeThatCreatesANewInsertionPoint(ScopeCreator & if (auto *body = scopeCreator - .addToScopeTreeAndReturnInsertionPoint(decl->getBody(), this, None) + .addToScopeTreeAndReturnInsertionPoint(decl->getBody(), this, endLoc) .getPtrOrNull()) return {body, "So next top level code scope and put its decls in its body " "under a guard statement scope (etc) from the last top level " diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index 1fca63ca5bdd8..258631729f3a5 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -221,7 +221,7 @@ SourceRange FunctionBodyScope::getSourceRangeOfThisASTNode( SourceRange TopLevelCodeScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - return decl->getSourceRange(); + return SourceRange(decl->getStartLoc(), endLoc); } SourceRange SubscriptDeclScope::getSourceRangeOfThisASTNode( @@ -401,9 +401,9 @@ BraceStmtScope::getSourceRangeOfThisASTNode(const bool omitAssertions) const { // 'in' keyword, when present. if (auto closure = parentClosureIfAny()) { if (closure.get()->getInLoc().isValid()) - return SourceRange(closure.get()->getInLoc(), stmt->getEndLoc()); + return SourceRange(closure.get()->getInLoc(), endLoc); } - return stmt->getSourceRange(); + return SourceRange(stmt->getStartLoc(), endLoc); } SourceRange ConditionalClauseInitializerScope::getSourceRangeOfThisASTNode( From 4252659a048aa7041fb231d6c289ca23bb8a6a82 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 2 Oct 2020 00:54:56 -0400 Subject: [PATCH 263/745] ASTScope: Add a getCharSourceRangeOfScope() method to ASTScopeImpl --- include/swift/AST/ASTScope.h | 6 ++++++ lib/AST/ASTScopeSourceRange.cpp | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 1d4af06aa2ad0..eae28785f0940 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -157,6 +157,8 @@ class ASTScopeImpl { // source range. So include their ranges here SourceRange sourceRangeOfIgnoredASTNodes; + mutable Optional cachedCharSourceRange; + #pragma mark - constructor / destructor public: ASTScopeImpl(){}; @@ -212,6 +214,10 @@ class ASTScopeImpl { SourceRange getSourceRangeOfScope(bool omitAssertions = false) const; + CharSourceRange getCharSourceRangeOfScope(SourceManager &SM, + bool omitAssertions = false) const; + bool isCharSourceRangeCached() const; + /// InterpolatedStringLiteralExprs and EditorPlaceHolders respond to /// getSourceRange with the starting point. But we might be asked to lookup an /// identifer within one of them. So, find the real source range of them here. diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index 258631729f3a5..f0ebd5275887c 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -450,6 +450,27 @@ SourceRange GuardStmtBodyScope::getSourceRangeOfThisASTNode( #pragma mark source range caching +CharSourceRange +ASTScopeImpl::getCharSourceRangeOfScope(SourceManager &SM, + bool omitAssertions) const { + if (!isCharSourceRangeCached()) { + auto range = getSourceRangeOfThisASTNode(omitAssertions); + ASTScopeAssert(range.isValid(), "scope has invalid source range"); + ASTScopeAssert(SM.isBeforeInBuffer(range.Start, range.End) || + range.Start == range.End, + "scope source range ends before start"); + + cachedCharSourceRange = + Lexer::getCharSourceRangeFromSourceRange(SM, range); + } + + return *cachedCharSourceRange; +} + +bool ASTScopeImpl::isCharSourceRangeCached() const { + return cachedCharSourceRange.hasValue(); +} + SourceRange ASTScopeImpl::getSourceRangeOfScope(const bool omitAssertions) const { if (!isSourceRangeCached(omitAssertions)) From b8cccb1ef83397a3e1b7267dfe27738cee90e19d Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 6 Oct 2020 00:55:12 -0400 Subject: [PATCH 264/745] ASTScope: Fix SourceFileScope source range A SourceFile might contain TopLevelCodeDecls with guard statements, which introduce names until the end of the file, so plumb that through. --- lib/AST/ASTScopeCreation.cpp | 6 ++---- test/NameLookup/scope_map_top_level.swift | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 1bb9fff0639a7..eda1e0c69cbb0 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -994,10 +994,8 @@ ASTSourceFileScope::expandAScopeThatCreatesANewInsertionPoint( ScopeCreator &scopeCreator) { ASTScopeAssert(SF, "Must already have a SourceFile."); ArrayRef decls = SF->getTopLevelDecls(); - // Assume that decls are only added at the end, in source order - Optional endLoc = None; - if (!decls.empty()) - endLoc = decls.back()->getEndLoc(); + + SourceLoc endLoc = getSourceRangeOfThisASTNode().End; std::vector newNodes(decls.begin(), decls.end()); insertionPoint = diff --git a/test/NameLookup/scope_map_top_level.swift b/test/NameLookup/scope_map_top_level.swift index f4c22426d66e6..07f94a67846bc 100644 --- a/test/NameLookup/scope_map_top_level.swift +++ b/test/NameLookup/scope_map_top_level.swift @@ -29,20 +29,20 @@ var i: Int = b.my_identity() // CHECK-EXPANDED-NEXT: ASTSourceFileScope {{.*}}, (uncached) [1:1 - 61:1] 'SOURCE_DIR{{[/\\]}}test{{[/\\]}}NameLookup{{[/\\]}}scope_map_top_level.swift' // CHECK-EXPANDED-NEXT: |-NominalTypeDeclScope {{.*}}, [4:1 - 4:13] // CHECK-EXPANDED-NEXT: `-NominalTypeBodyScope {{.*}}, [4:11 - 4:13] -// CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [6:1 - 21:28] -// CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [6:1 - 21:28] +// CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [6:1 - 61:1] +// CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [6:1 - 61:1] // CHECK-EXPANDED-NEXT: |-PatternEntryDeclScope {{.*}}, [6:5 - 6:15] entry 0 'a' // CHECK-EXPANDED-NEXT: `-PatternEntryInitializerScope {{.*}}, [6:15 - 6:15] entry 0 'a' -// CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [8:1 - 21:28] -// CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [8:1 - 21:28] -// CHECK-EXPANDED-NEXT: `-GuardStmtScope {{.*}}, [8:1 - 21:28] -// CHECK-EXPANDED-NEXT: `-ConditionalClausePatternUseScope, [8:15 - 21:28] let b{{\??}} +// CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [8:1 - 61:1] +// CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [8:1 - 61:1] +// CHECK-EXPANDED-NEXT: `-GuardStmtScope {{.*}}, [8:1 - 61:1] +// CHECK-EXPANDED-NEXT: `-ConditionalClausePatternUseScope, [8:15 - 61:1] let b{{\??}} // CHECK-EXPANDED-NEXT: |-ConditionalClauseInitializerScope, [8:15 - 8:15] // CHECK-EXPANDED-NEXT: |-AbstractFunctionDeclScope {{.*}}, [11:1 - 11:19] 'foo(x:)' // CHECK-EXPANDED-NEXT: |-ParameterListScope {{.*}}, [11:9 - 11:16] // CHECK-EXPANDED-NEXT: `-FunctionBodyScope {{.*}}, [11:18 - 11:19] -// CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [13:1 - 21:28] -// CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [13:1 - 21:28] +// CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [13:1 - 61:1] +// CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [13:1 - 61:1] // CHECK-EXPANDED-NEXT: |-PatternEntryDeclScope {{.*}}, [13:5 - 13:9] entry 0 'c' // CHECK-EXPANDED-NEXT: `-PatternEntryInitializerScope {{.*}}, [13:9 - 13:9] entry 0 'c' // CHECK-EXPANDED-NEXT: |-TypeAliasDeclScope {{.*}}, [15:1 - 15:15] @@ -51,8 +51,8 @@ var i: Int = b.my_identity() // CHECK-EXPANDED-NEXT: `-AbstractFunctionDeclScope {{.*}}, [18:3 - 18:43] 'my_identity()' // CHECK-EXPANDED-NEXT: `-FunctionBodyScope {{.*}}, [18:29 - 18:43] // CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [18:29 - 18:43] -// CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [21:1 - 21:28] -// CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [21:1 - 21:28] +// CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [21:1 - 61:1] +// CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [21:1 - 61:1] // CHECK-EXPANDED-NEXT: `-PatternEntryDeclScope {{.*}}, [21:5 - 21:28] entry 0 'i' // CHECK-EXPANDED-NEXT: `-PatternEntryInitializerScope {{.*}}, [21:14 - 21:28] entry 0 'i' From 771fd6e9d102c2baead48dc8557d5edc24007a89 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 2 Oct 2020 02:44:17 -0400 Subject: [PATCH 265/745] ASTScope: Redo assertions to look at CharSourceRanges --- include/swift/AST/ASTScope.h | 8 +- lib/AST/ASTScopeCreation.cpp | 13 +- lib/AST/ASTScopePrinting.cpp | 4 +- lib/AST/ASTScopeSourceRange.cpp | 164 ++++++---------------- test/NameLookup/scope_map_top_level.swift | 2 +- 5 files changed, 52 insertions(+), 139 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index eae28785f0940..f02cd3aa3c84a 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -273,14 +273,10 @@ class ASTScopeImpl { protected: SourceManager &getSourceManager() const; - bool hasValidSourceRange() const; - bool hasValidSourceRangeOfIgnoredASTNodes() const; - bool precedesInSource(const ASTScopeImpl *) const; - bool verifyThatChildrenAreContainedWithin(SourceRange) const; - bool verifyThatThisNodeComeAfterItsPriorSibling() const; private: - bool checkSourceRangeAfterExpansion(const ASTContext &) const; + void checkSourceRangeBeforeAddingChild(ASTScopeImpl *child, + const ASTContext &ctx) const; #pragma mark common queries public: diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index eda1e0c69cbb0..f5d5c2ca651b7 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -334,8 +334,6 @@ class ScopeCreator final { ASTScopeImpl *insertionPoint = child->expandAndBeCurrentDetectingRecursion(*this); - ASTScopeAssert(child->verifyThatThisNodeComeAfterItsPriorSibling(), - "Ensure search will work"); return insertionPoint; } @@ -881,6 +879,13 @@ ScopeCreator::addPatternBindingToScopeTree(PatternBindingDecl *patternBinding, #pragma mark creation helpers void ASTScopeImpl::addChild(ASTScopeImpl *child, ASTContext &ctx) { + ASTScopeAssert(!child->getParent(), "child should not already have parent"); + child->parent = this; + +#ifndef NDEBUG + checkSourceRangeBeforeAddingChild(child, ctx); +#endif + // If this is the first time we've added children, notify the ASTContext // that there's a SmallVector that needs to be cleaned up. // FIXME: If we had access to SmallVector::isSmall(), we could do better. @@ -889,8 +894,6 @@ void ASTScopeImpl::addChild(ASTScopeImpl *child, ASTContext &ctx) { haveAddedCleanup = true; } storedChildren.push_back(child); - ASTScopeAssert(!child->getParent(), "child should not already have parent"); - child->parent = this; clearCachedSourceRangesOfMeAndAncestors(); } @@ -926,8 +929,6 @@ ASTScopeImpl *ASTScopeImpl::expandAndBeCurrent(ScopeCreator &scopeCreator) { setWasExpanded(); - ASTScopeAssert(checkSourceRangeAfterExpansion(scopeCreator.getASTContext()), - "Bad range."); return insertionPoint; } diff --git a/lib/AST/ASTScopePrinting.cpp b/lib/AST/ASTScopePrinting.cpp index d300d24ded763..b9f84266768e4 100644 --- a/lib/AST/ASTScopePrinting.cpp +++ b/lib/AST/ASTScopePrinting.cpp @@ -123,9 +123,7 @@ static void printSourceRange(llvm::raw_ostream &out, const SourceRange range, } void ASTScopeImpl::printRange(llvm::raw_ostream &out) const { - if (!isSourceRangeCached(true)) - out << "(uncached) "; - SourceRange range = computeSourceRangeOfScope(/*omitAssertions=*/true); + SourceRange range = getSourceRangeOfThisASTNode(/*omitAssertions=*/true); printSourceRange(out, range, getSourceManager()); } diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index f0ebd5275887c..66e1270da73f3 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -79,113 +79,59 @@ ASTScopeImpl::widenSourceRangeForChildren(const SourceRange range, return r; } -bool ASTScopeImpl::checkSourceRangeAfterExpansion(const ASTContext &ctx) const { - ASTScopeAssert(getSourceRangeOfThisASTNode().isValid() || - !getChildren().empty(), - "need to be able to find source range"); - ASTScopeAssert(verifyThatChildrenAreContainedWithin(getSourceRangeOfScope()), - "Search will fail"); - ASTScopeAssert( - checkLazySourceRange(ctx), - "Lazy scopes must have compatible ranges before and after expansion"); +void ASTScopeImpl::checkSourceRangeBeforeAddingChild(ASTScopeImpl *child, + const ASTContext &ctx) const { + auto &sourceMgr = ctx.SourceMgr; - return true; -} - -#pragma mark validation & debugging + auto range = getCharSourceRangeOfScope(sourceMgr); -bool ASTScopeImpl::hasValidSourceRange() const { - const auto sourceRange = getSourceRangeOfScope(); - return sourceRange.Start.isValid() && sourceRange.End.isValid() && - !getSourceManager().isBeforeInBuffer(sourceRange.End, - sourceRange.Start); -} + auto childCharRange = child->getCharSourceRangeOfScope(sourceMgr); -bool ASTScopeImpl::hasValidSourceRangeOfIgnoredASTNodes() const { - return sourceRangeOfIgnoredASTNodes.isValid(); -} + bool childContainedInParent = [&]() { + // HACK: For code completion. Handle replaced range. + if (const auto &replacedRange = sourceMgr.getReplacedRange()) { + auto originalRange = Lexer::getCharSourceRangeFromSourceRange( + sourceMgr, replacedRange.Original); + auto newRange = Lexer::getCharSourceRangeFromSourceRange( + sourceMgr, replacedRange.New); -bool ASTScopeImpl::precedesInSource(const ASTScopeImpl *next) const { - if (!hasValidSourceRange() || !next->hasValidSourceRange()) - return false; - return !getSourceManager().isBeforeInBuffer( - next->getSourceRangeOfScope().Start, getSourceRangeOfScope().End); -} + if (range.contains(originalRange) && + newRange.contains(childCharRange)) + return true; + } -bool ASTScopeImpl::verifyThatChildrenAreContainedWithin( - const SourceRange range) const { - // assumes children are already in order - if (getChildren().empty()) - return true; - const SourceRange rangeOfChildren = - SourceRange(getChildren().front()->getSourceRangeOfScope().Start, - getChildren().back()->getSourceRangeOfScope().End); - if (getSourceManager().rangeContains(range, rangeOfChildren)) - return true; + return range.contains(childCharRange); + }(); - // HACK: For code completion. Handle replaced range. - if (const auto &replacedRange = getSourceManager().getReplacedRange()) { - if (getSourceManager().rangeContains(replacedRange.Original, range) && - getSourceManager().rangeContains(replacedRange.New, rangeOfChildren)) - return true; + if (!childContainedInParent) { + auto &out = verificationError() << "child not contained in its parent:\n"; + child->print(out); + out << "\n***Parent node***\n"; + this->print(out); + abort(); } - auto &out = verificationError() << "children not contained in its parent\n"; - if (getChildren().size() == 1) { - out << "\n***Only Child node***\n"; - getChildren().front()->print(out); - } else { - out << "\n***First Child node***\n"; - getChildren().front()->print(out); - out << "\n***Last Child node***\n"; - getChildren().back()->print(out); - } - out << "\n***Parent node***\n"; - this->print(out); - abort(); -} - -bool ASTScopeImpl::verifyThatThisNodeComeAfterItsPriorSibling() const { - auto priorSibling = getPriorSibling(); - if (!priorSibling) - return true; - if (priorSibling.get()->precedesInSource(this)) - return true; - auto &out = verificationError() << "unexpected out-of-order nodes\n"; - out << "\n***Penultimate child node***\n"; - priorSibling.get()->print(out); - out << "\n***Last Child node***\n"; - print(out); - out << "\n***Parent node***\n"; - getParent().get()->print(out); - // llvm::errs() << "\n\nsource:\n" - // << getSourceManager() - // .getRangeForBuffer( - // getSourceFile()->getBufferID().getValue()) - // .str(); - ASTScope_unreachable("unexpected out-of-order nodes"); - return false; -} - -NullablePtr ASTScopeImpl::getPriorSibling() const { - auto parent = getParent(); - if (!parent) - return nullptr; - auto const &siblingsAndMe = parent.get()->getChildren(); - // find myIndex, which is probably the last one - int myIndex = -1; - for (int i = siblingsAndMe.size() - 1; i >= 0; --i) { - if (siblingsAndMe[i] == this) { - myIndex = i; - break; + if (!storedChildren.empty()) { + auto previousChild = storedChildren.back(); + auto endOfPreviousChild = previousChild->getCharSourceRangeOfScope( + sourceMgr).getEnd(); + + if (childCharRange.getStart() != endOfPreviousChild && + !sourceMgr.isBeforeInBuffer(endOfPreviousChild, + childCharRange.getStart())) { + auto &out = verificationError() << "child overlaps previous child:\n"; + child->print(out); + out << "\n***Previous child\n"; + previousChild->print(out); + out << "\n***Parent node***\n"; + this->print(out); + abort(); } } - ASTScopeAssert(myIndex != -1, "I have been disowned!"); - if (myIndex == 0) - return nullptr; - return siblingsAndMe[myIndex - 1]; } +#pragma mark validation & debugging + bool ASTScopeImpl::doesRangeMatch(unsigned start, unsigned end, StringRef file, StringRef className) { if (!className.empty() && className != getClassName()) @@ -509,34 +455,6 @@ void ASTScopeImpl::computeAndCacheSourceRangeOfScope( cachedSourceRange = computeSourceRangeOfScope(omitAssertions); } -bool ASTScopeImpl::checkLazySourceRange(const ASTContext &ctx) const { - const auto unexpandedRange = sourceRangeForDeferredExpansion(); - const auto expandedRange = computeSourceRangeOfScopeWithChildASTNodes(); - if (unexpandedRange.isInvalid() || expandedRange.isInvalid()) - return true; - if (unexpandedRange == expandedRange) - return true; - - llvm::errs() << "*** Lazy range problem. Parent unexpanded: ***\n"; - unexpandedRange.print(llvm::errs(), getSourceManager(), false); - llvm::errs() << "\n"; - if (!getChildren().empty()) { - llvm::errs() << "*** vs last child: ***\n"; - auto b = getChildren().back()->computeSourceRangeOfScope(); - b.print(llvm::errs(), getSourceManager(), false); - llvm::errs() << "\n"; - } - else if (hasValidSourceRangeOfIgnoredASTNodes()) { - llvm::errs() << "*** vs ignored AST nodes: ***\n"; - sourceRangeOfIgnoredASTNodes.print(llvm::errs(), getSourceManager(), false); - llvm::errs() << "\n"; - } - print(llvm::errs(), 0, false); - llvm::errs() << "\n"; - - return false; -} - SourceRange ASTScopeImpl::computeSourceRangeOfScope(const bool omitAssertions) const { // If we don't need to consider children, it's cheaper diff --git a/test/NameLookup/scope_map_top_level.swift b/test/NameLookup/scope_map_top_level.swift index 07f94a67846bc..a6c3ef3be74a4 100644 --- a/test/NameLookup/scope_map_top_level.swift +++ b/test/NameLookup/scope_map_top_level.swift @@ -26,7 +26,7 @@ var i: Int = b.my_identity() // CHECK-EXPANDED: ***Complete scope map*** -// CHECK-EXPANDED-NEXT: ASTSourceFileScope {{.*}}, (uncached) [1:1 - 61:1] 'SOURCE_DIR{{[/\\]}}test{{[/\\]}}NameLookup{{[/\\]}}scope_map_top_level.swift' +// CHECK-EXPANDED-NEXT: ASTSourceFileScope {{.*}} [1:1 - 61:1] 'SOURCE_DIR{{[/\\]}}test{{[/\\]}}NameLookup{{[/\\]}}scope_map_top_level.swift' // CHECK-EXPANDED-NEXT: |-NominalTypeDeclScope {{.*}}, [4:1 - 4:13] // CHECK-EXPANDED-NEXT: `-NominalTypeBodyScope {{.*}}, [4:11 - 4:13] // CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [6:1 - 61:1] From e52413f913aca863884f2ca10c691814b0ef936a Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 2 Oct 2020 02:44:27 -0400 Subject: [PATCH 266/745] ASTScope: Use CharSourceRanges for lookup --- lib/AST/ASTScopeLookup.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index 396ddf8f658a3..6a8d25d13c2e9 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -83,11 +83,11 @@ bool ASTScopeImpl::checkSourceRangeOfThisASTNode() const { /// If the \p loc is in a new buffer but \p range is not, consider the location /// is at the start of replaced range. Otherwise, returns \p loc as is. static SourceLoc translateLocForReplacedRange(SourceManager &sourceMgr, - SourceRange range, + CharSourceRange range, SourceLoc loc) { if (const auto &replacedRange = sourceMgr.getReplacedRange()) { if (sourceMgr.rangeContainsTokenLoc(replacedRange.New, loc) && - !sourceMgr.rangeContains(replacedRange.New, range)) { + !sourceMgr.rangeContainsTokenLoc(replacedRange.New, range.getStart())) { return replacedRange.Original.Start; } } @@ -101,17 +101,19 @@ ASTScopeImpl::findChildContaining(SourceLoc loc, auto *const *child = llvm::lower_bound( getChildren(), loc, [&sourceMgr](const ASTScopeImpl *scope, SourceLoc loc) { - ASTScopeAssert(scope->checkSourceRangeOfThisASTNode(), "Bad range."); - auto rangeOfScope = scope->getSourceRangeOfScope(); + auto rangeOfScope = scope->getCharSourceRangeOfScope(sourceMgr); + ASTScopeAssert(!sourceMgr.isBeforeInBuffer(rangeOfScope.getEnd(), + rangeOfScope.getStart()), + "Source range is backwards"); loc = translateLocForReplacedRange(sourceMgr, rangeOfScope, loc); - return -1 == ASTScopeImpl::compare(rangeOfScope, loc, sourceMgr, - /*ensureDisjoint=*/false); + return (rangeOfScope.getEnd() == loc || + sourceMgr.isBeforeInBuffer(rangeOfScope.getEnd(), loc)); }); if (child != getChildren().end()) { - auto rangeOfScope = (*child)->getSourceRangeOfScope(); + auto rangeOfScope = (*child)->getCharSourceRangeOfScope(sourceMgr); loc = translateLocForReplacedRange(sourceMgr, rangeOfScope, loc); - if (sourceMgr.rangeContainsTokenLoc(rangeOfScope, loc)) + if (rangeOfScope.contains(loc)) return *child; } From 996100cfd1fc93ae92c587a3476988ea9a400bb3 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 6 Oct 2020 15:27:02 -0400 Subject: [PATCH 267/745] ASTScope: Remove old SourceRange machinery --- include/swift/AST/ASTScope.h | 86 +---------- lib/AST/ASTScope.cpp | 21 +-- lib/AST/ASTScopeCreation.cpp | 7 +- lib/AST/ASTScopeLookup.cpp | 8 -- lib/AST/ASTScopeSourceRange.cpp | 245 -------------------------------- 5 files changed, 5 insertions(+), 362 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index f02cd3aa3c84a..0e1f65e71625d 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -141,7 +141,6 @@ class ASTScopeImpl { ASTScopeImpl *parent = nullptr; // null at the root /// Child scopes, sorted by source range. - /// Must clear source range change whenever this changes Children storedChildren; bool wasExpanded = false; @@ -149,14 +148,6 @@ class ASTScopeImpl { /// Can clear storedChildren, so must remember this bool haveAddedCleanup = false; - // Must be updated after last child is added and after last child's source - // position is known - mutable Optional cachedSourceRange; - - // When ignoring ASTNodes in a scope, they still must count towards a scope's - // source range. So include their ranges here - SourceRange sourceRangeOfIgnoredASTNodes; - mutable Optional cachedCharSourceRange; #pragma mark - constructor / destructor @@ -194,9 +185,6 @@ class ASTScopeImpl { public: void addChild(ASTScopeImpl *child, ASTContext &); -private: - NullablePtr getPriorSibling() const; - public: void preOrderDo(function_ref); /// Like preorderDo but without myself. @@ -205,67 +193,15 @@ class ASTScopeImpl { #pragma mark - source ranges -#pragma mark - source range queries - public: /// Return signum of ranges. Centralize the invariant that ASTScopes use ends. static int compare(SourceRange, SourceRange, const SourceManager &, bool ensureDisjoint); - SourceRange getSourceRangeOfScope(bool omitAssertions = false) const; - CharSourceRange getCharSourceRangeOfScope(SourceManager &SM, bool omitAssertions = false) const; bool isCharSourceRangeCached() const; - /// InterpolatedStringLiteralExprs and EditorPlaceHolders respond to - /// getSourceRange with the starting point. But we might be asked to lookup an - /// identifer within one of them. So, find the real source range of them here. - SourceRange getEffectiveSourceRange(ASTNode) const; - - void computeAndCacheSourceRangeOfScope(bool omitAssertions = false) const; - bool isSourceRangeCached(bool omitAssertions = false) const; - - bool checkSourceRangeOfThisASTNode() const; - - /// For debugging - bool doesRangeMatch(unsigned start, unsigned end, StringRef file = "", - StringRef className = ""); - - unsigned countDescendants() const; - - /// Make sure that when the argument is executed, there are as many - /// descendants after as before. - void assertThatTreeDoesNotShrink(function_ref); - -private: - SourceRange computeSourceRangeOfScope(bool omitAssertions = false) const; - SourceRange - computeSourceRangeOfScopeWithChildASTNodes(bool omitAssertions = false) const; - bool ensureNoAncestorsSourceRangeIsCached() const; - -#pragma mark - source range adjustments -private: - SourceRange widenSourceRangeForIgnoredASTNodes(SourceRange range) const; - - /// If the scope refers to a Decl whose source range tells the whole story, - /// for example a NominalTypeScope, it is not necessary to widen the source - /// range by examining the children. In that case we could just return - /// the childlessRange here. - /// But, we have not marked such scopes yet. Doing so would be an - /// optimization. - SourceRange widenSourceRangeForChildren(SourceRange range, - bool omitAssertions) const; - - /// Even ASTNodes that do not form scopes must be included in a Scope's source - /// range. Widen the source range of the receiver to include the (ignored) - /// node. - void widenSourceRangeForIgnoredASTNode(ASTNode); - -private: - void clearCachedSourceRangesOfMeAndAncestors(); - -public: // public for debugging /// Returns source range of this node alone, without factoring in any /// children. virtual SourceRange @@ -331,19 +267,11 @@ class ASTScopeImpl { void setWasExpanded() { wasExpanded = true; } virtual ASTScopeImpl *expandSpecifically(ScopeCreator &) = 0; -private: - /// Compare the pre-expasion range with the post-expansion range and return - /// false if lazyiness couild miss lookups. - bool checkLazySourceRange(const ASTContext &) const; - public: /// Some scopes can be expanded lazily. - /// Such scopes must: not change their source ranges after expansion, and - /// their expansion must return an insertion point outside themselves. - /// After a node is expanded, its source range (getSourceRangeofThisASTNode - /// union children's ranges) must be same as this. + /// Such scopes must return an insertion point outside themselves when + /// expanded. virtual NullablePtr insertionPointForDeferredExpansion(); - virtual SourceRange sourceRangeForDeferredExpansion() const; private: virtual ScopeCreator &getScopeCreator(); @@ -547,8 +475,6 @@ class Portion { virtual NullablePtr insertionPointForDeferredExpansion(IterableTypeScope *) const = 0; - virtual SourceRange - sourceRangeForDeferredExpansion(const IterableTypeScope *) const = 0; }; // For the whole Decl scope of a GenericType or an Extension @@ -572,8 +498,6 @@ class Portion { NullablePtr insertionPointForDeferredExpansion(IterableTypeScope *) const override; - SourceRange - sourceRangeForDeferredExpansion(const IterableTypeScope *) const override; }; /// GenericTypeOrExtension = GenericType or Extension @@ -605,8 +529,6 @@ class GenericTypeOrExtensionWherePortion final NullablePtr insertionPointForDeferredExpansion(IterableTypeScope *) const override; - SourceRange - sourceRangeForDeferredExpansion(const IterableTypeScope *) const override; }; /// Behavior specific to representing the Body of a NominalTypeDecl or @@ -624,8 +546,6 @@ class IterableTypeBodyPortion final NullablePtr insertionPointForDeferredExpansion(IterableTypeScope *) const override; - SourceRange - sourceRangeForDeferredExpansion(const IterableTypeScope *) const override; }; /// GenericType or Extension scope @@ -722,7 +642,6 @@ class IterableTypeScope : public GenericTypeScope { public: NullablePtr insertionPointForDeferredExpansion() override; - SourceRange sourceRangeForDeferredExpansion() const override; void countBodies(ScopeCreator &) const; }; @@ -931,7 +850,6 @@ class FunctionBodyScope : public ASTScopeImpl { public: std::string getClassName() const override; NullablePtr insertionPointForDeferredExpansion() override; - SourceRange sourceRangeForDeferredExpansion() const override; }; class DefaultArgumentInitializerScope final : public ASTScopeImpl { diff --git a/lib/AST/ASTScope.cpp b/lib/AST/ASTScope.cpp index ec6868c2c4412..50c48de5bea68 100644 --- a/lib/AST/ASTScope.cpp +++ b/lib/AST/ASTScope.cpp @@ -197,23 +197,4 @@ void ASTScopeImpl::postOrderDo(function_ref fn) { for (auto *child : getChildren()) child->postOrderDo(fn); fn(this); -} - -unsigned ASTScopeImpl::countDescendants() const { - unsigned count = 0; - const_cast(this)->preOrderDo( - [&](ASTScopeImpl *) { ++count; }); - return count - 1; -} - -// Can fail if a subscope is lazy and not reexpanded -void ASTScopeImpl::assertThatTreeDoesNotShrink(function_ref fn) { -#ifndef NDEBUG - unsigned beforeCount = countDescendants(); -#endif - fn(); -#ifndef NDEBUG - unsigned afterCount = countDescendants(); - ASTScopeAssert(beforeCount <= afterCount, "shrank?!"); -#endif -} +} \ No newline at end of file diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index f5d5c2ca651b7..bbee272aed3ae 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -572,7 +572,6 @@ class NodeAdder #define VISIT_AND_IGNORE(What) \ NullablePtr visit##What(What *w, ASTScopeImpl *p, \ ScopeCreator &) { \ - p->widenSourceRangeForIgnoredASTNode(w); \ return p; \ } @@ -766,10 +765,9 @@ class NodeAdder NullablePtr visitExpr(Expr *expr, ASTScopeImpl *p, ScopeCreator &scopeCreator) { - if (expr) { - p->widenSourceRangeForIgnoredASTNode(expr); + if (expr) scopeCreator.addExprToScopeTree(expr, p); - } + return p; } }; @@ -894,7 +892,6 @@ void ASTScopeImpl::addChild(ASTScopeImpl *child, ASTContext &ctx) { haveAddedCleanup = true; } storedChildren.push_back(child); - clearCachedSourceRangesOfMeAndAncestors(); } #pragma mark implementations of expansion diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index 6a8d25d13c2e9..39db7f27c725d 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -72,14 +72,6 @@ ASTScopeImpl *ASTScopeImpl::findInnermostEnclosingScopeImpl( scopeCreator); } -bool ASTScopeImpl::checkSourceRangeOfThisASTNode() const { - const auto r = getSourceRangeOfThisASTNode(); - (void)r; - ASTScopeAssert(!getSourceManager().isBeforeInBuffer(r.End, r.Start), - "Range is backwards."); - return true; -} - /// If the \p loc is in a new buffer but \p range is not, consider the location /// is at the start of replaced range. Otherwise, returns \p loc as is. static SourceLoc translateLocForReplacedRange(SourceManager &sourceMgr, diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index 66e1270da73f3..d8a27d5b749cf 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -36,49 +36,8 @@ using namespace swift; using namespace ast_scope; -static SourceLoc getLocEncompassingPotentialLookups(const SourceManager &, - SourceLoc endLoc); static SourceLoc getLocAfterExtendedNominal(const ExtensionDecl *); -SourceRange ASTScopeImpl::widenSourceRangeForIgnoredASTNodes( - const SourceRange range) const { - if (range.isInvalid()) - return sourceRangeOfIgnoredASTNodes; - auto r = range; - if (sourceRangeOfIgnoredASTNodes.isValid()) - r.widen(sourceRangeOfIgnoredASTNodes); - return r; -} - -SourceRange -ASTScopeImpl::widenSourceRangeForChildren(const SourceRange range, - const bool omitAssertions) const { - if (getChildren().empty()) { - ASTScopeAssert(omitAssertions || range.Start.isValid(), "Bad range."); - return range; - } - const auto childStart = - getChildren().front()->getSourceRangeOfScope(omitAssertions).Start; - const auto childEnd = - getChildren().back()->getSourceRangeOfScope(omitAssertions).End; - auto childRange = SourceRange(childStart, childEnd); - ASTScopeAssert(omitAssertions || childRange.isValid(), "Bad range."); - - if (range.isInvalid()) - return childRange; - auto r = range; - - // HACK: For code completion. If the range of the child is from another - // source buffer, don't widen using that range. - if (const auto &replacedRange = getSourceManager().getReplacedRange()) { - if (getSourceManager().rangeContains(replacedRange.Original, range) && - getSourceManager().rangeContains(replacedRange.New, childRange)) - return r; - } - r.widen(childRange); - return r; -} - void ASTScopeImpl::checkSourceRangeBeforeAddingChild(ASTScopeImpl *child, const ASTContext &ctx) const { auto &sourceMgr = ctx.SourceMgr; @@ -130,24 +89,6 @@ void ASTScopeImpl::checkSourceRangeBeforeAddingChild(ASTScopeImpl *child, } } -#pragma mark validation & debugging - -bool ASTScopeImpl::doesRangeMatch(unsigned start, unsigned end, StringRef file, - StringRef className) { - if (!className.empty() && className != getClassName()) - return false; - const auto &SM = getSourceManager(); - const auto r = getSourceRangeOfScope(true); - if (start && start != SM.getLineAndColumnInBuffer(r.Start).first) - return false; - if (end && end != SM.getLineAndColumnInBuffer(r.End).first) - return false; - if (file.empty()) - return true; - const auto buf = SM.findBufferContainingLoc(r.Start); - return SM.getIdentifierForBuffer(buf).endswith(file); -} - #pragma mark getSourceRangeOfThisASTNode SourceRange SpecializeAttributeScope::getSourceRangeOfThisASTNode( @@ -417,192 +358,6 @@ bool ASTScopeImpl::isCharSourceRangeCached() const { return cachedCharSourceRange.hasValue(); } -SourceRange -ASTScopeImpl::getSourceRangeOfScope(const bool omitAssertions) const { - if (!isSourceRangeCached(omitAssertions)) - computeAndCacheSourceRangeOfScope(omitAssertions); - return *cachedSourceRange; -} - -bool ASTScopeImpl::isSourceRangeCached(const bool omitAssertions) const { - const bool isCached = cachedSourceRange.hasValue(); - ASTScopeAssert(omitAssertions || isCached || - ensureNoAncestorsSourceRangeIsCached(), - "Cached ancestor's range likely is obsolete."); - return isCached; -} - -bool ASTScopeImpl::ensureNoAncestorsSourceRangeIsCached() const { - if (const auto *const p = getParent().getPtrOrNull()) { - auto r = !p->isSourceRangeCached(true) && - p->ensureNoAncestorsSourceRangeIsCached(); - if (!r) - ASTScope_unreachable("found a violation"); - return true; - } - return true; -} - -void ASTScopeImpl::computeAndCacheSourceRangeOfScope( - const bool omitAssertions) const { - // In order to satisfy the invariant that, if my range is uncached, - // my parent's range is uncached, (which is needed to optimize invalidation - // by obviating the need to uncache all the way to the root every time), - // when caching a range, must ensure all children's ranges are cached. - for (auto *c : getChildren()) - c->computeAndCacheSourceRangeOfScope(omitAssertions); - - cachedSourceRange = computeSourceRangeOfScope(omitAssertions); -} - -SourceRange -ASTScopeImpl::computeSourceRangeOfScope(const bool omitAssertions) const { - // If we don't need to consider children, it's cheaper - const auto deferredRange = sourceRangeForDeferredExpansion(); - return deferredRange.isValid() - ? deferredRange - : computeSourceRangeOfScopeWithChildASTNodes(omitAssertions); -} - -SourceRange ASTScopeImpl::computeSourceRangeOfScopeWithChildASTNodes( - const bool omitAssertions) const { - const auto rangeOfJustThisASTNode = - getSourceRangeOfThisASTNode(omitAssertions); - const auto rangeIncludingIgnoredNodes = - widenSourceRangeForIgnoredASTNodes(rangeOfJustThisASTNode); - const auto uncachedSourceRange = - widenSourceRangeForChildren(rangeIncludingIgnoredNodes, omitAssertions); - return uncachedSourceRange; -} - -void ASTScopeImpl::clearCachedSourceRangesOfMeAndAncestors() { - // An optimization: if my range isn't cached, my ancestors must not be - if (!isSourceRangeCached()) - return; - cachedSourceRange = None; - if (auto p = getParent()) - p.get()->clearCachedSourceRangesOfMeAndAncestors(); -} - -#pragma mark compensating for InterpolatedStringLiteralExprs and EditorPlaceHolders - -static bool isInterpolatedStringLiteral(const Token& tok) { - SmallVector Segments; - Lexer::getStringLiteralSegments(tok, Segments, nullptr); - return Segments.size() != 1 || - Segments.front().Kind != Lexer::StringSegment::Literal; -} - -/// If right brace is missing, the source range of the body will end -/// at the last token, which may be a one of the special cases below. -/// This work is only needed for *unexpanded* scopes because unioning the range -/// with the children will do the same thing for an expanded scope. -/// It is also needed for ignored \c ASTNodes, which may be, e.g. \c -/// InterpolatedStringLiterals -static SourceLoc getLocEncompassingPotentialLookups(const SourceManager &SM, - const SourceLoc endLoc) { - const auto tok = Lexer::getTokenAtLocation(SM, endLoc); - switch (tok.getKind()) { - default: - return endLoc; - case tok::string_literal: - if (!isInterpolatedStringLiteral(tok)) - return endLoc; // Just the start of the last token - break; - case tok::identifier: - // subtract one to get a closed-range endpoint from a half-open - if (!Identifier::isEditorPlaceholder(tok.getText())) - return endLoc; - break; - } - return tok.getRange().getEnd().getAdvancedLoc(-1); -} - -SourceRange ASTScopeImpl::sourceRangeForDeferredExpansion() const { - return SourceRange(); -} -SourceRange IterableTypeScope::sourceRangeForDeferredExpansion() const { - return portion->sourceRangeForDeferredExpansion(this); -} -SourceRange FunctionBodyScope::sourceRangeForDeferredExpansion() const { - const auto bsr = decl->getOriginalBodySourceRange(); - const SourceLoc endEvenIfNoCloseBraceAndEndsWithInterpolatedStringLiteral = - getLocEncompassingPotentialLookups(getSourceManager(), bsr.End); - return SourceRange(bsr.Start, - endEvenIfNoCloseBraceAndEndsWithInterpolatedStringLiteral); -} - -SourceRange GenericTypeOrExtensionWholePortion::sourceRangeForDeferredExpansion( - const IterableTypeScope *s) const { - const auto rangeOfThisNodeWithoutChildren = - getChildlessSourceRangeOf(s, false); - const auto rangeExtendedForFinalToken = SourceRange( - rangeOfThisNodeWithoutChildren.Start, - getLocEncompassingPotentialLookups(s->getSourceManager(), - rangeOfThisNodeWithoutChildren.End)); - const auto rangePastExtendedNominal = - s->moveStartPastExtendedNominal(rangeExtendedForFinalToken); - return rangePastExtendedNominal; -} - -SourceRange GenericTypeOrExtensionWherePortion::sourceRangeForDeferredExpansion( - const IterableTypeScope *) const { - return SourceRange(); -} - -SourceRange IterableTypeBodyPortion::sourceRangeForDeferredExpansion( - const IterableTypeScope *s) const { - const auto bracesRange = getChildlessSourceRangeOf(s, false); - return SourceRange(bracesRange.Start, - getLocEncompassingPotentialLookups(s->getSourceManager(), - bracesRange.End)); -} - -SourceRange ASTScopeImpl::getEffectiveSourceRange(const ASTNode n) const { - if (const auto *d = n.dyn_cast()) - return d->getSourceRange(); - if (const auto *s = n.dyn_cast()) - return s->getSourceRange(); - auto *e = n.dyn_cast(); - return getLocEncompassingPotentialLookups(getSourceManager(), e->getEndLoc()); -} - -/// Some nodes (e.g. the error expression) cannot possibly contain anything to -/// be looked up and if included in a parent scope's source range would expand -/// it beyond an ancestor's source range. But if the ancestor is expanded -/// lazily, we check that its source range does not change when expanding it, -/// and this check would fail. -static bool sourceRangeWouldInterfereWithLaziness(const ASTNode n) { - return n.isExpr(ExprKind::Error); -} - -static bool -shouldIgnoredASTNodeSourceRangeWidenEnclosingScope(const ASTNode n) { - if (n.isDecl(DeclKind::Var)) { - // The pattern scopes will include the source ranges for VarDecls. - // Using its range here would cause a pattern initializer scope's range - // to overlap the pattern use scope's range. - return false; - } - if (sourceRangeWouldInterfereWithLaziness(n)) - return false; - return true; -} - -void ASTScopeImpl::widenSourceRangeForIgnoredASTNode(const ASTNode n) { - if (!shouldIgnoredASTNodeSourceRangeWidenEnclosingScope(n)) - return; - - // FIXME: why only do effectiveness bit for *ignored* nodes? - SourceRange r = getEffectiveSourceRange(n); - if (r.isInvalid()) - return; - if (sourceRangeOfIgnoredASTNodes.isInvalid()) - sourceRangeOfIgnoredASTNodes = r; - else - sourceRangeOfIgnoredASTNodes.widen(r); -} - SourceLoc getLocAfterExtendedNominal(const ExtensionDecl *const ext) { const auto *const etr = ext->getExtendedTypeRepr(); if (!etr) From fbb97b9e2650194f38c34f7a6e1fbfd57d78ae42 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 6 Oct 2020 15:35:24 -0400 Subject: [PATCH 268/745] AST: Fix an assertion in UnqualifiedLookup to use CharSourceRanges --- lib/AST/UnqualifiedLookup.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index d1fc74b511786..d1267254ca94c 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -27,6 +27,7 @@ #include "swift/Basic/STLExtras.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" +#include "swift/Parse/Lexer.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/Debug.h" @@ -147,7 +148,7 @@ namespace { #pragma mark context-based lookup declarations - bool isOutsideBodyOfFunction(const AbstractFunctionDecl *const AFD) const; + bool isInsideBodyOfFunction(const AbstractFunctionDecl *const AFD) const; /// For diagnostic purposes, move aside the unavailables, and put /// them back as a last-ditch effort. @@ -319,11 +320,11 @@ void UnqualifiedLookupFactory::lookUpTopLevelNamesInModuleScopeContext( #pragma mark context-based lookup definitions -bool UnqualifiedLookupFactory::isOutsideBodyOfFunction( +bool UnqualifiedLookupFactory::isInsideBodyOfFunction( const AbstractFunctionDecl *const AFD) const { - return !AFD->isImplicit() && Loc.isValid() && - AFD->getBodySourceRange().isValid() && - !SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc); + auto range = Lexer::getCharSourceRangeFromSourceRange( + SM, AFD->getBodySourceRange()); + return range.contains(Loc); } void UnqualifiedLookupFactory::ResultFinderForTypeContext::findResults( @@ -635,7 +636,7 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::lookInMembers( NominalTypeDecl *const nominal) { if (candidateSelfDC) { if (auto *afd = dyn_cast(candidateSelfDC)) { - assert(!factory.isOutsideBodyOfFunction(afd) && "Should be inside"); + assert(factory.isInsideBodyOfFunction(afd) && "Should be inside"); } } From e59069f60f34e1d120ed5e750b258945dfce405c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 6 Oct 2020 15:35:01 -0400 Subject: [PATCH 269/745] Add a couple of crashers that are now fixed --- validation-test/compiler_crashers_2_fixed/sr12997.swift | 5 +++++ validation-test/compiler_crashers_2_fixed/sr12999.swift | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 validation-test/compiler_crashers_2_fixed/sr12997.swift create mode 100644 validation-test/compiler_crashers_2_fixed/sr12999.swift diff --git a/validation-test/compiler_crashers_2_fixed/sr12997.swift b/validation-test/compiler_crashers_2_fixed/sr12997.swift new file mode 100644 index 0000000000000..fa04d7c402fad --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr12997.swift @@ -0,0 +1,5 @@ +// RUN: not %target-swift-frontend -typecheck %s + +func addProperties(b: Int) { + guard true else { + _ = "`\(b)`." diff --git a/validation-test/compiler_crashers_2_fixed/sr12999.swift b/validation-test/compiler_crashers_2_fixed/sr12999.swift new file mode 100644 index 0000000000000..a5f0b4fa09a3e --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr12999.swift @@ -0,0 +1,5 @@ + // RUN: not %target-swift-frontend -typecheck %s + +public final class Foo { + public var a: String { + return "\(backgroundContext)" From 38b1da30bdea7c468ebdcd8cd289c9504eea2089 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 7 Oct 2020 10:17:36 -0700 Subject: [PATCH 270/745] Patch Out Use-Of-Stack-After-Free In Cross Module Dependencies The fake job is entered into the map to satisfy the tracing machinery. When that same machinery kicks into gear to print out paths, lookups into the dictionary will access data has gone out of scope. For now, cut off the read. There will be another refactoring patch that keeps these temporaries out of the Driver's data structures entirely. rdar://70053563 --- lib/Driver/FineGrainedDependencyDriverGraph.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/Driver/FineGrainedDependencyDriverGraph.cpp b/lib/Driver/FineGrainedDependencyDriverGraph.cpp index f45950a234b8b..e7e55c6da3fd0 100644 --- a/lib/Driver/FineGrainedDependencyDriverGraph.cpp +++ b/lib/Driver/FineGrainedDependencyDriverGraph.cpp @@ -752,6 +752,11 @@ StringRef ModuleDepGraph::getProvidingFilename( const Optional swiftDeps) const { if (!swiftDeps) return "getFirstSwiftPrimaryInput()); // FineGrainedDependencyGraphTests work with simulated jobs with empty From b0f0587963fefef42c20d0a3495551a286be3a6d Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 7 Oct 2020 10:39:06 -0700 Subject: [PATCH 271/745] Keep a Cache of Externally-Dependent Jobs A more durable form of #34218. Keep a side cache of externally-dependent jobs for now. This ensures our pseudo-Jobs don't get prematurely deallocated before the tracing machinery has had a chance to report the structure of the Job graph. rdar://70053563 --- include/swift/Driver/Compilation.h | 18 +++++++++++++++++- lib/Driver/Compilation.cpp | 13 ++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/include/swift/Driver/Compilation.h b/include/swift/Driver/Compilation.h index 6c75516c71e90..2dd41bfb57582 100644 --- a/include/swift/Driver/Compilation.h +++ b/include/swift/Driver/Compilation.h @@ -157,6 +157,16 @@ class Compilation { /// The Jobs which will be performed by this compilation. SmallVector, 32> Jobs; + // The Jobs which represent external actions performed by other drivers in + // the build graph. + // + // These Jobs are never scheduled into the build graph. This vector is + // populated by the routine that computes the set of incremental external + // dependencies that affect the current computation. Due to the way the + // Driver models multiple aspects of the incremental compilation scheduler + // by mapping to and from Jobs, it is necessary to lie and retain a set of + // pseudo-Jobs. + SmallVector, 32> ExternalJobs; /// The original (untranslated) input argument list. /// @@ -348,7 +358,6 @@ class Compilation { UnwrappedArrayView getJobs() const { return llvm::makeArrayRef(Jobs); } - Job *addJob(std::unique_ptr J); /// To send job list to places that don't truck in fancy array views. std::vector getJobsSimply() const { @@ -515,6 +524,13 @@ class Compilation { const JobCollection &unsortedJobs, SmallVectorImpl &sortedJobs) const; +private: + friend class Driver; + friend class PerformJobsState; + + Job *addJob(std::unique_ptr J); + Job *addExternalJob(std::unique_ptr J); + private: /// Perform all jobs. /// diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index c373d401a2542..73b84e0885aca 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -1179,10 +1179,11 @@ namespace driver { // Cons up a fake `Job` to satisfy the incremental job tracing // code's internal invariants. - Job fakeJob(Comp.getDerivedOutputFileMap(), external); + const auto *externalJob = Comp.addExternalJob( + std::make_unique(Comp.getDerivedOutputFileMap(), external)); auto subChanges = getFineGrainedDepGraph(forRanges).loadFromSwiftModuleBuffer( - &fakeJob, *buffer.get(), Comp.getDiags()); + externalJob, *buffer.get(), Comp.getDiags()); // If the incremental dependency graph failed to load, fall back to // treating this as plain external job. @@ -1194,7 +1195,7 @@ namespace driver { for (auto *CMD : getFineGrainedDepGraph(forRanges) .findJobsToRecompileWhenNodesChange(subChanges.getValue())) { - if (CMD == &fakeJob) { + if (CMD == externalJob) { continue; } ExternallyDependentJobs.push_back(CMD); @@ -1717,6 +1718,12 @@ Job *Compilation::addJob(std::unique_ptr J) { return result; } +Job *Compilation::addExternalJob(std::unique_ptr J) { + Job *result = J.get(); + ExternalJobs.emplace_back(std::move(J)); + return result; +} + static void checkForOutOfDateInputs(DiagnosticEngine &diags, const InputInfoMap &inputs) { for (const auto &inputPair : inputs) { From 2e0fb766544fc55ae541fd9a215717a999990b8f Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Wed, 7 Oct 2020 10:43:31 -0700 Subject: [PATCH 272/745] ModuleInterface: rephrase remark message when acquiring lock file failed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original remark message “could not acquire lock file for module interface” sounds too severe. It may confuse users that this is a serious issue. We should rephrase it to a less obtrusive one. rdar://70055223 --- include/swift/AST/DiagnosticsFrontend.def | 2 +- lib/Frontend/ModuleInterfaceBuilder.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 6719a13851ec0..a55b8ca8c97ce 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -393,7 +393,7 @@ ERROR(error_creating_remark_serializer,none, "error while creating remark serializer: '%0'", (StringRef)) REMARK(interface_file_lock_failure,none, - "could not acquire lock file for module interface '%0'", (StringRef)) + "building module interface without lock file", ()) REMARK(interface_file_lock_timed_out,none, "timed out waiting to acquire lock file for module interface '%0'", (StringRef)) diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index 45f9ea0867c55..ea0b21cf6e7a4 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -297,7 +297,7 @@ bool ModuleInterfaceBuilder::buildSwiftModule(StringRef OutPath, // necessary for performance. Fallback to building the module in case of any lock // related errors. if (RemarkRebuild) { - diagnose(diag::interface_file_lock_failure, interfacePath); + diagnose(diag::interface_file_lock_failure); } // Clear out any potential leftover. Locked.unsafeRemoveLockFile(); From b33b4f05bb79f51dc908b5c6c46eb4ec46ebc272 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 7 Oct 2020 10:46:59 -0700 Subject: [PATCH 273/745] [Test] Ensure linked dylib is copied to devices. The tests IRGen/async/run-* all link against a PrintShims dylib. To force that dylib to be copied onto devices running these tests, add the dylib's path to the %target-run line. --- test/IRGen/async/run-call-classinstance-int64-to-void.sil | 2 +- test/IRGen/async/run-call-classinstance-void-to-void.sil | 2 +- test/IRGen/async/run-call-existential-to-void.sil | 2 +- test/IRGen/async/run-call-generic-to-generic.sil | 2 +- test/IRGen/async/run-call-generic-to-void.sil | 2 +- test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil | 2 +- test/IRGen/async/run-call-int64-and-int64-to-void.sil | 2 +- test/IRGen/async/run-call-int64-to-void.sil | 2 +- .../async/run-call-protocolextension_instance-void-to-int64.sil | 2 +- .../async/run-call-protocolwitness_instance-void-to-int64.sil | 2 +- test/IRGen/async/run-call-structinstance-int64-to-void.sil | 2 +- ...hrows-to-int-throwing_call-async-nothrow_call-sync-throw.sil | 2 +- .../run-call-void-throws-to-int-throwing_call-async-throw.sil | 2 +- ...hrows-to-int-throwing_call-sync-nothrow_call-async-throw.sil | 2 +- .../run-call-void-throws-to-int-throwing_call-sync-throw.sil | 2 +- test/IRGen/async/run-call-void-to-existential.sil | 2 +- test/IRGen/async/run-call-void-to-int64-and-int64.sil | 2 +- test/IRGen/async/run-call-void-to-int64.sil | 2 +- test/IRGen/async/run-call-void-to-struct_large.sil | 2 +- ...ic-protocolwitness_instance-generic-to-int64-and-generic.sil | 2 +- .../run-call_generic-protocolwitness_instance-void-to-int64.sil | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/test/IRGen/async/run-call-classinstance-int64-to-void.sil b/test/IRGen/async/run-call-classinstance-int64-to-void.sil index cce841fa8e0f3..1f1adbfd61d7c 100644 --- a/test/IRGen/async/run-call-classinstance-int64-to-void.sil +++ b/test/IRGen/async/run-call-classinstance-int64-to-void.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-classinstance-void-to-void.sil b/test/IRGen/async/run-call-classinstance-void-to-void.sil index cad66b346e9a9..40da285fed9d8 100644 --- a/test/IRGen/async/run-call-classinstance-void-to-void.sil +++ b/test/IRGen/async/run-call-classinstance-void-to-void.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-existential-to-void.sil b/test/IRGen/async/run-call-existential-to-void.sil index f76c750e2ff7b..29019ab19464f 100644 --- a/test/IRGen/async/run-call-existential-to-void.sil +++ b/test/IRGen/async/run-call-existential-to-void.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-generic-to-generic.sil b/test/IRGen/async/run-call-generic-to-generic.sil index 812a3cb99936f..d5da1c9f97e21 100644 --- a/test/IRGen/async/run-call-generic-to-generic.sil +++ b/test/IRGen/async/run-call-generic-to-generic.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-generic-to-void.sil b/test/IRGen/async/run-call-generic-to-void.sil index d692426cbaf5e..94f6d550c3edf 100644 --- a/test/IRGen/async/run-call-generic-to-void.sil +++ b/test/IRGen/async/run-call-generic-to-void.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil b/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil index 5b8ae8f9b015c..1bce84155f6de 100644 --- a/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil +++ b/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-int64-and-int64-to-void.sil b/test/IRGen/async/run-call-int64-and-int64-to-void.sil index a23df61b548a5..8921d5ac538a9 100644 --- a/test/IRGen/async/run-call-int64-and-int64-to-void.sil +++ b/test/IRGen/async/run-call-int64-and-int64-to-void.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-int64-to-void.sil b/test/IRGen/async/run-call-int64-to-void.sil index a0a7775df3a63..5425e64cc2a2d 100644 --- a/test/IRGen/async/run-call-int64-to-void.sil +++ b/test/IRGen/async/run-call-int64-to-void.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil b/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil index 10badd65e163d..fae1f0549f1bf 100644 --- a/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil +++ b/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil b/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil index 046508f05c1eb..b260aaf31ac30 100644 --- a/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil +++ b/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-structinstance-int64-to-void.sil b/test/IRGen/async/run-call-structinstance-int64-to-void.sil index ae950265fd4d6..612cbdb9c4b44 100644 --- a/test/IRGen/async/run-call-structinstance-int64-to-void.sil +++ b/test/IRGen/async/run-call-structinstance-int64-to-void.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil index 1441327807b9f..b8cb7270ebc1e 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil index 14a92c6272992..de2055a6f1756 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil index 49daf094c4e4b..4d45943257fa6 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil index 95c083a9558a9..64a5a42098f99 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-void-to-existential.sil b/test/IRGen/async/run-call-void-to-existential.sil index fc58a6c644689..24cbcc6f91b8d 100644 --- a/test/IRGen/async/run-call-void-to-existential.sil +++ b/test/IRGen/async/run-call-void-to-existential.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-void-to-int64-and-int64.sil b/test/IRGen/async/run-call-void-to-int64-and-int64.sil index ebfd51c0b067c..27a5ae1767ec6 100644 --- a/test/IRGen/async/run-call-void-to-int64-and-int64.sil +++ b/test/IRGen/async/run-call-void-to-int64-and-int64.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-void-to-int64.sil b/test/IRGen/async/run-call-void-to-int64.sil index 168bca6875fc1..48efd5e337cc0 100644 --- a/test/IRGen/async/run-call-void-to-int64.sil +++ b/test/IRGen/async/run-call-void-to-int64.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call-void-to-struct_large.sil b/test/IRGen/async/run-call-void-to-struct_large.sil index abb78dacaf223..006355b21a1f5 100644 --- a/test/IRGen/async/run-call-void-to-struct_large.sil +++ b/test/IRGen/async/run-call-void-to-struct_large.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil b/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil index b7ae98587e77a..796e25df19519 100644 --- a/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil +++ b/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil b/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil index da00bc58c4bb3..99b78463e333e 100644 --- a/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil +++ b/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil @@ -4,7 +4,7 @@ // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL // RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none From 8f43d888b8fd88540c5ba3d770a857928ac0b3a7 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Wed, 7 Oct 2020 07:24:37 +0300 Subject: [PATCH 274/745] Sema: Disallow usage of settable Self-returning storage requirements on existential base --- lib/AST/Decl.cpp | 28 ++++++++++-------- test/decl/protocol/req/dynamic_self.swift | 36 +++++++++++++++++++++++ 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a330b63faedcb..8d570aa307800 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4946,21 +4946,19 @@ ProtocolDecl::findProtocolSelfReferences(const ValueDecl *value, return result; } - return ::findProtocolSelfReferences(this, type, - skipAssocTypes); - } else if (auto subscript = dyn_cast(value)) { - // Check the requirements of a generic subscript. - if (subscript->isGeneric()) { - if (auto result = - ::findProtocolSelfReferences(this, - subscript->getGenericSignature())) - return result; - } - return ::findProtocolSelfReferences(this, type, skipAssocTypes); } else { - assert(isa(value)); + assert(isa(value)); + + if (auto *const subscript = dyn_cast(value)) { + // Check the requirements of a generic subscript. + if (subscript->isGeneric()) { + if (auto result = ::findProtocolSelfReferences( + this, subscript->getGenericSignature())) + return result; + } + } return ::findProtocolSelfReferences(this, type, skipAssocTypes); @@ -4978,6 +4976,12 @@ bool ProtocolDecl::isAvailableInExistential(const ValueDecl *decl) const { if (selfKind.parameter || selfKind.other) return false; + // FIXME: Appropriately diagnose assignments instead. + if (auto *const storageDecl = dyn_cast(decl)) { + if (selfKind.result && storageDecl->supportsMutation()) + return false; + } + return true; } diff --git a/test/decl/protocol/req/dynamic_self.swift b/test/decl/protocol/req/dynamic_self.swift index 758a301ef6f22..ef4f33a100b1d 100644 --- a/test/decl/protocol/req/dynamic_self.swift +++ b/test/decl/protocol/req/dynamic_self.swift @@ -86,3 +86,39 @@ enum EError : P { // expected-error{{type 'EError' does not conform to protocol subscript() -> Int { 0 } // expected-note{{candidate has non-matching type '() -> Int'}} func f() -> Int { 0 } // expected-note{{candidate has non-matching type '() -> Int'}} } + + +// Settable storage declaration requirements with a 'Self' result type may not +// be used with an existential base. +protocol P2 { + subscript() -> Self { get set } +} +protocol P3 { + var prop: Self { get set } +} +protocol P4 { + subscript() -> T where T.Element == Self { get set } +} +func takesP2P3P4(p2: P2, p3: P3, p4: P4) { } +// expected-error@-1{{protocol 'P2' can only be used as a generic constraint because it has Self or associated type requirements}} +// expected-error@-2{{protocol 'P3' can only be used as a generic constraint because it has Self or associated type requirements}} + +protocol P5 { +} +extension P5 { + var prop: Self { + get { self } + set { } + } + + subscript() -> Self { + get { self } + set { } + } +} +func takesP5(p5: P5) { + _ = p5[] + // expected-error@-1{{member 'subscript' cannot be used on value of protocol type 'P5'; use a generic constraint instead}} + _ = p5.prop + // expected-error@-1{{member 'prop' cannot be used on value of protocol type 'P5'; use a generic constraint instead}} +} From e5737bc5cb428bbed9ddcb25c539eb0fe65f4e0c Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 7 Oct 2020 12:07:16 -0700 Subject: [PATCH 275/745] [NFC] Insert IncrementalJobAction into the Action Hierarchy This will be used to extend incremental behavior to merge-modules and the bridging header precompile step. --- include/swift/Driver/Action.h | 32 ++++++++++++++++++++++++-------- lib/Driver/Action.cpp | 2 ++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/include/swift/Driver/Action.h b/include/swift/Driver/Action.h index b2d6ed1a2c538..f4f15b8affedd 100644 --- a/include/swift/Driver/Action.h +++ b/include/swift/Driver/Action.h @@ -127,7 +127,7 @@ class JobAction : public Action { } }; -class CompileJobAction : public JobAction { +class IncrementalJobAction : public JobAction { public: struct InputInfo { enum Status { @@ -136,6 +136,8 @@ class CompileJobAction : public JobAction { NeedsNonCascadingBuild, NewlyAdded }; + + public: Status status = UpToDate; llvm::sys::TimePoint<> previousModTime; @@ -153,18 +155,32 @@ class CompileJobAction : public JobAction { InputInfo inputInfo; public: - CompileJobAction(file_types::ID OutputType) - : JobAction(Action::Kind::CompileJob, None, OutputType), - inputInfo() {} - - CompileJobAction(Action *Input, file_types::ID OutputType, InputInfo info) - : JobAction(Action::Kind::CompileJob, Input, OutputType), - inputInfo(info) {} + IncrementalJobAction(Kind Kind, ArrayRef Inputs, + file_types::ID Type, InputInfo info) + : JobAction(Kind, Inputs, Type), inputInfo(info) {} +public: InputInfo getInputInfo() const { return inputInfo; } +public: + static bool classof(const Action *A) { + return A->getKind() == Action::Kind::CompileJob; + } +}; + +class CompileJobAction : public IncrementalJobAction { +private: + virtual void anchor() override; + +public: + CompileJobAction(file_types::ID OutputType) + : IncrementalJobAction(Action::Kind::CompileJob, None, OutputType, {}) {} + CompileJobAction(Action *Input, file_types::ID OutputType, InputInfo info) + : IncrementalJobAction(Action::Kind::CompileJob, Input, OutputType, + info) {} + static bool classof(const Action *A) { return A->getKind() == Action::Kind::CompileJob; } diff --git a/lib/Driver/Action.cpp b/lib/Driver/Action.cpp index 25117800eed96..27f0d9306d95a 100644 --- a/lib/Driver/Action.cpp +++ b/lib/Driver/Action.cpp @@ -43,6 +43,8 @@ void InputAction::anchor() {} void JobAction::anchor() {} +void IncrementalJobAction::anchor() {} + void CompileJobAction::anchor() {} void InterpretJobAction::anchor() {} From 8fbcdf392f5815e18d097c5021eb6a196a3ca135 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 7 Oct 2020 12:08:21 -0700 Subject: [PATCH 276/745] [NFC] Use IncrementalJobAction to Supersede getFirstSwiftPrimaryInput() This function was being used in a roundabout way to test for a compile job action. But the actual data itself was not being used. --- lib/Driver/Compilation.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index c373d401a2542..503c88c0ffb71 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -860,7 +860,7 @@ namespace driver { computeFirstRoundCompileJobsForIncrementalCompilation(); for (const Job *Cmd : Comp.getJobs()) { - if (Cmd->getFirstSwiftPrimaryInput().empty() || + if (!isa(Cmd->getSource()) || compileJobsToSchedule.count(Cmd)) { scheduleCommandIfNecessaryAndPossible(Cmd); noteBuilding(Cmd, /*willBeBuilding*/ true, /*isTentative=*/false, @@ -899,20 +899,22 @@ namespace driver { CommandSet computeDependenciesAndGetNeededCompileJobs(const bool forRanges) { auto getEveryCompileJob = [&] { - CommandSet everyCompileJob; + CommandSet everyIncrementalJob; for (const Job *Cmd : Comp.getJobs()) { - if (!Cmd->getFirstSwiftPrimaryInput().empty()) - everyCompileJob.insert(Cmd); + if (isa(Cmd->getSource())) + everyIncrementalJob.insert(Cmd); } - return everyCompileJob; + return everyIncrementalJob; }; CommandSet jobsToSchedule; CommandSet initialCascadingCommands; for (const Job *cmd : Comp.getJobs()) { - const StringRef primary = cmd->getFirstSwiftPrimaryInput(); - if (primary.empty()) - continue; // not Compile + // Skip jobs that have no associated incremental info. + if (!isa(cmd->getSource())) { + continue; + } + const Optional> shouldSchedAndIsCascading = computeShouldInitiallyScheduleJobAndDependendents(cmd, forRanges); if (!shouldSchedAndIsCascading) From ae0c380f1b0f37e11b111a06634dd4c64e67b96c Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Wed, 7 Oct 2020 15:19:23 -0700 Subject: [PATCH 277/745] Improve -*prefix-map help text Before: ``` -coverage-prefix-map -debug-prefix-map ``` After: ``` -coverage-prefix-map -debug-prefix-map ``` --- include/swift/Option/Options.td | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 48e01d78b14f1..df553ff7c8e40 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -756,10 +756,10 @@ def gdwarf_types : Flag<["-"], "gdwarf-types">, HelpText<"Emit full DWARF type info.">; def debug_prefix_map : Separate<["-"], "debug-prefix-map">, Flags<[FrontendOption]>, - HelpText<"Remap source paths in debug info">; + HelpText<"Remap source paths in debug info">, MetaVarName<"">; def coverage_prefix_map : Separate<["-"], "coverage-prefix-map">, Flags<[FrontendOption]>, - HelpText<"Remap source paths in coverage info">; + HelpText<"Remap source paths in coverage info">, MetaVarName<"">; def debug_info_format : Joined<["-"], "debug-info-format=">, Flags<[FrontendOption]>, From 456a50527c5948d0acfb2a155c6be3e414a8bfd0 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 7 Oct 2020 16:06:49 -0700 Subject: [PATCH 278/745] [Sema] Adjust mutability in buildStorageRef appropriately for accessors on local variables. --- lib/Sema/TypeCheckStorage.cpp | 55 ++++++++++++++++------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 5b3d658a718bd..c9a4c5a2cf9f1 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -830,12 +830,32 @@ static Expr *buildStorageReference(AccessorDecl *accessor, } } + // If the base is not 'self', default get access to nonmutating and set access to mutating. + bool getterMutatesBase = selfDecl && storage->isGetterMutating(); + bool setterMutatesBase = !selfDecl || storage->isSetterMutating(); + // If we're not accessing via a property wrapper, we don't need to adjust + // the mutability. + if (target == TargetImpl::Wrapper || target == TargetImpl::WrapperStorage) { + auto var = cast(accessor->getStorage()); + auto mutability = var->getPropertyWrapperMutability(); + // Only adjust mutability if it's possible to mutate the base. + if (mutability && !var->isStatic() && + !(selfDecl && selfTypeForAccess->hasReferenceSemantics())) { + getterMutatesBase = (mutability->Getter == PropertyWrapperMutability::Mutating); + setterMutatesBase = (mutability->Setter == PropertyWrapperMutability::Mutating); + } + } + + // If the accessor is mutating, then the base should be referred as an l-value + bool isBaseLValue = (getterMutatesBase && isUsedForGetAccess) || + (setterMutatesBase && isUsedForSetAccess); + if (!selfDecl) { assert(target != TargetImpl::Super); auto *storageDRE = new (ctx) DeclRefExpr(storage, DeclNameLoc(), /*IsImplicit=*/true, semantics); auto type = storage->getValueInterfaceType().subst(subs); - if (isLValue) + if (isBaseLValue) type = LValueType::get(type); storageDRE->setType(type); @@ -843,32 +863,9 @@ static Expr *buildStorageReference(AccessorDecl *accessor, } // Build self - - bool isGetterMutating = storage->isGetterMutating(); - bool isSetterMutating = storage->isSetterMutating(); - // If we're not accessing via a property wrapper, we don't need to adjust - // the mutability. - if (target == TargetImpl::Wrapper || target == TargetImpl::WrapperStorage) { - auto var = cast(accessor->getStorage()); - if (auto mutability = var->getPropertyWrapperMutability()) { - // We consider the storage's mutability too because the wrapped property - // might be part of a class, in case of which nothing is mutating. - isGetterMutating = (mutability->Getter == PropertyWrapperMutability::Mutating) - ? (storage->isGetterMutating() || storage->isSetterMutating()) - : storage->isGetterMutating(); - isSetterMutating = (mutability->Setter == PropertyWrapperMutability::Mutating) - ? (storage->isGetterMutating() || storage->isSetterMutating()) - : storage->isGetterMutating(); - } - } - - // If the accessor is mutating, then self should be referred as an l-value - bool isSelfLValue = (isGetterMutating && isUsedForGetAccess) || - (isSetterMutating && isUsedForSetAccess); - - Expr *selfDRE = buildSelfReference(selfDecl, selfAccessKind, isSelfLValue, + Expr *selfDRE = buildSelfReference(selfDecl, selfAccessKind, isBaseLValue, /*convertTy*/ selfTypeForAccess); - if (isSelfLValue) + if (isBaseLValue) selfTypeForAccess = LValueType::get(selfTypeForAccess); if (!selfDRE->getType()->isEqual(selfTypeForAccess)) { @@ -1169,9 +1166,8 @@ synthesizeTrivialGetterBody(AccessorDecl *getter, TargetImpl target, body.push_back(returnStmt); } - // Don't mark local accessors as type-checked - captures still need to be computed. return { BraceStmt::create(ctx, loc, body, loc, true), - /*isTypeChecked=*/!getter->getDeclContext()->isLocalContext() }; + /*isTypeChecked=*/true }; } /// Synthesize the body of a getter which just directly accesses the @@ -1446,9 +1442,8 @@ synthesizeTrivialSetterBodyWithStorage(AccessorDecl *setter, createPropertyStoreOrCallSuperclassSetter(setter, valueDRE, storageToUse, target, setterBody, ctx); - // Don't mark local accessors as type-checked - captures still need to be computed. return { BraceStmt::create(ctx, loc, setterBody, loc, true), - /*isTypeChecked=*/!setter->getDeclContext()->isLocalContext() }; + /*isTypeChecked=*/true }; } static std::pair From 0c6ef5838e171ad6a79d76e1905ce01ef3cf909d Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 7 Oct 2020 16:08:41 -0700 Subject: [PATCH 279/745] [DeclChecker] Explicitly compute captures for local functions in the decl checker. --- lib/Sema/TypeCheckDeclPrimary.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 9e44995acd561..adc9c837df6c3 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -2362,6 +2362,7 @@ class DeclChecker : public DeclVisitor { } else if (FD->getDeclContext()->isLocalContext()) { // Check local function bodies right away. (void)FD->getTypecheckedBody(); + TypeChecker::computeCaptures(FD); } else if (shouldSkipBodyTypechecking(FD)) { FD->setBodySkipped(FD->getBodySourceRange()); } else { From d3c6d36af9a64172a37b9823e62f94a3a4bcf937 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 7 Oct 2020 16:13:01 -0700 Subject: [PATCH 280/745] Fixup Use-of-Temporary The Optional parameter here was being copied instead of being taken by const reference. The expectation is that we call this function and extract the data from a ModuleDepGraphNode node, but instead we were extracting a copy which would promptly blow up. Thanks ASAN! --- include/swift/Driver/FineGrainedDependencyDriverGraph.h | 2 +- lib/Driver/FineGrainedDependencyDriverGraph.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/swift/Driver/FineGrainedDependencyDriverGraph.h b/include/swift/Driver/FineGrainedDependencyDriverGraph.h index 8ae3ac783d332..20ccaeccd96b0 100644 --- a/include/swift/Driver/FineGrainedDependencyDriverGraph.h +++ b/include/swift/Driver/FineGrainedDependencyDriverGraph.h @@ -466,7 +466,7 @@ class ModuleDepGraph { void printPath(raw_ostream &out, const driver::Job *node) const; /// Get a printable filename, given a node's swiftDeps. - StringRef getProvidingFilename(Optional swiftDeps) const; + StringRef getProvidingFilename(const Optional &swiftDeps) const; /// Print one node on the dependency path. static void printOneNodeOfPath(raw_ostream &out, const DependencyKey &key, diff --git a/lib/Driver/FineGrainedDependencyDriverGraph.cpp b/lib/Driver/FineGrainedDependencyDriverGraph.cpp index e7e55c6da3fd0..d7cf58be0cb8e 100644 --- a/lib/Driver/FineGrainedDependencyDriverGraph.cpp +++ b/lib/Driver/FineGrainedDependencyDriverGraph.cpp @@ -749,7 +749,7 @@ void ModuleDepGraph::printPath(raw_ostream &out, } StringRef ModuleDepGraph::getProvidingFilename( - const Optional swiftDeps) const { + const Optional &swiftDeps) const { if (!swiftDeps) return " Date: Fri, 25 Sep 2020 16:46:50 -0700 Subject: [PATCH 281/745] [AST] Add method to check if a function type has a non-trivial Clang type. --- include/swift/AST/ExtInfo.h | 1 + include/swift/AST/Types.h | 23 +++++++++++++++++++++++ lib/AST/ExtInfo.cpp | 4 ++++ lib/AST/Type.cpp | 24 ++++++++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/include/swift/AST/ExtInfo.h b/include/swift/AST/ExtInfo.h index ffea1db284b01..0620c14285a0c 100644 --- a/include/swift/AST/ExtInfo.h +++ b/include/swift/AST/ExtInfo.h @@ -70,6 +70,7 @@ class ClangTypeInfo { constexpr ClangTypeInfo(const clang::Type *type) : type(type) {} friend bool operator==(ClangTypeInfo lhs, ClangTypeInfo rhs); + friend bool operator!=(ClangTypeInfo lhs, ClangTypeInfo rhs); ClangTypeInfo getCanonical() const; public: diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 12c2643de24ea..1374b073ec8dc 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -2928,6 +2928,24 @@ class AnyFunctionType : public TypeBase { ClangTypeInfo getClangTypeInfo() const; ClangTypeInfo getCanonicalClangTypeInfo() const; + /// Returns true if the function type stores a Clang type that cannot + /// be derived from its Swift type. Returns false otherwise, including if + /// the function type is not @convention(c) or @convention(block). + /// + /// For example, if you have a function pointer from C getting imported with + /// the following type: + /// + /// @convention(c, cType: "void (*)(size_t (*)(size_t))") + /// (@convention(c, cType: "size_t (*)(size_t)") (Int) -> Int)) -> Void + /// + /// The parameter's function type will have hasNonDerivableClangType() = true, + /// but the outer function type will have hasNonDerivableClangType() = false, + /// because the parameter and result type are sufficient to correctly derive + /// the Clang type for the outer function type. In terms of mangling, + /// the parameter type's mangling will incorporate the Clang type but the + /// outer function type's mangling doesn't need to duplicate that information. + bool hasNonDerivableClangType(); + ExtInfo getExtInfo() const { return ExtInfo(Bits.AnyFunctionType.ExtInfoBits, getClangTypeInfo()); } @@ -4309,6 +4327,11 @@ class SILFunctionType final ClangTypeInfo getClangTypeInfo() const; + /// Returns true if the function type stores a Clang type that cannot + /// be derived from its Swift type. Returns false otherwise, including if + /// the function type is not @convention(c) or @convention(block). + bool hasNonDerivableClangType(); + bool hasSameExtInfoAs(const SILFunctionType *otherFn); /// Given that `this` is a `@differentiable` or `@differentiable(linear)` diff --git a/lib/AST/ExtInfo.cpp b/lib/AST/ExtInfo.cpp index 1fb30adc6af16..f4cf5db268b1c 100644 --- a/lib/AST/ExtInfo.cpp +++ b/lib/AST/ExtInfo.cpp @@ -34,6 +34,10 @@ bool operator==(ClangTypeInfo lhs, ClangTypeInfo rhs) { return false; } +bool operator!=(ClangTypeInfo lhs, ClangTypeInfo rhs) { + return !(lhs == rhs); +} + ClangTypeInfo ClangTypeInfo::getCanonical() const { if (!type) return ClangTypeInfo(); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 129efae29ed8c..71563ca68ec1e 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -3424,6 +3424,16 @@ ClangTypeInfo AnyFunctionType::getCanonicalClangTypeInfo() const { return getClangTypeInfo().getCanonical(); } +bool AnyFunctionType::hasNonDerivableClangType() { + auto clangTypeInfo = getClangTypeInfo(); + if (clangTypeInfo.empty()) + return false; + auto computedClangType = getASTContext().getClangFunctionType( + getParams(), getResult(), getRepresentation()); + assert(computedClangType && "Failed to compute Clang type."); + return clangTypeInfo != ClangTypeInfo(computedClangType); +} + bool AnyFunctionType::hasSameExtInfoAs(const AnyFunctionType *otherFn) { return getExtInfo().isEqualTo(otherFn->getExtInfo(), useClangTypes(this)); } @@ -3437,6 +3447,20 @@ ClangTypeInfo SILFunctionType::getClangTypeInfo() const { return *info; } +bool SILFunctionType::hasNonDerivableClangType() { + auto clangTypeInfo = getClangTypeInfo(); + if (clangTypeInfo.empty()) + return false; + auto results = getResults(); + auto computedClangType = + getASTContext().getCanonicalClangFunctionType( + getParameters(), + results.empty() ? None : Optional(results[0]), + getRepresentation()); + assert(computedClangType && "Failed to compute Clang type."); + return clangTypeInfo != ClangTypeInfo(computedClangType); +} + bool SILFunctionType::hasSameExtInfoAs(const SILFunctionType *otherFn) { return getExtInfo().isEqualTo(otherFn->getExtInfo(), useClangTypes(this)); } From 703eece10df887222fd081574e8b4d4f6d93d365 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 7 Oct 2020 16:21:13 -0700 Subject: [PATCH 282/745] [NFC] Extract ModuleInputs as a Separate Utility The driver will eventually use this to compute the input status of a merge-modules job. --- lib/Driver/Driver.cpp | 48 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index ed09cf7bb028e..1fab89633740d 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1876,6 +1876,54 @@ Driver::computeCompilerMode(const DerivedArgList &Args, return OutputInfo::Mode::StandardCompile; } +namespace { +/// Encapsulates the computation of input jobs that are relevant to the +/// merge-modules job the scheduler can insert if we are not in a single compile +/// mode. +class ModuleInputs final { +private: + using InputInfo = IncrementalJobAction::InputInfo; + SmallVector AllModuleInputs; + InputInfo StatusBound; + +public: + explicit ModuleInputs() + : StatusBound + {InputInfo::Status::UpToDate, llvm::sys::TimePoint<>::min()} {} + +public: + void addInput(const Action *inputAction) { + if (auto *IJA = dyn_cast(inputAction)) { + // Take the upper bound of the status of any incremental inputs to + // ensure that the merge-modules job gets run if *any* input job is run. + const auto conservativeStatus = + std::max(StatusBound.status, IJA->getInputInfo().status); + // The modification time here is not important to the rest of the + // incremental build. We take the upper bound in case an attempt to + // compare the swiftmodule output's mod time and any input files is + // made. If the compilation has been correctly scheduled, the + // swiftmodule's mod time will always strictly exceed the mod time of + // any of its inputs when we are able to skip it. + const auto conservativeModTime = std::max( + StatusBound.previousModTime, IJA->getInputInfo().previousModTime); + StatusBound = InputInfo{conservativeStatus, conservativeModTime}; + } + AllModuleInputs.push_back(inputAction); + } + +public: + /// Returns \c true if no inputs have been registered with this instance. + bool empty() const { return AllModuleInputs.empty(); } + +public: + /// Consumes this \c ModuleInputs instance and returns a merge-modules action + /// from the list of input actions and status it has computed thus far. + JobAction *intoAction(Compilation &C) && { + return C.createAction(AllModuleInputs, StatusBound); + } +}; +} // namespace + void Driver::buildActions(SmallVectorImpl &TopLevelActions, const ToolChain &TC, const OutputInfo &OI, const InputInfoMap *OutOfDateMap, From 25978e1ac955a7de888b405c7bf72e85b425f81d Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 7 Oct 2020 16:21:45 -0700 Subject: [PATCH 283/745] [NFC] Add InputInfo::makeNeedsCascadingRebuild --- include/swift/Driver/Action.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/swift/Driver/Action.h b/include/swift/Driver/Action.h index f4f15b8affedd..fafb1685eec9d 100644 --- a/include/swift/Driver/Action.h +++ b/include/swift/Driver/Action.h @@ -146,7 +146,11 @@ class IncrementalJobAction : public JobAction { : status(stat), previousModTime(time) {} static InputInfo makeNewlyAdded() { - return InputInfo(Status::NewlyAdded, llvm::sys::TimePoint<>::max()); + return {Status::NewlyAdded, llvm::sys::TimePoint<>::max()}; + } + + static InputInfo makeNeedsCascadingRebuild() { + return {Status::NeedsCascadingBuild, llvm::sys::TimePoint<>::min()}; } }; From 83f5162f9ed3867fdb352612ad9930de3fa239fc Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 7 Oct 2020 16:32:29 -0700 Subject: [PATCH 284/745] [NFC] Reorder InputInfo::Status In Terms of Impact on the Incremental Build --- include/swift/Driver/Action.h | 2 +- lib/Driver/CompilationRecord.h | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/swift/Driver/Action.h b/include/swift/Driver/Action.h index fafb1685eec9d..a2c262795edc6 100644 --- a/include/swift/Driver/Action.h +++ b/include/swift/Driver/Action.h @@ -132,8 +132,8 @@ class IncrementalJobAction : public JobAction { struct InputInfo { enum Status { UpToDate, - NeedsCascadingBuild, NeedsNonCascadingBuild, + NeedsCascadingBuild, NewlyAdded }; diff --git a/lib/Driver/CompilationRecord.h b/lib/Driver/CompilationRecord.h index ce1215008a058..0fdf38dc72350 100644 --- a/lib/Driver/CompilationRecord.h +++ b/lib/Driver/CompilationRecord.h @@ -59,12 +59,12 @@ inline static StringRef getName(TopLevelKey Key) { inline static StringRef getIdentifierForInputInfoStatus(CompileJobAction::InputInfo::Status Status) { switch (Status) { - case CompileJobAction::InputInfo::UpToDate: + case CompileJobAction::InputInfo::Status::UpToDate: return ""; - case CompileJobAction::InputInfo::NewlyAdded: - case CompileJobAction::InputInfo::NeedsCascadingBuild: + case CompileJobAction::InputInfo::Status::NewlyAdded: + case CompileJobAction::InputInfo::Status::NeedsCascadingBuild: return "!dirty"; - case CompileJobAction::InputInfo::NeedsNonCascadingBuild: + case CompileJobAction::InputInfo::Status::NeedsNonCascadingBuild: return "!private"; } @@ -76,11 +76,11 @@ getIdentifierForInputInfoStatus(CompileJobAction::InputInfo::Status Status) { /// compilation record file (.swiftdeps file). inline static Optional getInfoStatusForIdentifier(StringRef Identifier) { - return llvm::StringSwitch>(Identifier) - .Case("", CompileJobAction::InputInfo::UpToDate) - .Case("!dirty", CompileJobAction::InputInfo::NeedsCascadingBuild) - .Case("!private", CompileJobAction::InputInfo::NeedsNonCascadingBuild) + using InputStatus = CompileJobAction::InputInfo::Status; + return llvm::StringSwitch>(Identifier) + .Case("", InputStatus::UpToDate) + .Case("!dirty", InputStatus::NeedsCascadingBuild) + .Case("!private", InputStatus::NeedsNonCascadingBuild) .Default(None); } From d29dcf89011b8e94fbb841d6a1f2a2709df9bd87 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 7 Oct 2020 16:33:56 -0700 Subject: [PATCH 285/745] [NFC] Upgrade CompileJobAction::InputInfo::Status to an enum class --- include/swift/Driver/Action.h | 17 +++++++++++++++-- lib/Driver/Compilation.cpp | 6 +++--- lib/Driver/Driver.cpp | 13 +++++++------ 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/include/swift/Driver/Action.h b/include/swift/Driver/Action.h index a2c262795edc6..b9e43872f548f 100644 --- a/include/swift/Driver/Action.h +++ b/include/swift/Driver/Action.h @@ -130,15 +130,28 @@ class JobAction : public Action { class IncrementalJobAction : public JobAction { public: struct InputInfo { - enum Status { + /// The status of an input known to the driver. These are used to affect + /// the scheduling decisions made during an incremental build. + /// + /// \Note The order of cases matters. They are ordered from least to + /// greatest impact on the incremental build schedule. + enum class Status { + /// The input to this job is up to date. UpToDate, + /// The input to this job has changed in a way that requires this job to + /// be rerun, but not in such a way that it requires a cascading rebuild. NeedsNonCascadingBuild, + /// The input to this job has changed in a way that requires this job to + /// be rerun, and in such a way that all jobs dependent upon this one + /// must be scheduled as well. NeedsCascadingBuild, + /// The input to this job was not known to the driver when it was last + /// run. NewlyAdded }; public: - Status status = UpToDate; + Status status = Status::UpToDate; llvm::sys::TimePoint<> previousModTime; InputInfo() = default; diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index 503c88c0ffb71..d599fef02294d 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -1608,8 +1608,8 @@ namespace driver { CompileJobAction::InputInfo info; info.previousModTime = entry.first->getInputModTime(); info.status = entry.second ? - CompileJobAction::InputInfo::NeedsCascadingBuild : - CompileJobAction::InputInfo::NeedsNonCascadingBuild; + CompileJobAction::InputInfo::Status::NeedsCascadingBuild : + CompileJobAction::InputInfo::Status::NeedsNonCascadingBuild; inputs[&inputFile->getInputArg()] = info; } } @@ -1626,7 +1626,7 @@ namespace driver { CompileJobAction::InputInfo info; info.previousModTime = entry->getInputModTime(); - info.status = CompileJobAction::InputInfo::UpToDate; + info.status = CompileJobAction::InputInfo::Status::UpToDate; inputs[&inputFile->getInputArg()] = info; } } diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 1fab89633740d..a1cd2820568bf 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -2755,6 +2755,7 @@ static void handleCompileJobCondition(Job *J, CompileJobAction::InputInfo inputInfo, StringRef input, bool alwaysRebuildDependents) { if (inputInfo.status == CompileJobAction::InputInfo::NewlyAdded) { + using InputStatus = CompileJobAction::InputInfo::Status; J->setCondition(Job::Condition::NewlyAdded); return; } @@ -2769,24 +2770,24 @@ handleCompileJobCondition(Job *J, CompileJobAction::InputInfo inputInfo, Job::Condition condition; if (hasValidModTime && J->getInputModTime() == inputInfo.previousModTime) { switch (inputInfo.status) { - case CompileJobAction::InputInfo::UpToDate: - if (llvm::sys::fs::exists(J->getOutput().getPrimaryOutputFilename())) + case InputStatus::UpToDate: + if (llvm::sys::fs::exists(output)) condition = Job::Condition::CheckDependencies; else condition = Job::Condition::RunWithoutCascading; break; - case CompileJobAction::InputInfo::NeedsCascadingBuild: + case InputStatus::NeedsCascadingBuild: condition = Job::Condition::Always; break; - case CompileJobAction::InputInfo::NeedsNonCascadingBuild: + case InputStatus::NeedsNonCascadingBuild: condition = Job::Condition::RunWithoutCascading; break; - case CompileJobAction::InputInfo::NewlyAdded: + case InputStatus::NewlyAdded: llvm_unreachable("handled above"); } } else { if (alwaysRebuildDependents || - inputInfo.status == CompileJobAction::InputInfo::NeedsCascadingBuild) { + inputInfo.status == InputStatus::NeedsCascadingBuild) { condition = Job::Condition::Always; } else { condition = Job::Condition::RunWithoutCascading; From 84f9ba300420d5fc44c46dce675dea77942e80ab Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 7 Oct 2020 16:36:07 -0700 Subject: [PATCH 286/745] Use ModuleInputs instead of a Vector to build MergeModules --- lib/Driver/Compilation.cpp | 7 ++++++- lib/Driver/Driver.cpp | 20 +++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index d599fef02294d..bb6a7c5ca2570 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -1163,7 +1163,12 @@ namespace driver { continue; } - // Can we run a cross-module incremental build at all? If not, fallback. + // Is this module out of date? If not, just keep searching. + if (Comp.getLastBuildTime() >= depStatus.getLastModificationTime()) + continue; + + // Can we run a cross-module incremental build at all? + // If not, fall back. if (!Comp.getEnableCrossModuleIncrementalBuild()) { fallbackToExternalBehavior(external); continue; diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index a1cd2820568bf..518ac607bdace 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1936,7 +1936,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, return; } - SmallVector AllModuleInputs; + ModuleInputs AllModuleInputs; SmallVector AllLinkerInputs; switch (OI.CompilerMode) { @@ -1977,10 +1977,8 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, // Source inputs always need to be compiled. assert(file_types::isPartOfSwiftCompilation(InputType)); - CompileJobAction::InputInfo previousBuildState = { - CompileJobAction::InputInfo::NeedsCascadingBuild, - llvm::sys::TimePoint<>::min() - }; + auto previousBuildState = + IncrementalJobAction::InputInfo::makeNeedsCascadingRebuild(); if (OutOfDateMap) previousBuildState = OutOfDateMap->lookup(InputArg); if (Args.hasArg(options::OPT_embed_bitcode)) { @@ -1988,7 +1986,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, Current, file_types::TY_LLVM_BC, previousBuildState); if (PCH) cast(Current)->addInput(PCH); - AllModuleInputs.push_back(Current); + AllModuleInputs.addInput(Current); Current = C.createAction(Current, OI.CompilerOutputType, 0); } else { @@ -1997,7 +1995,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, previousBuildState); if (PCH) cast(Current)->addInput(PCH); - AllModuleInputs.push_back(Current); + AllModuleInputs.addInput(Current); } AllLinkerInputs.push_back(Current); break; @@ -2009,7 +2007,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, // When generating a .swiftmodule as a top-level output (as opposed // to, for example, linking an image), treat .swiftmodule files as // inputs to a MergeModule action. - AllModuleInputs.push_back(Current); + AllModuleInputs.addInput(Current); break; } else if (OI.shouldLink()) { // Otherwise, if linking, pass .swiftmodule files as inputs to the @@ -2091,7 +2089,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, // Create a single CompileJobAction and a single BackendJobAction. JobAction *CA = C.createAction(file_types::TY_LLVM_BC); - AllModuleInputs.push_back(CA); + AllModuleInputs.addInput(CA); int InputIndex = 0; for (const InputPair &Input : Inputs) { @@ -2127,7 +2125,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, CA->addInput(C.createAction(*InputArg, InputType)); } - AllModuleInputs.push_back(CA); + AllModuleInputs.addInput(CA); AllLinkerInputs.push_back(CA); break; } @@ -2176,7 +2174,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, !AllModuleInputs.empty()) { // We're performing multiple compilations; set up a merge module step // so we generate a single swiftmodule as output. - MergeModuleAction = C.createAction(AllModuleInputs); + MergeModuleAction = std::move(AllModuleInputs).intoAction(C); } bool shouldPerformLTO = OI.LTOVariant != OutputInfo::LTOKind::None; From a81cf936ee1a0d77faf3881d17bf87eb683820b0 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 7 Oct 2020 16:36:29 -0700 Subject: [PATCH 287/745] [NFC] Promote MergeModuleJobAction to an IncrementalJobAction --- include/swift/Driver/Action.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/swift/Driver/Action.h b/include/swift/Driver/Action.h index b9e43872f548f..7b325fa0907c6 100644 --- a/include/swift/Driver/Action.h +++ b/include/swift/Driver/Action.h @@ -183,7 +183,8 @@ class IncrementalJobAction : public JobAction { public: static bool classof(const Action *A) { - return A->getKind() == Action::Kind::CompileJob; + return A->getKind() == Action::Kind::CompileJob || + A->getKind() == Action::Kind::MergeModuleJob; } }; @@ -280,12 +281,12 @@ class REPLJobAction : public JobAction { } }; -class MergeModuleJobAction : public JobAction { +class MergeModuleJobAction : public IncrementalJobAction { virtual void anchor() override; public: - MergeModuleJobAction(ArrayRef Inputs) - : JobAction(Action::Kind::MergeModuleJob, Inputs, - file_types::TY_SwiftModuleFile) {} + MergeModuleJobAction(ArrayRef Inputs, InputInfo input) + : IncrementalJobAction(Action::Kind::MergeModuleJob, Inputs, + file_types::TY_SwiftModuleFile, input) {} static bool classof(const Action *A) { return A->getKind() == Action::Kind::MergeModuleJob; From cecb7c8313bea26583bed64b621871d4d3d20dc4 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 7 Oct 2020 16:55:56 -0700 Subject: [PATCH 288/745] Schedule MergeModules Incrementally Plumb the logic necessary to schedule merge-modules incrementally. This means that if any inputs jobs to merge-modules run, merge-modules is run. But, if they are all skipped, merge-modules will be skipped as well. This requires some light special-casing of the legacy driver's incremental job handling because it assumes in a few places it can always extract a swiftdeps file. This invariant will be further broken when the precompile step for bridging headers is skipped as well. rdar://65893400 --- lib/Driver/Compilation.cpp | 27 ++++++++++++- lib/Driver/Driver.cpp | 38 ++++++++++++------- .../one-way-merge-module-fine.swift | 2 +- test/Incremental/CrossModule/linear.swift | 19 +++++++++- test/Incremental/CrossModule/transitive.swift | 17 +++++++++ 5 files changed, 85 insertions(+), 18 deletions(-) diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index bb6a7c5ca2570..e17d540bccb14 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -446,7 +446,7 @@ namespace driver { std::vector reloadAndRemarkDeps(const Job *FinishedCmd, int ReturnCode, - const bool forRanges) { + const bool forRanges) { const CommandOutput &Output = FinishedCmd->getOutput(); StringRef DependenciesFile = Output.getAdditionalOutputForType(file_types::TY_SwiftDeps); @@ -458,7 +458,8 @@ namespace driver { // coarse dependencies that always affect downstream nodes), but we're // not using either of those right now, and this logic should probably // be revisited when we are. - assert(FinishedCmd->getCondition() == Job::Condition::Always); + assert(isa(FinishedCmd->getSource()) || + FinishedCmd->getCondition() == Job::Condition::Always); return {}; } const bool compileExitedNormally = @@ -907,6 +908,7 @@ namespace driver { return everyIncrementalJob; }; + const Job *mergeModulesJob = nullptr; CommandSet jobsToSchedule; CommandSet initialCascadingCommands; for (const Job *cmd : Comp.getJobs()) { @@ -915,6 +917,11 @@ namespace driver { continue; } + if (isa(cmd->getSource())) { + assert(!mergeModulesJob && "multiple scheduled merge-modules jobs?"); + mergeModulesJob = cmd; + } + const Optional> shouldSchedAndIsCascading = computeShouldInitiallyScheduleJobAndDependendents(cmd, forRanges); if (!shouldSchedAndIsCascading) @@ -936,6 +943,15 @@ namespace driver { collectIncrementalExternallyDependentJobsFromDependencyGraph( forRanges)) jobsToSchedule.insert(cmd); + + // The merge-modules job is special: it *must* be scheduled if any other + // job has been scheduled because any other job can influence the + // structure of the resulting module. Additionally, the initial scheduling + // predicate above is only aware of intra-module changes. External + // dependencies changing *must* cause merge-modules to be scheduled. + if (!jobsToSchedule.empty() && mergeModulesJob) { + jobsToSchedule.insert(mergeModulesJob); + } return jobsToSchedule; } @@ -1031,6 +1047,13 @@ namespace driver { /// But returns None if there was a dependency read error. Optional> loadDependenciesAndComputeCondition(const Job *const Cmd, bool forRanges) { + // merge-modules Jobs do not have .swiftdeps files associated with them, + // however, their compilation condition is computed as a function of their + // inputs, so their condition can be used as normal. + if (isa(Cmd->getSource())) { + return std::make_pair(Cmd->getCondition(), true); + } + // Try to load the dependencies file for this job. If there isn't one, we // always have to run the job, but it doesn't affect any other jobs. If // there should be one but it's not present or can't be loaded, we have to diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 518ac607bdace..fa8366d97b328 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -2749,24 +2749,30 @@ static void addDiagFileOutputForPersistentPCHAction( /// If the file at \p input has not been modified since the last build (i.e. its /// mtime has not changed), adjust the Job's condition accordingly. -static void -handleCompileJobCondition(Job *J, CompileJobAction::InputInfo inputInfo, - StringRef input, bool alwaysRebuildDependents) { - if (inputInfo.status == CompileJobAction::InputInfo::NewlyAdded) { +static void handleCompileJobCondition(Job *J, + CompileJobAction::InputInfo inputInfo, + Optional input, + bool alwaysRebuildDependents) { using InputStatus = CompileJobAction::InputInfo::Status; + + if (inputInfo.status == InputStatus::NewlyAdded) { J->setCondition(Job::Condition::NewlyAdded); return; } + auto output = J->getOutput().getPrimaryOutputFilename(); bool hasValidModTime = false; llvm::sys::fs::file_status inputStatus; - if (!llvm::sys::fs::status(input, inputStatus)) { + if (input.hasValue() && !llvm::sys::fs::status(*input, inputStatus)) { + J->setInputModTime(inputStatus.getLastModificationTime()); + hasValidModTime = J->getInputModTime() == inputInfo.previousModTime; + } else if (!llvm::sys::fs::status(output, inputStatus)) { J->setInputModTime(inputStatus.getLastModificationTime()); hasValidModTime = true; } Job::Condition condition; - if (hasValidModTime && J->getInputModTime() == inputInfo.previousModTime) { + if (hasValidModTime) { switch (inputInfo.status) { case InputStatus::UpToDate: if (llvm::sys::fs::exists(output)) @@ -2942,14 +2948,18 @@ Job *Driver::buildJobsForAction(Compilation &C, const JobAction *JA, Job *J = C.addJob(std::move(ownedJob)); // If we track dependencies for this job, we may be able to avoid running it. - if (!J->getOutput() - .getAdditionalOutputForType(file_types::TY_SwiftDeps) - .empty()) { - if (InputActions.size() == 1) { - auto compileJob = cast(JA); - bool alwaysRebuildDependents = - C.getArgs().hasArg(options::OPT_driver_always_rebuild_dependents); - handleCompileJobCondition(J, compileJob->getInputInfo(), BaseInput, + if (auto incrementalJob = dyn_cast(JA)) { + const bool alwaysRebuildDependents = + C.getArgs().hasArg(options::OPT_driver_always_rebuild_dependents); + if (!J->getOutput() + .getAdditionalOutputForType(file_types::TY_SwiftDeps) + .empty()) { + if (InputActions.size() == 1) { + handleCompileJobCondition(J, incrementalJob->getInputInfo(), BaseInput, + alwaysRebuildDependents); + } + } else if (isa(JA)) { + handleCompileJobCondition(J, incrementalJob->getInputInfo(), None, alwaysRebuildDependents); } } diff --git a/test/Driver/Dependencies/one-way-merge-module-fine.swift b/test/Driver/Dependencies/one-way-merge-module-fine.swift index 4e1bcb5c4b182..5ef38421fc5bc 100644 --- a/test/Driver/Dependencies/one-way-merge-module-fine.swift +++ b/test/Driver/Dependencies/one-way-merge-module-fine.swift @@ -15,4 +15,4 @@ // CHECK-SECOND-NOT: warning // CHECK-SECOND-NOT: Handled -// CHECK-SECOND: Produced master.swiftmodule +// CHECK-SECOND-NOT: Produced master.swiftmodule diff --git a/test/Incremental/CrossModule/linear.swift b/test/Incremental/CrossModule/linear.swift index eada05f2cc737..488c715320159 100644 --- a/test/Incremental/CrossModule/linear.swift +++ b/test/Incremental/CrossModule/linear.swift @@ -38,4 +38,21 @@ // MODULE-B: Job finished: {merge-module: B.swiftmodule <= B.o} // MODULE-A: Job skipped: {compile: A.o <= A.swift} -// MODULE-A: Job finished: {merge-module: A.swiftmodule <= A.o} +// MODULE-A: Job skipped: {merge-module: A.swiftmodule <= A.o} + +// +// And ensure that the null build really is null. +// + +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s + +// MODULE-C-NULL: Job skipped: {compile: C.o <= C.swift} +// MODULE-C-NULL: Job skipped: {merge-module: C.swiftmodule <= C.o} + +// MODULE-B-NULL: Job skipped: {compile: B.o <= B.swift} +// MODULE-B-NULL: Job skipped: {merge-module: B.swiftmodule <= B.o} + +// MODULE-A-NULL: Job skipped: {compile: A.o <= A.swift} +// MODULE-A-NULL: Job skipped: {merge-module: A.swiftmodule <= A.o} diff --git a/test/Incremental/CrossModule/transitive.swift b/test/Incremental/CrossModule/transitive.swift index 002d4279a7406..83f354f082ed9 100644 --- a/test/Incremental/CrossModule/transitive.swift +++ b/test/Incremental/CrossModule/transitive.swift @@ -42,3 +42,20 @@ // MODULE-A: Queuing because of incremental external dependencies: {compile: A.o <= A.swift} // MODULE-A: Job finished: {compile: A.o <= A.swift} // MODULE-A: Job finished: {merge-module: A.swiftmodule <= A.o} + +// +// And ensure that the null build really is null. +// + +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s + +// MODULE-C-NULL: Job skipped: {compile: C.o <= C.swift} +// MODULE-C-NULL: Job skipped: {merge-module: C.swiftmodule <= C.o} + +// MODULE-B-NULL: Job skipped: {compile: B.o <= B.swift} +// MODULE-B-NULL: Job skipped: {merge-module: B.swiftmodule <= B.o} + +// MODULE-A-NULL: Job skipped: {compile: A.o <= A.swift} +// MODULE-A-NULL: Job skipped: {merge-module: A.swiftmodule <= A.o} From 5e892b3dc950761697a5f2672dbb47c6e8bd1ce3 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 6 Oct 2020 18:57:30 -0400 Subject: [PATCH 289/745] Sema: Eagerly expand scopes in TypeCheckASTNodeAtLocRequest --- lib/Sema/TypeCheckStmt.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index f320bbe34f09b..97c2846b23dfd 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -20,6 +20,7 @@ #include "MiscDiagnostics.h" #include "swift/Subsystems.h" #include "swift/AST/ASTPrinter.h" +#include "swift/AST/ASTScope.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/DiagnosticsSema.h" @@ -1892,6 +1893,8 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(Evaluator &evaluator, if (auto *AFD = dyn_cast(DC)) { if (AFD->isBodyTypeChecked()) return false; + + ASTScope::expandFunctionBody(AFD); } // Function builder function doesn't support partial type checking. From 0c11fad5bd8018e18124f3e3cd4333cf99040c5b Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 6 Oct 2020 17:03:49 -0400 Subject: [PATCH 290/745] ASTScope: Don't silently drop duplicate AST nodes Instead, let's rely on the existing source range assertions. They will fire if we add two nodes with overlapping or equal source ranges. --- include/swift/AST/ASTScope.h | 21 --- lib/AST/ASTScopeCreation.cpp | 316 ++++++++++------------------------- 2 files changed, 84 insertions(+), 253 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 0e1f65e71625d..1e70eb886a34d 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -224,7 +224,6 @@ class ASTScopeImpl { virtual NullablePtr getDeclAttributeIfAny() const { return nullptr; } - virtual NullablePtr getReferrent() const { return nullptr; } #pragma mark - debugging and printing @@ -470,9 +469,6 @@ class Portion { virtual NullablePtr getLookupLimitFor(const GenericTypeOrExtensionScope *) const; - virtual const Decl * - getReferrentOfScope(const GenericTypeOrExtensionScope *s) const; - virtual NullablePtr insertionPointForDeferredExpansion(IterableTypeScope *) const = 0; }; @@ -493,9 +489,6 @@ class Portion { NullablePtr getLookupLimitFor(const GenericTypeOrExtensionScope *) const override; - const Decl * - getReferrentOfScope(const GenericTypeOrExtensionScope *s) const override; - NullablePtr insertionPointForDeferredExpansion(IterableTypeScope *) const override; }; @@ -570,7 +563,6 @@ class GenericTypeOrExtensionScope : public ASTScopeImpl { virtual Decl *getDecl() const = 0; NullablePtr getDeclIfAny() const override { return getDecl(); } - NullablePtr getReferrent() const override; private: AnnotatedInsertionPoint @@ -745,7 +737,6 @@ class GenericParamScope final : public ASTScopeImpl { /// Actually holder is always a GenericContext, need to test if /// ProtocolDecl or SubscriptDecl but will refactor later. - NullablePtr getReferrent() const override; std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; @@ -788,8 +779,6 @@ class AbstractFunctionDeclScope final : public ASTScopeImpl { virtual NullablePtr getDeclIfAny() const override { return decl; } Decl *getDecl() const { return decl; } - NullablePtr getReferrent() const override; - protected: NullablePtr genericParams() const override; }; @@ -902,7 +891,6 @@ class AttachedPropertyWrapperScope final : public ASTScopeImpl { NullablePtr getDeclAttributeIfAny() const override { return attr; } - NullablePtr getReferrent() const override; private: void expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator &); @@ -970,8 +958,6 @@ class PatternEntryDeclScope final : public AbstractPatternEntryScope { SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; - NullablePtr getReferrent() const override; - protected: bool lookupLocalsOrMembers(DeclConsumer) const override; bool isLabeledStmtLookupTerminator() const override; @@ -1072,7 +1058,6 @@ class CaptureListScope final : public ASTScopeImpl { getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; NullablePtr getExprIfAny() const override { return expr; } Expr *getExpr() const { return expr; } - NullablePtr getReferrent() const override; bool lookupLocalsOrMembers(DeclConsumer) const override; }; @@ -1094,7 +1079,6 @@ class ClosureParametersScope final : public ASTScopeImpl { } NullablePtr getExprIfAny() const override { return closureExpr; } Expr *getExpr() const { return closureExpr; } - NullablePtr getReferrent() const override; protected: ASTScopeImpl *expandSpecifically(ScopeCreator &scopeCreator) override; @@ -1128,7 +1112,6 @@ class TopLevelCodeScope final : public ASTScopeImpl { getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; virtual NullablePtr getDeclIfAny() const override { return decl; } Decl *getDecl() const { return decl; } - NullablePtr getReferrent() const override; }; /// The \c _@specialize attribute. @@ -1153,7 +1136,6 @@ class SpecializeAttributeScope final : public ASTScopeImpl { NullablePtr getDeclAttributeIfAny() const override { return specializeAttr; } - NullablePtr getReferrent() const override; protected: ASTScopeImpl *expandSpecifically(ScopeCreator &) override; @@ -1183,7 +1165,6 @@ class DifferentiableAttributeScope final : public ASTScopeImpl { NullablePtr getDeclAttributeIfAny() const override { return differentiableAttr; } - NullablePtr getReferrent() const override; protected: ASTScopeImpl *expandSpecifically(ScopeCreator &) override; @@ -1214,7 +1195,6 @@ class SubscriptDeclScope final : public ASTScopeImpl { public: virtual NullablePtr getDeclIfAny() const override { return decl; } Decl *getDecl() const { return decl; } - NullablePtr getReferrent() const override; protected: NullablePtr genericParams() const override; @@ -1244,7 +1224,6 @@ class AbstractStmtScope : public ASTScopeImpl { getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; virtual Stmt *getStmt() const = 0; NullablePtr getStmtIfAny() const override { return getStmt(); } - NullablePtr getReferrent() const override; protected: bool isLabeledStmtLookupTerminator() const override; diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index bbee272aed3ae..6dd649c11c747 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -132,61 +132,6 @@ static std::vector asNodeVector(DeclRange dr) { namespace swift { namespace ast_scope { -namespace { -/// Use me with any ASTNode, Expr*, Decl*, or Stmt* -/// I will yield a void* that is the same, even when given an Expr* and a -/// ClosureExpr* because I take the Expr*, figure its real class, then up -/// cast. -/// Useful for duplicate checking. -class UniquePointerCalculator - : public ASTVisitor { -public: - template const void *visit(const T *x) { - return const_cast(x); - } - - // Call these only from the superclass - void *visitDecl(Decl *e) { return e; } - void *visitStmt(Stmt *e) { return e; } - void *visitExpr(Expr *e) { return e; } - void *visitPattern(Pattern *e) { return e; } - void *visitDeclAttribute(DeclAttribute *e) { return e; } - -// Provide default implementations for statements as ASTVisitor does for Exprs -#define STMT(CLASS, PARENT) \ - void *visit##CLASS##Stmt(CLASS##Stmt *S) { return visitStmt(S); } -#include "swift/AST/StmtNodes.def" - -// Provide default implementations for patterns as ASTVisitor does for Exprs -#define PATTERN(CLASS, PARENT) \ - void *visit##CLASS##Pattern(CLASS##Pattern *S) { return visitPattern(S); } -#include "swift/AST/PatternNodes.def" -}; - -/// A set that does the right pointer calculation for comparing Decls to -/// DeclContexts, and Exprs -class NodeSet { - ::llvm::DenseSet pointers; - -public: - bool contains(const ASTScopeImpl *const s) { - if (auto *r = s->getReferrent().getPtrOrNull()) - return pointers.count(r); - return false; // never exclude a non-checkable scope - } - bool insert(const ASTScopeImpl *const s) { - if (auto *r = s->getReferrent().getPtrOrNull()) - return pointers.insert(r).second; - return true; - } - void erase(const ASTScopeImpl *const s) { - if (auto *r = s->getReferrent().getPtrOrNull()) - pointers.erase(r); - } -}; -} // namespace - #pragma mark ScopeCreator class ScopeCreator final { @@ -198,15 +143,9 @@ class ScopeCreator final { ASTSourceFileScope *const sourceFileScope; ASTContext &getASTContext() const { return ctx; } - /// The AST can have duplicate nodes, and we don't want to create scopes for - /// those. - NodeSet scopedNodes; - ScopeCreator(SourceFile *SF) : ctx(SF->getASTContext()), - sourceFileScope(new (ctx) ASTSourceFileScope(SF, this)) { - ctx.addDestructorCleanup(scopedNodes); - } + sourceFileScope(new (ctx) ASTSourceFileScope(SF, this)) {} ScopeCreator(const ScopeCreator &) = delete; // ensure no copies ScopeCreator(const ScopeCreator &&) = delete; // ensure no moves @@ -222,9 +161,7 @@ class ScopeCreator final { Optional endLoc) { auto *ip = insertionPoint; for (auto nd : nodesOrDeclsToAdd) { - auto *const newIP = - addToScopeTreeAndReturnInsertionPoint(nd, ip, endLoc).getPtrOr(ip); - ip = newIP; + ip = addToScopeTreeAndReturnInsertionPoint(nd, ip, endLoc); } return ip; } @@ -234,12 +171,12 @@ class ScopeCreator final { void addToScopeTree(ASTNode n, ASTScopeImpl *parent) { (void)addToScopeTreeAndReturnInsertionPoint(n, parent, None); } - /// Return new insertion point if the scope was not a duplicate + /// Return new insertion point. /// For ease of searching, don't call unless insertion point is needed /// /// \param endLoc The end location for any "scopes until the end" that /// we introduce here, such as PatternEntryDeclScope and GuardStmtScope - NullablePtr + ASTScopeImpl * addToScopeTreeAndReturnInsertionPoint(ASTNode, ASTScopeImpl *parent, Optional endLoc); @@ -299,31 +236,6 @@ class ScopeCreator final { return true; } - /// Create a new scope of class ChildScope initialized with a ChildElement, - /// expandScope it, - /// add it as a child of the receiver, and return the child and the scope to - /// receive more decls. - template - ASTScopeImpl *constructExpandAndInsertUncheckable(ASTScopeImpl *parent, - Args... args) { - ASTScopeAssert(!Scope(args...).getReferrent(), - "Not checking for duplicate ASTNode but class supports it"); - return constructExpandAndInsert(parent, args...); - } - - template - NullablePtr - ifUniqueConstructExpandAndInsert(ASTScopeImpl *parent, Args... args) { - Scope dryRun(args...); - ASTScopeAssert( - dryRun.getReferrent(), - "Checking for duplicate ASTNode but class does not support it"); - if (scopedNodes.insert(&dryRun)) - return constructExpandAndInsert(parent, args...); - return nullptr; - } - -private: template ASTScopeImpl *constructExpandAndInsert(ASTScopeImpl *parent, Args... args) { auto *child = new (ctx) Scope(args...); @@ -342,15 +254,7 @@ class ScopeCreator final { ASTScopeImpl *constructWithPortionExpandAndInsert(ASTScopeImpl *parent, Args... args) { const Portion *portion = new (ctx) PortionClass(); - return constructExpandAndInsertUncheckable(parent, portion, args...); - } - - template - NullablePtr - ifUniqueConstructWithPortionExpandAndInsert(ASTScopeImpl *parent, - Args... args) { - const Portion *portion = new (ctx) PortionClass(); - return ifUniqueConstructExpandAndInsert(parent, portion, args...); + return constructExpandAndInsert(parent, portion, args...); } void addExprToScopeTree(Expr *expr, ASTScopeImpl *parent) { @@ -370,13 +274,13 @@ class ScopeCreator final { std::pair walkToExprPre(Expr *E) override { if (auto *closure = dyn_cast(E)) { scopeCreator - .ifUniqueConstructExpandAndInsert( + .constructExpandAndInsert( parent, closure); return {false, E}; } if (auto *capture = dyn_cast(E)) { scopeCreator - .ifUniqueConstructExpandAndInsert( + .constructExpandAndInsert( parent, capture); return {false, E}; } @@ -411,9 +315,8 @@ class ScopeCreator final { return parent; auto *s = parent; for (unsigned i : indices(generics->getParams())) - s = ifUniqueConstructExpandAndInsert( - s, parameterizedDecl, generics, i) - .getPtrOr(s); + s = constructExpandAndInsert( + s, parameterizedDecl, generics, i); return s; } @@ -431,7 +334,7 @@ class ScopeCreator final { /// \param endLoc Must be valid iff the pattern binding is in a local /// scope, in which case this is the last source location where the /// pattern bindings are going to be visible. - NullablePtr + ASTScopeImpl * addPatternBindingToScopeTree(PatternBindingDecl *patternBinding, ASTScopeImpl *parent, Optional endLoc); @@ -555,8 +458,8 @@ namespace swift { namespace ast_scope { class NodeAdder - : public ASTVisitor, - NullablePtr, NullablePtr, + : public ASTVisitor { Optional endLoc; @@ -565,13 +468,9 @@ class NodeAdder #pragma mark ASTNodes that do not create scopes - // Even ignored Decls and Stmts must extend the source range of a scope: - // E.g. a braceStmt with some definitions that ends in a statement that - // accesses such a definition must resolve as being IN the scope. - #define VISIT_AND_IGNORE(What) \ - NullablePtr visit##What(What *w, ASTScopeImpl *p, \ - ScopeCreator &) { \ + ASTScopeImpl *visit##What(What *w, ASTScopeImpl *p, \ + ScopeCreator &) { \ return p; \ } @@ -602,9 +501,9 @@ class NodeAdder #pragma mark simple creation ignoring deferred nodes #define VISIT_AND_CREATE(What, ScopeClass) \ - NullablePtr visit##What(What *w, ASTScopeImpl *p, \ - ScopeCreator &scopeCreator) { \ - return scopeCreator.ifUniqueConstructExpandAndInsert(p, w); \ + ASTScopeImpl *visit##What(What *w, ASTScopeImpl *p, \ + ScopeCreator &scopeCreator) { \ + return scopeCreator.constructExpandAndInsert(p, w); \ } VISIT_AND_CREATE(SubscriptDecl, SubscriptDeclScope) @@ -623,9 +522,9 @@ class NodeAdder #pragma mark 2D simple creation (ignoring deferred nodes) #define VISIT_AND_CREATE_WHOLE_PORTION(What, WhatScope) \ - NullablePtr visit##What(What *w, ASTScopeImpl *p, \ - ScopeCreator &scopeCreator) { \ - return scopeCreator.ifUniqueConstructWithPortionExpandAndInsert< \ + ASTScopeImpl *visit##What(What *w, ASTScopeImpl *p, \ + ScopeCreator &scopeCreator) { \ + return scopeCreator.constructWithPortionExpandAndInsert< \ WhatScope, GenericTypeOrExtensionWholePortion>(p, w); \ } @@ -640,8 +539,8 @@ class NodeAdder // This declaration is handled from // addChildrenForAllLocalizableAccessorsInSourceOrder - NullablePtr visitAccessorDecl(AccessorDecl *ad, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + ASTScopeImpl *visitAccessorDecl(AccessorDecl *ad, ASTScopeImpl *p, + ScopeCreator &scopeCreator) { return visitAbstractFunctionDecl(ad, p, scopeCreator); } @@ -650,17 +549,17 @@ class NodeAdder // Each of the following creates a new scope, so that nodes which were parsed // after them need to be placed in scopes BELOW them in the tree. So pass down // the deferred nodes. - NullablePtr visitGuardStmt(GuardStmt *e, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + ASTScopeImpl *visitGuardStmt(GuardStmt *e, ASTScopeImpl *p, + ScopeCreator &scopeCreator) { ASTScopeAssert(endLoc.hasValue(), "GuardStmt outside of a BraceStmt?"); - return scopeCreator.ifUniqueConstructExpandAndInsert( + return scopeCreator.constructExpandAndInsert( p, e, *endLoc); } - NullablePtr visitTopLevelCodeDecl(TopLevelCodeDecl *d, - ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + ASTScopeImpl *visitTopLevelCodeDecl(TopLevelCodeDecl *d, + ASTScopeImpl *p, + ScopeCreator &scopeCreator) { ASTScopeAssert(endLoc.hasValue(), "TopLevelCodeDecl in wrong place?"); - return scopeCreator.ifUniqueConstructExpandAndInsert( + return scopeCreator.constructExpandAndInsert( p, d, *endLoc); } @@ -670,21 +569,21 @@ class NodeAdder ASTScope_unreachable("SourceFiles are orphans."); } - NullablePtr visitYieldStmt(YieldStmt *ys, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + ASTScopeImpl *visitYieldStmt(YieldStmt *ys, ASTScopeImpl *p, + ScopeCreator &scopeCreator) { for (Expr *e : ys->getYields()) visitExpr(e, p, scopeCreator); return p; } - NullablePtr visitDeferStmt(DeferStmt *ds, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + ASTScopeImpl *visitDeferStmt(DeferStmt *ds, ASTScopeImpl *p, + ScopeCreator &scopeCreator) { visitFuncDecl(ds->getTempDecl(), p, scopeCreator); return p; } - NullablePtr visitBraceStmt(BraceStmt *bs, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + ASTScopeImpl *visitBraceStmt(BraceStmt *bs, ASTScopeImpl *p, + ScopeCreator &scopeCreator) { if (bs->empty()) return p; @@ -710,17 +609,16 @@ class NodeAdder if (endLoc.hasValue()) endLocForBraceStmt = *endLoc; - auto maybeBraceScope = - scopeCreator.ifUniqueConstructExpandAndInsert( - p, bs, std::move(localFuncsAndTypes), std::move(localVars), - endLocForBraceStmt); if (auto *s = scopeCreator.getASTContext().Stats) ++s->getFrontendCounters().NumBraceStmtASTScopes; - return maybeBraceScope.getPtrOr(p); + return + scopeCreator.constructExpandAndInsert( + p, bs, std::move(localFuncsAndTypes), std::move(localVars), + endLocForBraceStmt); } - NullablePtr + ASTScopeImpl * visitPatternBindingDecl(PatternBindingDecl *patternBinding, ASTScopeImpl *parentScope, ScopeCreator &scopeCreator) { @@ -728,43 +626,43 @@ class NodeAdder patternBinding, parentScope, endLoc); } - NullablePtr visitEnumElementDecl(EnumElementDecl *eed, - ASTScopeImpl *p, - ScopeCreator &scopeCreator) { - scopeCreator.constructExpandAndInsertUncheckable(p, eed); + ASTScopeImpl *visitEnumElementDecl(EnumElementDecl *eed, + ASTScopeImpl *p, + ScopeCreator &scopeCreator) { + scopeCreator.constructExpandAndInsert(p, eed); return p; } - NullablePtr visitIfConfigDecl(IfConfigDecl *icd, - ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + ASTScopeImpl *visitIfConfigDecl(IfConfigDecl *icd, + ASTScopeImpl *p, + ScopeCreator &scopeCreator) { ASTScope_unreachable( "Should be handled inside of " "expandIfConfigClausesThenCullAndSortElementsOrMembers"); } - NullablePtr visitReturnStmt(ReturnStmt *rs, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + ASTScopeImpl *visitReturnStmt(ReturnStmt *rs, ASTScopeImpl *p, + ScopeCreator &scopeCreator) { if (rs->hasResult()) visitExpr(rs->getResult(), p, scopeCreator); return p; } - NullablePtr visitThrowStmt(ThrowStmt *ts, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + ASTScopeImpl *visitThrowStmt(ThrowStmt *ts, ASTScopeImpl *p, + ScopeCreator &scopeCreator) { visitExpr(ts->getSubExpr(), p, scopeCreator); return p; } - NullablePtr visitPoundAssertStmt(PoundAssertStmt *pas, - ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + ASTScopeImpl *visitPoundAssertStmt(PoundAssertStmt *pas, + ASTScopeImpl *p, + ScopeCreator &scopeCreator) { visitExpr(pas->getCondition(), p, scopeCreator); return p; } - NullablePtr visitExpr(Expr *expr, ASTScopeImpl *p, - ScopeCreator &scopeCreator) { + ASTScopeImpl *visitExpr(Expr *expr, ASTScopeImpl *p, + ScopeCreator &scopeCreator) { if (expr) scopeCreator.addExprToScopeTree(expr, p); @@ -776,7 +674,7 @@ class NodeAdder // These definitions are way down here so it can call into // NodeAdder -NullablePtr +ASTScopeImpl * ScopeCreator::addToScopeTreeAndReturnInsertionPoint(ASTNode n, ASTScopeImpl *parent, Optional endLoc) { @@ -823,23 +721,23 @@ void ScopeCreator::addChildrenForKnownAttributes(ValueDecl *decl, for (auto *attr : relevantAttrs) { if (auto *diffAttr = dyn_cast(attr)) { - ifUniqueConstructExpandAndInsert( + constructExpandAndInsert( parent, diffAttr, decl); } else if (auto *specAttr = dyn_cast(attr)) { if (auto *afd = dyn_cast(decl)) { - ifUniqueConstructExpandAndInsert( + constructExpandAndInsert( parent, specAttr, afd); } } else if (auto *customAttr = dyn_cast(attr)) { if (auto *vd = dyn_cast(decl)) { - ifUniqueConstructExpandAndInsert( + constructExpandAndInsert( parent, customAttr, vd); } } } } -NullablePtr +ASTScopeImpl * ScopeCreator::addPatternBindingToScopeTree(PatternBindingDecl *patternBinding, ASTScopeImpl *parentScope, Optional endLoc) { @@ -861,10 +759,9 @@ ScopeCreator::addPatternBindingToScopeTree(PatternBindingDecl *patternBinding, } insertionPoint = - ifUniqueConstructExpandAndInsert( + constructExpandAndInsert( insertionPoint, patternBinding, i, - isLocalBinding, endLocForBinding) - .getPtrOr(insertionPoint); + isLocalBinding, endLocForBinding); ASTScopeAssert(isLocalBinding || insertionPoint == parentScope, "Bindings at the top-level or members of types should " @@ -1017,7 +914,7 @@ ParameterListScope::expandAScopeThatDoesNotCreateANewInsertionPoint( for (ParamDecl *pd : params->getArray()) { if (pd->hasDefaultExpr()) scopeCreator - .constructExpandAndInsertUncheckable( + .constructExpandAndInsert( this, pd); } } @@ -1041,7 +938,7 @@ PatternEntryDeclScope::expandAScopeThatCreatesANewInsertionPoint( patternEntry.getOriginalInit()->getStartLoc(), decl->getStartLoc()), "Original inits are always after the '='"); scopeCreator - .constructExpandAndInsertUncheckable( + .constructExpandAndInsert( this, decl, patternEntryIndex); } @@ -1074,7 +971,7 @@ ConditionalClausePatternUseScope::expandAScopeThatCreatesANewInsertionPoint( auto *initializer = sec.getInitializer(); if (!isa(initializer)) { scopeCreator - .constructExpandAndInsertUncheckable( + .constructExpandAndInsert( this, initializer); } @@ -1105,7 +1002,7 @@ GuardStmtScope::expandAScopeThatCreatesANewInsertionPoint(ScopeCreator & auto *body = stmt->getBody(); if (!body->empty()) { scopeCreator - .constructExpandAndInsertUncheckable( + .constructExpandAndInsert( conditionLookupParent, this, stmt->getBody()); } @@ -1142,14 +1039,13 @@ AnnotatedInsertionPoint TopLevelCodeScope::expandAScopeThatCreatesANewInsertionPoint(ScopeCreator & scopeCreator) { - if (auto *body = - scopeCreator - .addToScopeTreeAndReturnInsertionPoint(decl->getBody(), this, endLoc) - .getPtrOrNull()) - return {body, "So next top level code scope and put its decls in its body " - "under a guard statement scope (etc) from the last top level " - "code scope"}; - return {this, "No body"}; + auto *body = + scopeCreator + .addToScopeTreeAndReturnInsertionPoint(decl->getBody(), this, endLoc); + + return {body, "So next top level code scope and put its decls in its body " + "under a guard statement scope (etc) from the last top level " + "code scope"}; } #pragma mark expandAScopeThatDoesNotCreateANewInsertionPoint @@ -1171,7 +1067,7 @@ void AbstractFunctionDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint( auto *params = decl->getParameters(); if (params->size() > 0) { - scopeCreator.constructExpandAndInsertUncheckable( + scopeCreator.constructExpandAndInsert( leaf, params, nullptr); } } @@ -1180,16 +1076,14 @@ void AbstractFunctionDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint( // We create body scopes when there is no body for source kit to complete // erroneous code in bodies. if (decl->getBodySourceRange().isValid()) { - scopeCreator.constructExpandAndInsertUncheckable(leaf, - decl); + scopeCreator.constructExpandAndInsert(leaf, decl); } } void EnumElementScope::expandAScopeThatDoesNotCreateANewInsertionPoint( ScopeCreator &scopeCreator) { if (auto *pl = decl->getParameterList()) - scopeCreator.constructExpandAndInsertUncheckable( - this, pl, nullptr); + scopeCreator.constructExpandAndInsert(this, pl, nullptr); // The invariant that the raw value expression can never introduce a new scope // is checked in Parse. However, this guarantee is not future-proof. Compute // and add the raw value expression anyways just to be defensive. @@ -1261,8 +1155,7 @@ void SwitchStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint( for (auto caseStmt : stmt->getCases()) { if (isLocalizable(caseStmt)) - scopeCreator.ifUniqueConstructExpandAndInsert(this, - caseStmt); + scopeCreator.constructExpandAndInsert(this, caseStmt); } } @@ -1277,8 +1170,7 @@ void ForEachStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint( // above. if (!stmt->getBody()->isImplicit()) { if (isLocalizable(stmt->getBody())) - scopeCreator.constructExpandAndInsertUncheckable( - this, stmt); + scopeCreator.constructExpandAndInsert(this, stmt); } } @@ -1292,14 +1184,12 @@ void CaseStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint( ScopeCreator &scopeCreator) { for (auto &item : stmt->getCaseLabelItems()) { if (item.getGuardExpr()) { - scopeCreator.constructExpandAndInsertUncheckable( - this, item); + scopeCreator.constructExpandAndInsert(this, item); } } if (!stmt->getBody()->empty()) { - scopeCreator.constructExpandAndInsertUncheckable( - this, stmt); + scopeCreator.constructExpandAndInsert(this, stmt); } } @@ -1318,7 +1208,7 @@ void SubscriptDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint( scopeCreator.addChildrenForKnownAttributes(decl, this); auto *leaf = scopeCreator.addNestedGenericParamScopesToTree( decl, decl->getGenericParams(), this); - scopeCreator.constructExpandAndInsertUncheckable( + scopeCreator.constructExpandAndInsert( leaf, decl->getIndices(), decl->getAccessor(AccessorKind::Get)); scopeCreator.addChildrenForAllLocalizableAccessorsInSourceOrder(decl, leaf); } @@ -1327,8 +1217,7 @@ void CaptureListScope::expandAScopeThatDoesNotCreateANewInsertionPoint( ScopeCreator &scopeCreator) { auto *closureExpr = expr->getClosureBody(); scopeCreator - .ifUniqueConstructExpandAndInsert( - this, closureExpr); + .constructExpandAndInsert(this, closureExpr); } void ClosureParametersScope::expandAScopeThatDoesNotCreateANewInsertionPoint( @@ -1454,7 +1343,7 @@ ASTScopeImpl *LabeledConditionalStmtScope::createNestedConditionalClauseScopes( break; case StmtConditionElement::CK_PatternBinding: insertionPoint = - scopeCreator.constructExpandAndInsertUncheckable< + scopeCreator.constructExpandAndInsert< ConditionalClausePatternUseScope>( insertionPoint, sec, endLoc); break; @@ -1512,43 +1401,6 @@ ScopeCreator &ASTScopeImpl::getScopeCreator() { ScopeCreator &ASTSourceFileScope::getScopeCreator() { return *scopeCreator; } -#pragma mark getReferrent - - // These are the scopes whose ASTNodes (etc) might be duplicated in the AST - // getReferrent is the cookie used to dedup them - -#define GET_REFERRENT(Scope, x) \ - NullablePtr Scope::getReferrent() const { \ - return UniquePointerCalculator().visit(x); \ - } - -GET_REFERRENT(AbstractFunctionDeclScope, getDecl()) -// If the PatternBindingDecl is a dup, detect it for the first -// PatternEntryDeclScope; the others are subscopes. -GET_REFERRENT(PatternEntryDeclScope, getPattern()) -GET_REFERRENT(TopLevelCodeScope, getDecl()) -GET_REFERRENT(SubscriptDeclScope, getDecl()) -GET_REFERRENT(GenericParamScope, paramList->getParams()[index]) -GET_REFERRENT(AbstractStmtScope, getStmt()) -GET_REFERRENT(CaptureListScope, getExpr()) -GET_REFERRENT(ClosureParametersScope, getExpr()) -GET_REFERRENT(SpecializeAttributeScope, specializeAttr) -GET_REFERRENT(DifferentiableAttributeScope, differentiableAttr) -GET_REFERRENT(AttachedPropertyWrapperScope, attr) -GET_REFERRENT(GenericTypeOrExtensionScope, portion->getReferrentOfScope(this)); - -const Decl * -Portion::getReferrentOfScope(const GenericTypeOrExtensionScope *s) const { - return nullptr; -}; - -const Decl *GenericTypeOrExtensionWholePortion::getReferrentOfScope( - const GenericTypeOrExtensionScope *s) const { - return s->getDecl(); -}; - -#undef GET_REFERRENT - #pragma mark currency NullablePtr ASTScopeImpl::insertionPointForDeferredExpansion() { return nullptr; From ee0d008178d6046e12acaeaa9aad4f5d8647814c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 7 Oct 2020 15:22:49 -0400 Subject: [PATCH 291/745] Parse: Preserve source order when code completion adds delayed declarations --- include/swift/AST/DeclContext.h | 22 +++++-- lib/AST/DeclContext.cpp | 109 +++++++++++++++++++++++++++----- lib/Parse/Parser.cpp | 4 +- 3 files changed, 110 insertions(+), 25 deletions(-) diff --git a/include/swift/AST/DeclContext.h b/include/swift/AST/DeclContext.h index e71f5d96c95fd..c6ef79874d2b9 100644 --- a/include/swift/AST/DeclContext.h +++ b/include/swift/AST/DeclContext.h @@ -782,9 +782,20 @@ class IterableDeclContext { /// abstractions on top of member loading, such as a name lookup table. DeclRange getCurrentMembersWithoutLoading() const; - /// Add a member to this context. If the hint decl is specified, the new decl - /// is inserted immediately after the hint. - void addMember(Decl *member, Decl *hint = nullptr); + /// Add a member to this context. + /// + /// If the hint decl is specified, the new decl is inserted immediately + /// after the hint. + /// + /// If insertAtHead is set, the new decl is inserted at the beginning of + /// the list. + /// + /// Otherwise, it is inserted at the end. + void addMember(Decl *member, Decl *hint = nullptr, bool insertAtHead = false); + + /// Add a member in the right place to preserve source order. This should + /// only be called from the code completion delayed parsing path. + void addMemberPreservingSourceOrder(Decl *member); /// Check whether there are lazily-loaded members. bool hasLazyMembers() const { @@ -862,10 +873,7 @@ class IterableDeclContext { private: /// Add a member to the list for iteration purposes, but do not notify the /// subclass that we have done so. - /// - /// This is used internally when loading members, because loading a - /// member is an invisible addition. - void addMemberSilently(Decl *member, Decl *hint = nullptr) const; + void addMemberSilently(Decl *member, Decl *hint, bool insertAtHead) const; }; /// Define simple_display for DeclContexts but not for subclasses in order to diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index 25224941d99af..2ef4cd00deb9c 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -764,10 +764,33 @@ ArrayRef IterableDeclContext::getSemanticMembers() const { ArrayRef()); } +void IterableDeclContext::addMemberPreservingSourceOrder(Decl *member) { + auto &SM = getASTContext().SourceMgr; + + SourceLoc start = member->getStartLoc(); + Decl *hint = nullptr; + + for (auto *existingMember : getMembers()) { + if (existingMember->isImplicit()) + continue; + + if (isa(existingMember) || + isa(existingMember)) + continue; + + if (!SM.isBeforeInBuffer(existingMember->getEndLoc(), start)) + break; + + hint = existingMember; + } + + addMember(member, hint, /*insertAtHead=*/hint == nullptr); +} + /// Add a member to this context. -void IterableDeclContext::addMember(Decl *member, Decl *Hint) { +void IterableDeclContext::addMember(Decl *member, Decl *hint, bool insertAtHead) { // Add the member to the list of declarations without notification. - addMemberSilently(member, Hint); + addMemberSilently(member, hint, insertAtHead); // Notify our parent declaration that we have added the member, which can // be used to update the lookup tables. @@ -790,29 +813,83 @@ void IterableDeclContext::addMember(Decl *member, Decl *Hint) { } } -void IterableDeclContext::addMemberSilently(Decl *member, Decl *hint) const { +void IterableDeclContext::addMemberSilently(Decl *member, Decl *hint, + bool insertAtHead) const { assert(!isa(member) && "Accessors should not be added here"); assert(!member->NextDecl && "Already added to a container"); - // If there is a hint decl that specifies where to add this, just - // link into the chain immediately following it. - if (hint) { +#ifndef NDEBUG + auto checkSourceRange = [&](Decl *prev, Decl *next) { + if (!member->getDeclContext()->getParentSourceFile()) + return; + + auto shouldSkip = [](Decl *d) { + if (isa(d) || isa(d) || isa(d)) + return true; + + if (d->isImplicit()) + return true; + + return false; + }; + + if (shouldSkip(prev) || shouldSkip(next)) + return; + + SourceLoc prevEnd = prev->getEndLoc(); + SourceLoc nextStart = next->getStartLoc(); + + if (!prevEnd.isValid() || !nextStart.isValid()) + return; + + if (getASTContext().SourceMgr.isBeforeInBuffer(prevEnd, nextStart)) + return; + + llvm::errs() << "Source ranges out of order in addMember():\n"; + prev->dump(llvm::errs()); + next->dump(llvm::errs()); + abort(); + }; +#endif + + // Empty list. + if (!FirstDeclAndLazyMembers.getPointer()) { + assert(hint == nullptr); + + FirstDeclAndLazyMembers.setPointer(member); + LastDeclAndKind.setPointer(member); + + // Insertion at the head. + } else if (insertAtHead) { + assert(hint == nullptr); + + member->NextDecl = FirstDeclAndLazyMembers.getPointer(); + FirstDeclAndLazyMembers.setPointer(member); + + // Insertion at the tail. + } else if (hint == nullptr) { + auto *last = LastDeclAndKind.getPointer(); + +#ifndef NDEBUG + checkSourceRange(last, member); +#endif + + last->NextDecl = member; + LastDeclAndKind.setPointer(member); + + // Insertion after 'hint' (which may be the tail). + } else { +#ifndef NDEBUG + checkSourceRange(hint, member); +#endif + member->NextDecl = hint->NextDecl; hint->NextDecl = member; - // If the hint was the last in the parent context's chain, update it. + // Handle case where the 'hint' is the tail. if (LastDeclAndKind.getPointer() == hint) LastDeclAndKind.setPointer(member); - return; - } - - if (auto last = LastDeclAndKind.getPointer()) { - last->NextDecl = member; - assert(last != member && "Simple cycle in decl list"); - } else { - FirstDeclAndLazyMembers.setPointer(member); } - LastDeclAndKind.setPointer(member); } void IterableDeclContext::setMemberLoader(LazyMemberLoader *loader, diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 66eeaced9ffa6..4dd6fc627b3b7 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -189,9 +189,9 @@ void Parser::performCodeCompletionSecondPassImpl( parseDecl(ParseDeclOptions(info.Flags), /*IsAtStartOfLineOrPreviousHadSemi=*/true, [&](Decl *D) { if (auto *NTD = dyn_cast(DC)) { - NTD->addMember(D); + NTD->addMemberPreservingSourceOrder(D); } else if (auto *ED = dyn_cast(DC)) { - ED->addMember(D); + ED->addMemberPreservingSourceOrder(D); } else if (auto *SF = dyn_cast(DC)) { SF->addTopLevelDecl(D); } else { From 14620a34db8a92020daec4e09748e491bbfd0eca Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 6 Oct 2020 19:01:16 -0400 Subject: [PATCH 292/745] ASTScope: Remove source range sorting --- include/swift/AST/ASTScope.h | 4 ---- lib/AST/ASTScopeCreation.cpp | 31 ++++--------------------------- lib/AST/ASTScopeSourceRange.cpp | 33 +-------------------------------- 3 files changed, 5 insertions(+), 63 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 1e70eb886a34d..a1145d3f96e45 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -194,10 +194,6 @@ class ASTScopeImpl { #pragma mark - source ranges public: - /// Return signum of ranges. Centralize the invariant that ASTScopes use ends. - static int compare(SourceRange, SourceRange, const SourceManager &, - bool ensureDisjoint); - CharSourceRange getCharSourceRangeOfScope(SourceManager &SM, bool omitAssertions = false) const; bool isCharSourceRangeCached() const; diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 6dd649c11c747..1dfaf3c5eb6ab 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -359,27 +359,6 @@ class ScopeCreator final { return culled; } - /// Templated to work on either ASTNodes, Decl*'s, or whatnot. - template - std::vector - sortBySourceRange(std::vector toBeSorted) const { - auto compareNodes = [&](Rangeable n1, Rangeable n2) { - return isNotAfter(n1, n2); - }; - std::stable_sort(toBeSorted.begin(), toBeSorted.end(), compareNodes); - return toBeSorted; - } - - template - bool isNotAfter(Rangeable n1, Rangeable n2) const { - const auto r1 = getRangeableSourceRange(n1); - const auto r2 = getRangeableSourceRange(n2); - - const int signum = ASTScopeImpl::compare(r1, r2, ctx.SourceMgr, - /*ensureDisjoint=*/true); - return -1 == signum; - } - SWIFT_DEBUG_DUMP { print(llvm::errs()); } void print(raw_ostream &out) const { @@ -895,8 +874,7 @@ ASTSourceFileScope::expandAScopeThatCreatesANewInsertionPoint( std::vector newNodes(decls.begin(), decls.end()); insertionPoint = scopeCreator.addSiblingsToScopeTree(insertionPoint, - scopeCreator.sortBySourceRange( - scopeCreator.cull(newNodes)), + scopeCreator.cull(newNodes), endLoc); // Too slow to perform all the time: @@ -1024,9 +1002,8 @@ BraceStmtScope::expandAScopeThatCreatesANewInsertionPoint( // elements in source order auto *insertionPoint = scopeCreator.addSiblingsToScopeTree(this, - scopeCreator.sortBySourceRange( - scopeCreator.cull( - stmt->getElements())), + scopeCreator.cull( + stmt->getElements()), endLoc); if (auto *s = scopeCreator.getASTContext().Stats) ++s->getFrontendCounters().NumBraceStmtASTScopeExpansions; @@ -1388,7 +1365,7 @@ void GenericTypeOrExtensionScope::expandBody(ScopeCreator &) {} void IterableTypeScope::expandBody(ScopeCreator &scopeCreator) { auto nodes = asNodeVector(getIterableDeclContext().get()->getMembers()); - nodes = scopeCreator.sortBySourceRange(scopeCreator.cull(nodes)); + nodes = scopeCreator.cull(nodes); scopeCreator.addSiblingsToScopeTree(this, nodes, None); if (auto *s = scopeCreator.getASTContext().Stats) ++s->getFrontendCounters().NumIterableTypeBodyASTScopeExpansions; diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index d8a27d5b749cf..ecd6892623987 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -371,35 +371,4 @@ SourceLoc ast_scope::extractNearestSourceLoc( std::tuple scopeAndCreator) { const ASTScopeImpl *scope = std::get<0>(scopeAndCreator); return scope->getSourceRangeOfThisASTNode().Start; -} - -int ASTScopeImpl::compare(const SourceRange lhs, const SourceRange rhs, - const SourceManager &SM, const bool ensureDisjoint) { - ASTScopeAssert(!SM.isBeforeInBuffer(lhs.End, lhs.Start), - "Range is backwards."); - ASTScopeAssert(!SM.isBeforeInBuffer(rhs.End, rhs.Start), - "Range is backwards."); - - auto cmpLoc = [&](const SourceLoc lhs, const SourceLoc rhs) { - return lhs == rhs ? 0 : SM.isBeforeInBuffer(lhs, rhs) ? -1 : 1; - }; - // Establish that we use end locations throughout ASTScopes here - const int endOrder = cmpLoc(lhs.End, rhs.End); - -#ifndef NDEBUG - if (ensureDisjoint) { - const int startOrder = cmpLoc(lhs.Start, rhs.Start); - - if (startOrder * endOrder == -1) { - llvm::errs() << "*** Start order contradicts end order between: ***\n"; - lhs.print(llvm::errs(), SM, false); - llvm::errs() << "\n*** and: ***\n"; - rhs.print(llvm::errs(), SM, false); - } - ASTScopeAssert(startOrder * endOrder != -1, - "Start order contradicts end order"); - } -#endif - - return endOrder; -} +} \ No newline at end of file From 63dcd4e5605eb0944f0ecc74c38e3d0d45c48445 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 7 Oct 2020 23:52:57 -0700 Subject: [PATCH 293/745] [Concurrency] Fix parsing bug async function types and labeled parameters. --- lib/Parse/ParseType.cpp | 6 ++++-- test/Parse/async-syntax.swift | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index a1ceca394ac08..b9c8c03f6f207 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -1195,8 +1195,10 @@ ParserResult Parser::parseTypeTupleBody() { if (EllipsisLoc.isInvalid()) EllipsisIdx = ElementsR.size(); - bool isFunctionType = Tok.isAny(tok::arrow, tok::kw_throws, - tok::kw_rethrows); + bool isFunctionType = + Tok.isAny(tok::arrow, tok::kw_throws, tok::kw_rethrows) || + (shouldParseExperimentalConcurrency() && + Tok.isContextualKeyword("async")); // If there were any labels, figure out which labels should go into the type // representation. diff --git a/test/Parse/async-syntax.swift b/test/Parse/async-syntax.swift index c305fc44dd67b..e4e3fc1c59867 100644 --- a/test/Parse/async-syntax.swift +++ b/test/Parse/async-syntax.swift @@ -5,6 +5,7 @@ func asyncGlobal2() async throws { } typealias AsyncFunc1 = () async -> () typealias AsyncFunc2 = () async throws -> () +typealias AsyncFunc3 = (_ a: Bool, _ b: Bool) async throws -> () func testTypeExprs() { let _ = [() async -> ()]() From 939f547d517a2f9f6906d739dbb9da5a231be8aa Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Wed, 7 Oct 2020 20:40:12 -0700 Subject: [PATCH 294/745] [Build Script] Pass Foundation and Dispatch build directories to the swift-driver build This is required in order to build swift-driver using CMake on Linux --- .../products/swiftdriver.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/utils/swift_build_support/swift_build_support/products/swiftdriver.py b/utils/swift_build_support/swift_build_support/products/swiftdriver.py index cead28701b74b..666b316645854 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftdriver.py +++ b/utils/swift_build_support/swift_build_support/products/swiftdriver.py @@ -74,6 +74,7 @@ def install(self, host_target): def run_build_script_helper(action, host_target, product, args): + build_root = os.path.dirname(product.build_dir) script_path = os.path.join( product.source_dir, 'Utilities', 'build-script-helper.py') @@ -84,6 +85,15 @@ def run_build_script_helper(action, host_target, product, args): product.build_dir) toolchain_path = targets.toolchain_path(install_destdir, args.install_prefix) + + # Pass Dispatch directory down if we built it + dispatch_build_dir = os.path.join( + build_root, '%s-%s' % ('libdispatch', host_target)) + + # Pass Foundation directory down if we built it + foundation_build_dir = os.path.join( + build_root, '%s-%s' % ('foundation', host_target)) + is_release = product.is_release() configuration = 'release' if is_release else 'debug' helper_cmd = [ @@ -96,6 +106,14 @@ def run_build_script_helper(action, host_target, product, args): '--ninja-bin', product.toolchain.ninja, '--cmake-bin', product.toolchain.cmake, ] + if os.path.exists(dispatch_build_dir): + helper_cmd += [ + '--dispatch-build-dir', dispatch_build_dir + ] + if os.path.exists(foundation_build_dir): + helper_cmd += [ + '--foundation-build-dir', foundation_build_dir + ] if args.verbose_build: helper_cmd.append('--verbose') From 767c1a0ed6377470c2afdcd2a53281d13e88a099 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 8 Oct 2020 10:42:08 -0700 Subject: [PATCH 295/745] [ConstraintSystem] NFC: Move `SolutionResult.h` to `include/swift/Sema` --- {lib => include/swift}/Sema/SolutionResult.h | 0 lib/Sema/BuilderTransform.cpp | 2 +- lib/Sema/CSApply.cpp | 2 +- lib/Sema/CSSolver.cpp | 2 +- lib/Sema/ConstraintSystem.cpp | 2 +- lib/Sema/ConstraintSystem.h | 2 +- lib/Sema/TypeCheckConstraints.cpp | 2 +- 7 files changed, 6 insertions(+), 6 deletions(-) rename {lib => include/swift}/Sema/SolutionResult.h (100%) diff --git a/lib/Sema/SolutionResult.h b/include/swift/Sema/SolutionResult.h similarity index 100% rename from lib/Sema/SolutionResult.h rename to include/swift/Sema/SolutionResult.h diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index a5a60d07c150c..e86161a5da036 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -17,7 +17,6 @@ #include "ConstraintSystem.h" #include "MiscDiagnostics.h" -#include "SolutionResult.h" #include "TypeChecker.h" #include "TypeCheckAvailability.h" #include "swift/Sema/IDETypeChecking.h" @@ -28,6 +27,7 @@ #include "swift/AST/NameLookupRequests.h" #include "swift/AST/ParameterList.h" #include "swift/AST/TypeCheckRequests.h" +#include "swift/Sema/SolutionResult.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 18e298493e271..ba16b46156848 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -20,7 +20,6 @@ #include "CodeSynthesis.h" #include "CSDiagnostics.h" #include "MiscDiagnostics.h" -#include "SolutionResult.h" #include "TypeCheckProtocol.h" #include "TypeCheckType.h" #include "swift/AST/ASTVisitor.h" @@ -34,6 +33,7 @@ #include "swift/AST/ProtocolConformance.h" #include "swift/AST/SubstitutionMap.h" #include "swift/Basic/StringExtras.h" +#include "swift/Sema/SolutionResult.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/SmallString.h" diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 6621372fbd07f..31be05c2cecfe 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -16,11 +16,11 @@ #include "CSStep.h" #include "ConstraintGraph.h" #include "ConstraintSystem.h" -#include "SolutionResult.h" #include "TypeCheckType.h" #include "TypeChecker.h" #include "swift/AST/ParameterList.h" #include "swift/AST/TypeWalker.h" +#include "swift/Sema/SolutionResult.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 53ff93528dbea..04037f3453d6c 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -19,7 +19,6 @@ #include "ConstraintGraph.h" #include "CSDiagnostics.h" #include "CSFix.h" -#include "SolutionResult.h" #include "TypeChecker.h" #include "TypeCheckType.h" #include "swift/AST/Initializer.h" @@ -27,6 +26,7 @@ #include "swift/AST/ParameterList.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Statistic.h" +#include "swift/Sema/SolutionResult.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 9de342031ac2b..88f4f46ade755 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -24,7 +24,6 @@ #include "ConstraintGraphScope.h" #include "ConstraintLocator.h" #include "OverloadChoice.h" -#include "SolutionResult.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" #include "swift/AST/ASTVisitor.h" @@ -37,6 +36,7 @@ #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/OptionSet.h" +#include "swift/Sema/SolutionResult.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index f73bb7fb8feaa..32b116167e431 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -18,7 +18,6 @@ #include "ConstraintSystem.h" #include "MiscDiagnostics.h" -#include "SolutionResult.h" #include "TypeChecker.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" @@ -30,6 +29,7 @@ #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Statistic.h" #include "swift/Sema/CodeCompletionTypeChecking.h" +#include "swift/Sema/SolutionResult.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" From 087906a013ad278deae80c2a64dcfe7a49f41b90 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 7 Oct 2020 12:30:59 -0700 Subject: [PATCH 296/745] [ConstraintSystem] NFC: Move `OverloadChoice.h` to `include/swift/Sema` --- {lib => include/swift}/Sema/OverloadChoice.h | 0 lib/Sema/CSDiagnostics.h | 2 +- lib/Sema/CSFix.cpp | 2 +- lib/Sema/Constraint.h | 2 +- lib/Sema/ConstraintSystem.h | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename {lib => include/swift}/Sema/OverloadChoice.h (100%) diff --git a/lib/Sema/OverloadChoice.h b/include/swift/Sema/OverloadChoice.h similarity index 100% rename from lib/Sema/OverloadChoice.h rename to include/swift/Sema/OverloadChoice.h diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 6a4283e19dc51..05ed45bde9330 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -18,7 +18,6 @@ #include "Constraint.h" #include "ConstraintSystem.h" -#include "OverloadChoice.h" #include "TypeChecker.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" @@ -29,6 +28,7 @@ #include "swift/AST/OperatorNameLookup.h" #include "swift/AST/Types.h" #include "swift/Basic/SourceLoc.h" +#include "swift/Sema/OverloadChoice.h" #include "llvm/ADT/ArrayRef.h" #include diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index cb1c2c621f989..b5e731da2b996 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -20,12 +20,12 @@ #include "CSDiagnostics.h" #include "ConstraintLocator.h" #include "ConstraintSystem.h" -#include "OverloadChoice.h" #include "swift/AST/Expr.h" #include "swift/AST/ParameterList.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" #include "swift/Basic/SourceManager.h" +#include "swift/Sema/OverloadChoice.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" #include diff --git a/lib/Sema/Constraint.h b/lib/Sema/Constraint.h index 5604145a13bfb..4fbcaeb4119fe 100644 --- a/lib/Sema/Constraint.h +++ b/lib/Sema/Constraint.h @@ -19,11 +19,11 @@ #define SWIFT_SEMA_CONSTRAINT_H #include "CSFix.h" -#include "OverloadChoice.h" #include "swift/AST/FunctionRefKind.h" #include "swift/AST/Identifier.h" #include "swift/AST/Type.h" #include "swift/Basic/Debug.h" +#include "swift/Sema/OverloadChoice.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ilist.h" #include "llvm/ADT/ilist_node.h" diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 88f4f46ade755..483ad04eb0596 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -23,7 +23,6 @@ #include "ConstraintGraph.h" #include "ConstraintGraphScope.h" #include "ConstraintLocator.h" -#include "OverloadChoice.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" #include "swift/AST/ASTVisitor.h" @@ -36,6 +35,7 @@ #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/OptionSet.h" +#include "swift/Sema/OverloadChoice.h" #include "swift/Sema/SolutionResult.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/PointerUnion.h" From f2614dec4a2fecef64de73e3e6c50df5effc25d3 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 7 Oct 2020 13:05:55 -0700 Subject: [PATCH 297/745] [ConstraintSystem] NFC: Move `ConstraintLocator.h` to `include/swift/Sema` --- {lib => include/swift}/Sema/ConstraintLocator.h | 0 {lib => include/swift}/Sema/ConstraintLocatorPathElts.def | 0 lib/Sema/CSFix.cpp | 2 +- lib/Sema/CSFix.h | 2 +- lib/Sema/ConstraintLocator.cpp | 2 +- lib/Sema/ConstraintSystem.h | 2 +- 6 files changed, 4 insertions(+), 4 deletions(-) rename {lib => include/swift}/Sema/ConstraintLocator.h (100%) rename {lib => include/swift}/Sema/ConstraintLocatorPathElts.def (100%) diff --git a/lib/Sema/ConstraintLocator.h b/include/swift/Sema/ConstraintLocator.h similarity index 100% rename from lib/Sema/ConstraintLocator.h rename to include/swift/Sema/ConstraintLocator.h diff --git a/lib/Sema/ConstraintLocatorPathElts.def b/include/swift/Sema/ConstraintLocatorPathElts.def similarity index 100% rename from lib/Sema/ConstraintLocatorPathElts.def rename to include/swift/Sema/ConstraintLocatorPathElts.def diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index b5e731da2b996..75d2753b251fb 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -18,13 +18,13 @@ #include "CSFix.h" #include "CSDiagnostics.h" -#include "ConstraintLocator.h" #include "ConstraintSystem.h" #include "swift/AST/Expr.h" #include "swift/AST/ParameterList.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" #include "swift/Basic/SourceManager.h" +#include "swift/Sema/ConstraintLocator.h" #include "swift/Sema/OverloadChoice.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h index 77933617db862..80a6b7535401f 100644 --- a/lib/Sema/CSFix.h +++ b/lib/Sema/CSFix.h @@ -17,7 +17,6 @@ #ifndef SWIFT_SEMA_CSFIX_H #define SWIFT_SEMA_CSFIX_H -#include "ConstraintLocator.h" #include "swift/AST/ASTNode.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" @@ -25,6 +24,7 @@ #include "swift/AST/Type.h" #include "swift/AST/Types.h" #include "swift/Basic/Debug.h" +#include "swift/Sema/ConstraintLocator.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/TrailingObjects.h" diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index 401e48e9cfb3a..bd3fc5379aebe 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -15,11 +15,11 @@ // a particular constraint was derived. // //===----------------------------------------------------------------------===// -#include "ConstraintLocator.h" #include "ConstraintSystem.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" #include "swift/AST/Types.h" +#include "swift/Sema/ConstraintLocator.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 483ad04eb0596..10f4030939ba1 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -22,7 +22,6 @@ #include "Constraint.h" #include "ConstraintGraph.h" #include "ConstraintGraphScope.h" -#include "ConstraintLocator.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" #include "swift/AST/ASTVisitor.h" @@ -35,6 +34,7 @@ #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/OptionSet.h" +#include "swift/Sema/ConstraintLocator.h" #include "swift/Sema/OverloadChoice.h" #include "swift/Sema/SolutionResult.h" #include "llvm/ADT/MapVector.h" From ab951c208ab0f9bf8127ee1049dfd7c1174bfa2d Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 7 Oct 2020 13:15:57 -0700 Subject: [PATCH 298/745] [ConstraintSystem] NFC: Move `ConstraintGraph{Scope}.h` to `include/swift/Sema` --- {lib => include/swift}/Sema/ConstraintGraph.h | 0 {lib => include/swift}/Sema/ConstraintGraphScope.h | 0 lib/Sema/CSBindings.cpp | 2 +- lib/Sema/CSGen.cpp | 2 +- lib/Sema/CSSolver.cpp | 2 +- lib/Sema/CSStep.h | 2 +- lib/Sema/ConstraintGraph.cpp | 4 ++-- lib/Sema/ConstraintSystem.cpp | 2 +- lib/Sema/ConstraintSystem.h | 4 ++-- lib/Sema/IDETypeCheckingRequests.cpp | 1 - 10 files changed, 9 insertions(+), 10 deletions(-) rename {lib => include/swift}/Sema/ConstraintGraph.h (100%) rename {lib => include/swift}/Sema/ConstraintGraphScope.h (100%) diff --git a/lib/Sema/ConstraintGraph.h b/include/swift/Sema/ConstraintGraph.h similarity index 100% rename from lib/Sema/ConstraintGraph.h rename to include/swift/Sema/ConstraintGraph.h diff --git a/lib/Sema/ConstraintGraphScope.h b/include/swift/Sema/ConstraintGraphScope.h similarity index 100% rename from lib/Sema/ConstraintGraphScope.h rename to include/swift/Sema/ConstraintGraphScope.h diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 0c6e07e2004a5..eb1c84f89e423 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -13,9 +13,9 @@ // This file implements selection of bindings for type variables. // //===----------------------------------------------------------------------===// -#include "ConstraintGraph.h" #include "ConstraintSystem.h" #include "TypeChecker.h" +#include "swift/Sema/ConstraintGraph.h" #include "llvm/ADT/SetVector.h" #include diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index e37c5faa13bda..a7f97a6dc3c82 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -13,7 +13,6 @@ // This file implements constraint generation for the type checker. // //===----------------------------------------------------------------------===// -#include "ConstraintGraph.h" #include "ConstraintSystem.h" #include "TypeCheckType.h" #include "TypeChecker.h" @@ -25,6 +24,7 @@ #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/SubstitutionMap.h" #include "swift/AST/TypeCheckRequests.h" +#include "swift/Sema/ConstraintGraph.h" #include "swift/Sema/IDETypeChecking.h" #include "swift/Subsystems.h" #include "llvm/ADT/APInt.h" diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 31be05c2cecfe..b019adad09f09 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -14,12 +14,12 @@ // //===----------------------------------------------------------------------===// #include "CSStep.h" -#include "ConstraintGraph.h" #include "ConstraintSystem.h" #include "TypeCheckType.h" #include "TypeChecker.h" #include "swift/AST/ParameterList.h" #include "swift/AST/TypeWalker.h" +#include "swift/Sema/ConstraintGraph.h" #include "swift/Sema/SolutionResult.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" diff --git a/lib/Sema/CSStep.h b/lib/Sema/CSStep.h index 23c1f8aef642b..084dd153833ca 100644 --- a/lib/Sema/CSStep.h +++ b/lib/Sema/CSStep.h @@ -19,9 +19,9 @@ #define SWIFT_SEMA_CSSTEP_H #include "Constraint.h" -#include "ConstraintGraph.h" #include "ConstraintSystem.h" #include "swift/AST/Types.h" +#include "swift/Sema/ConstraintGraph.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" diff --git a/lib/Sema/ConstraintGraph.cpp b/lib/Sema/ConstraintGraph.cpp index 12194cb96a4e4..13f5dfdb3a7e5 100644 --- a/lib/Sema/ConstraintGraph.cpp +++ b/lib/Sema/ConstraintGraph.cpp @@ -15,10 +15,10 @@ // //===----------------------------------------------------------------------===// -#include "ConstraintGraph.h" -#include "ConstraintGraphScope.h" #include "ConstraintSystem.h" #include "swift/Basic/Statistic.h" +#include "swift/Sema/ConstraintGraph.h" +#include "swift/Sema/ConstraintGraphScope.h" #include "llvm/ADT/SetVector.h" #include "llvm/Support/Debug.h" #include "llvm/Support/SaveAndRestore.h" diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 04037f3453d6c..cae09b1404aa9 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -16,7 +16,6 @@ // //===----------------------------------------------------------------------===// #include "ConstraintSystem.h" -#include "ConstraintGraph.h" #include "CSDiagnostics.h" #include "CSFix.h" #include "TypeChecker.h" @@ -26,6 +25,7 @@ #include "swift/AST/ParameterList.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Statistic.h" +#include "swift/Sema/ConstraintGraph.h" #include "swift/Sema/SolutionResult.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 10f4030939ba1..bf2540c526072 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -20,8 +20,6 @@ #include "CSFix.h" #include "Constraint.h" -#include "ConstraintGraph.h" -#include "ConstraintGraphScope.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" #include "swift/AST/ASTVisitor.h" @@ -34,6 +32,8 @@ #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/OptionSet.h" +#include "swift/Sema/ConstraintGraph.h" +#include "swift/Sema/ConstraintGraphScope.h" #include "swift/Sema/ConstraintLocator.h" #include "swift/Sema/OverloadChoice.h" #include "swift/Sema/SolutionResult.h" diff --git a/lib/Sema/IDETypeCheckingRequests.cpp b/lib/Sema/IDETypeCheckingRequests.cpp index 739694f801205..bca5cc2e43f05 100644 --- a/lib/Sema/IDETypeCheckingRequests.cpp +++ b/lib/Sema/IDETypeCheckingRequests.cpp @@ -18,7 +18,6 @@ #include "swift/Sema/IDETypeCheckingRequests.h" #include "swift/Subsystems.h" #include "TypeChecker.h" -#include "ConstraintGraph.h" #include "ConstraintSystem.h" using namespace swift; From 4954763524b92fcac638efdee3f84233c4dbc9bf Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 7 Oct 2020 14:09:50 -0700 Subject: [PATCH 299/745] [ConstraintSystem] NFC: Move `CSFix.h` to `include/swift/Sema` --- include/swift/AST/FunctionRefKind.h | 4 +++- {lib => include/swift}/Sema/CSFix.h | 0 lib/Sema/CSFix.cpp | 2 +- lib/Sema/CSSimplify.cpp | 2 +- lib/Sema/Constraint.h | 2 +- lib/Sema/ConstraintSystem.cpp | 2 +- lib/Sema/ConstraintSystem.h | 2 +- 7 files changed, 8 insertions(+), 6 deletions(-) rename {lib => include/swift}/Sema/CSFix.h (100%) diff --git a/include/swift/AST/FunctionRefKind.h b/include/swift/AST/FunctionRefKind.h index bcde3a53b6059..50c32b16a4fe6 100644 --- a/include/swift/AST/FunctionRefKind.h +++ b/include/swift/AST/FunctionRefKind.h @@ -17,6 +17,8 @@ #ifndef SWIFT_AST_FUNCTION_REF_KIND_H #define SWIFT_AST_FUNCTION_REF_KIND_H +#include "llvm/ADT/StringRef.h" + namespace swift { /// Describes how a function is referenced within an expression node, @@ -43,7 +45,7 @@ enum class FunctionRefKind : unsigned { /// Produce a string describing a function reference kind, for /// debugging purposes. -StringRef getFunctionRefKindStr(FunctionRefKind refKind); +llvm::StringRef getFunctionRefKindStr(FunctionRefKind refKind); } diff --git a/lib/Sema/CSFix.h b/include/swift/Sema/CSFix.h similarity index 100% rename from lib/Sema/CSFix.h rename to include/swift/Sema/CSFix.h diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 75d2753b251fb..2831aa079dbd4 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -16,7 +16,6 @@ // //===----------------------------------------------------------------------===// -#include "CSFix.h" #include "CSDiagnostics.h" #include "ConstraintSystem.h" #include "swift/AST/Expr.h" @@ -25,6 +24,7 @@ #include "swift/AST/Types.h" #include "swift/Basic/SourceManager.h" #include "swift/Sema/ConstraintLocator.h" +#include "swift/Sema/CSFix.h" #include "swift/Sema/OverloadChoice.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index e0a48caa88359..27d192297c6b6 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -16,7 +16,6 @@ //===----------------------------------------------------------------------===// #include "CSDiagnostics.h" -#include "CSFix.h" #include "ConstraintSystem.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" @@ -28,6 +27,7 @@ #include "swift/AST/ProtocolConformance.h" #include "swift/Basic/StringExtras.h" #include "swift/ClangImporter/ClangModule.h" +#include "swift/Sema/CSFix.h" #include "swift/Sema/IDETypeChecking.h" #include "llvm/ADT/SetVector.h" #include "llvm/Support/Compiler.h" diff --git a/lib/Sema/Constraint.h b/lib/Sema/Constraint.h index 4fbcaeb4119fe..5eb4ab14a0f3a 100644 --- a/lib/Sema/Constraint.h +++ b/lib/Sema/Constraint.h @@ -18,7 +18,6 @@ #ifndef SWIFT_SEMA_CONSTRAINT_H #define SWIFT_SEMA_CONSTRAINT_H -#include "CSFix.h" #include "swift/AST/FunctionRefKind.h" #include "swift/AST/Identifier.h" #include "swift/AST/Type.h" @@ -43,6 +42,7 @@ class TypeVariableType; namespace constraints { +class ConstraintFix; class ConstraintLocator; class ConstraintSystem; enum class TrailingClosureMatching; diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index cae09b1404aa9..eb981e126a06d 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -17,7 +17,6 @@ //===----------------------------------------------------------------------===// #include "ConstraintSystem.h" #include "CSDiagnostics.h" -#include "CSFix.h" #include "TypeChecker.h" #include "TypeCheckType.h" #include "swift/AST/Initializer.h" @@ -25,6 +24,7 @@ #include "swift/AST/ParameterList.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Statistic.h" +#include "swift/Sema/CSFix.h" #include "swift/Sema/ConstraintGraph.h" #include "swift/Sema/SolutionResult.h" #include "llvm/ADT/SetVector.h" diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index bf2540c526072..a2eb2af082760 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -18,7 +18,6 @@ #ifndef SWIFT_SEMA_CONSTRAINT_SYSTEM_H #define SWIFT_SEMA_CONSTRAINT_SYSTEM_H -#include "CSFix.h" #include "Constraint.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" @@ -35,6 +34,7 @@ #include "swift/Sema/ConstraintGraph.h" #include "swift/Sema/ConstraintGraphScope.h" #include "swift/Sema/ConstraintLocator.h" +#include "swift/Sema/CSFix.h" #include "swift/Sema/OverloadChoice.h" #include "swift/Sema/SolutionResult.h" #include "llvm/ADT/MapVector.h" From 6ba7ecb7c2d525f1f84dace06336669e37c5556d Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 7 Oct 2020 14:31:24 -0700 Subject: [PATCH 300/745] [ConstraintSystem] NFC: Move `Constraint.h` to `include/swift/Sema` --- {lib => include/swift}/Sema/Constraint.h | 0 lib/Sema/CSDiagnostics.h | 1 - lib/Sema/CSStep.h | 2 +- lib/Sema/Constraint.cpp | 2 +- lib/Sema/ConstraintSystem.h | 2 +- 5 files changed, 3 insertions(+), 4 deletions(-) rename {lib => include/swift}/Sema/Constraint.h (100%) diff --git a/lib/Sema/Constraint.h b/include/swift/Sema/Constraint.h similarity index 100% rename from lib/Sema/Constraint.h rename to include/swift/Sema/Constraint.h diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 05ed45bde9330..b47304177534d 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -16,7 +16,6 @@ #ifndef SWIFT_SEMA_CSDIAGNOSTICS_H #define SWIFT_SEMA_CSDIAGNOSTICS_H -#include "Constraint.h" #include "ConstraintSystem.h" #include "TypeChecker.h" #include "swift/AST/ASTContext.h" diff --git a/lib/Sema/CSStep.h b/lib/Sema/CSStep.h index 084dd153833ca..2097a737cbaf1 100644 --- a/lib/Sema/CSStep.h +++ b/lib/Sema/CSStep.h @@ -18,9 +18,9 @@ #ifndef SWIFT_SEMA_CSSTEP_H #define SWIFT_SEMA_CSSTEP_H -#include "Constraint.h" #include "ConstraintSystem.h" #include "swift/AST/Types.h" +#include "swift/Sema/Constraint.h" #include "swift/Sema/ConstraintGraph.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index 894427a24e89d..bc647dfd4d389 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -15,10 +15,10 @@ // constraint that must be solved. // //===----------------------------------------------------------------------===// -#include "Constraint.h" #include "ConstraintSystem.h" #include "swift/AST/Types.h" #include "swift/Basic/Compiler.h" +#include "swift/Sema/Constraint.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" #include diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index a2eb2af082760..17f804123a6c3 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -18,7 +18,6 @@ #ifndef SWIFT_SEMA_CONSTRAINT_SYSTEM_H #define SWIFT_SEMA_CONSTRAINT_SYSTEM_H -#include "Constraint.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" #include "swift/AST/ASTVisitor.h" @@ -31,6 +30,7 @@ #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/OptionSet.h" +#include "swift/Sema/Constraint.h" #include "swift/Sema/ConstraintGraph.h" #include "swift/Sema/ConstraintGraphScope.h" #include "swift/Sema/ConstraintLocator.h" From 461eafff54b44a59ac4a1f8e1c3ee536b94ff895 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 8 Oct 2020 00:18:32 -0700 Subject: [PATCH 301/745] [ConstraintSystem] NFC: Move `ConstraintSystem.h` to `include/swift/Sema` --- {lib => include/swift}/Sema/ConstraintSolverStats.def | 0 {lib => include/swift}/Sema/ConstraintSystem.h | 0 lib/Sema/BuilderTransform.cpp | 2 +- lib/Sema/CSApply.cpp | 2 +- lib/Sema/CSBindings.cpp | 2 +- lib/Sema/CSClosure.cpp | 2 +- lib/Sema/CSDiagnostics.cpp | 1 - lib/Sema/CSDiagnostics.h | 2 +- lib/Sema/CSFix.cpp | 2 +- lib/Sema/CSGen.cpp | 2 +- lib/Sema/CSRanking.cpp | 2 +- lib/Sema/CSSimplify.cpp | 2 +- lib/Sema/CSSolver.cpp | 10 +++++----- lib/Sema/CSStep.cpp | 2 +- lib/Sema/CSStep.h | 2 +- lib/Sema/CodeSynthesis.cpp | 2 +- lib/Sema/Constraint.cpp | 2 +- lib/Sema/ConstraintGraph.cpp | 2 +- lib/Sema/ConstraintLocator.cpp | 2 +- lib/Sema/ConstraintSystem.cpp | 2 +- lib/Sema/IDETypeCheckingRequests.cpp | 2 +- lib/Sema/MiscDiagnostics.cpp | 2 +- lib/Sema/PreCheckExpr.cpp | 4 ++-- lib/Sema/TypeCheckCodeCompletion.cpp | 2 +- lib/Sema/TypeCheckConstraints.cpp | 2 +- lib/Sema/TypeCheckDeclPrimary.cpp | 1 - lib/Sema/TypeCheckExpr.cpp | 1 - lib/Sema/TypeCheckPropertyWrapper.cpp | 1 - lib/Sema/TypeCheckProtocol.cpp | 1 - lib/Sema/TypeCheckProtocol.h | 3 ++- lib/Sema/TypeCheckType.h | 1 + lib/Sema/TypeChecker.cpp | 1 - lib/Sema/TypeChecker.h | 2 +- 33 files changed, 31 insertions(+), 35 deletions(-) rename {lib => include/swift}/Sema/ConstraintSolverStats.def (100%) rename {lib => include/swift}/Sema/ConstraintSystem.h (100%) diff --git a/lib/Sema/ConstraintSolverStats.def b/include/swift/Sema/ConstraintSolverStats.def similarity index 100% rename from lib/Sema/ConstraintSolverStats.def rename to include/swift/Sema/ConstraintSolverStats.def diff --git a/lib/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h similarity index 100% rename from lib/Sema/ConstraintSystem.h rename to include/swift/Sema/ConstraintSystem.h diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index e86161a5da036..7bde9305edb86 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -15,7 +15,6 @@ // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" #include "MiscDiagnostics.h" #include "TypeChecker.h" #include "TypeCheckAvailability.h" @@ -27,6 +26,7 @@ #include "swift/AST/NameLookupRequests.h" #include "swift/AST/ParameterList.h" #include "swift/AST/TypeCheckRequests.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/SolutionResult.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index ba16b46156848..4c1441c30692d 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -16,7 +16,6 @@ // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" #include "CodeSynthesis.h" #include "CSDiagnostics.h" #include "MiscDiagnostics.h" @@ -33,6 +32,7 @@ #include "swift/AST/ProtocolConformance.h" #include "swift/AST/SubstitutionMap.h" #include "swift/Basic/StringExtras.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/SolutionResult.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index eb1c84f89e423..16afaf1834f19 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -13,9 +13,9 @@ // This file implements selection of bindings for type variables. // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" #include "TypeChecker.h" #include "swift/Sema/ConstraintGraph.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/SetVector.h" #include diff --git a/lib/Sema/CSClosure.cpp b/lib/Sema/CSClosure.cpp index 529721a921bb5..f6a26225b7923 100644 --- a/lib/Sema/CSClosure.cpp +++ b/lib/Sema/CSClosure.cpp @@ -16,8 +16,8 @@ // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" #include "TypeChecker.h" +#include "swift/Sema/ConstraintSystem.h" using namespace swift; using namespace swift::constraints; diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index afe708f9fa38a..27f2cc1487f0f 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -15,7 +15,6 @@ //===----------------------------------------------------------------------===// #include "CSDiagnostics.h" -#include "ConstraintSystem.h" #include "MiscDiagnostics.h" #include "TypeCheckProtocol.h" #include "TypoCorrection.h" diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index b47304177534d..1a2f1b4c99da1 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -16,7 +16,6 @@ #ifndef SWIFT_SEMA_CSDIAGNOSTICS_H #define SWIFT_SEMA_CSDIAGNOSTICS_H -#include "ConstraintSystem.h" #include "TypeChecker.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" @@ -27,6 +26,7 @@ #include "swift/AST/OperatorNameLookup.h" #include "swift/AST/Types.h" #include "swift/Basic/SourceLoc.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/OverloadChoice.h" #include "llvm/ADT/ArrayRef.h" #include diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 2831aa079dbd4..aba584a600148 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -17,13 +17,13 @@ //===----------------------------------------------------------------------===// #include "CSDiagnostics.h" -#include "ConstraintSystem.h" #include "swift/AST/Expr.h" #include "swift/AST/ParameterList.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" #include "swift/Basic/SourceManager.h" #include "swift/Sema/ConstraintLocator.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/CSFix.h" #include "swift/Sema/OverloadChoice.h" #include "llvm/ADT/SmallString.h" diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index a7f97a6dc3c82..a4da2d0a183f6 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -13,7 +13,6 @@ // This file implements constraint generation for the type checker. // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" #include "TypeCheckType.h" #include "TypeChecker.h" #include "swift/AST/ASTVisitor.h" @@ -25,6 +24,7 @@ #include "swift/AST/SubstitutionMap.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Sema/ConstraintGraph.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/IDETypeChecking.h" #include "swift/Subsystems.h" #include "llvm/ADT/APInt.h" diff --git a/lib/Sema/CSRanking.cpp b/lib/Sema/CSRanking.cpp index 2aaf110138b03..2e9ee713a6a6e 100644 --- a/lib/Sema/CSRanking.cpp +++ b/lib/Sema/CSRanking.cpp @@ -14,12 +14,12 @@ // constraint-based type checker. // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" #include "TypeChecker.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/TypeCheckRequests.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Compiler.h" diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 27d192297c6b6..f56558cdf3cc6 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -16,7 +16,6 @@ //===----------------------------------------------------------------------===// #include "CSDiagnostics.h" -#include "ConstraintSystem.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" @@ -27,6 +26,7 @@ #include "swift/AST/ProtocolConformance.h" #include "swift/Basic/StringExtras.h" #include "swift/ClangImporter/ClangModule.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/CSFix.h" #include "swift/Sema/IDETypeChecking.h" #include "llvm/ADT/SetVector.h" diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index b019adad09f09..789e937ceb76a 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -14,12 +14,12 @@ // //===----------------------------------------------------------------------===// #include "CSStep.h" -#include "ConstraintSystem.h" #include "TypeCheckType.h" #include "TypeChecker.h" #include "swift/AST/ParameterList.h" #include "swift/AST/TypeWalker.h" #include "swift/Sema/ConstraintGraph.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/SolutionResult.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" @@ -45,13 +45,13 @@ STATISTIC(TotalNumTypeVariables, "# of type variables created"); #define CS_STATISTIC(Name, Description) \ STATISTIC(Overall##Name, Description); -#include "ConstraintSolverStats.def" +#include "swift/Sema/ConstraintSolverStats.def" #undef DEBUG_TYPE #define DEBUG_TYPE "Constraint solver largest system" #define CS_STATISTIC(Name, Description) \ STATISTIC(Largest##Name, Description); -#include "ConstraintSolverStats.def" +#include "swift/Sema/ConstraintSolverStats.def" STATISTIC(LargestSolutionAttemptNumber, "# of the largest solution attempt"); TypeVariableType *ConstraintSystem::createTypeVariable( @@ -445,7 +445,7 @@ ConstraintSystem::SolverState::~SolverState() { // Write our local statistics back to the overall statistics. #define CS_STATISTIC(Name, Description) JOIN2(Overall,Name) += Name; - #include "ConstraintSolverStats.def" + #include "swift/Sema/ConstraintSolverStats.def" #if LLVM_ENABLE_STATS // Update the "largest" statistics if this system is larger than the @@ -457,7 +457,7 @@ ConstraintSystem::SolverState::~SolverState() { #define CS_STATISTIC(Name, Description) \ JOIN2(Largest,Name) = Name-1; \ ++JOIN2(Largest,Name); - #include "ConstraintSolverStats.def" + #include "swift/Sema/ConstraintSolverStats.def" } #endif } diff --git a/lib/Sema/CSStep.cpp b/lib/Sema/CSStep.cpp index 8717fd15d469f..26fff0c7eebab 100644 --- a/lib/Sema/CSStep.cpp +++ b/lib/Sema/CSStep.cpp @@ -16,8 +16,8 @@ //===----------------------------------------------------------------------===// #include "CSStep.h" -#include "ConstraintSystem.h" #include "swift/AST/Types.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/STLExtras.h" diff --git a/lib/Sema/CSStep.h b/lib/Sema/CSStep.h index 2097a737cbaf1..89b812ee53511 100644 --- a/lib/Sema/CSStep.h +++ b/lib/Sema/CSStep.h @@ -18,10 +18,10 @@ #ifndef SWIFT_SEMA_CSSTEP_H #define SWIFT_SEMA_CSSTEP_H -#include "ConstraintSystem.h" #include "swift/AST/Types.h" #include "swift/Sema/Constraint.h" #include "swift/Sema/ConstraintGraph.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 25e6f207b8d17..ea1c2d5eac3d7 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -16,7 +16,6 @@ #include "CodeSynthesis.h" -#include "ConstraintSystem.h" #include "TypeChecker.h" #include "TypeCheckDecl.h" #include "TypeCheckObjC.h" @@ -33,6 +32,7 @@ #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Defer.h" #include "swift/ClangImporter/ClangModule.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" using namespace swift; diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index bc647dfd4d389..a26577dfbe276 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -15,10 +15,10 @@ // constraint that must be solved. // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" #include "swift/AST/Types.h" #include "swift/Basic/Compiler.h" #include "swift/Sema/Constraint.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" #include diff --git a/lib/Sema/ConstraintGraph.cpp b/lib/Sema/ConstraintGraph.cpp index 13f5dfdb3a7e5..0d73ce5e2f095 100644 --- a/lib/Sema/ConstraintGraph.cpp +++ b/lib/Sema/ConstraintGraph.cpp @@ -15,10 +15,10 @@ // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" #include "swift/Basic/Statistic.h" #include "swift/Sema/ConstraintGraph.h" #include "swift/Sema/ConstraintGraphScope.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/SetVector.h" #include "llvm/Support/Debug.h" #include "llvm/Support/SaveAndRestore.h" diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index bd3fc5379aebe..afe82c87230de 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -15,11 +15,11 @@ // a particular constraint was derived. // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" #include "swift/AST/Types.h" #include "swift/Sema/ConstraintLocator.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index eb981e126a06d..fc9b1fd3e165d 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -15,7 +15,6 @@ // inference for expressions. // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" #include "CSDiagnostics.h" #include "TypeChecker.h" #include "TypeCheckType.h" @@ -26,6 +25,7 @@ #include "swift/Basic/Statistic.h" #include "swift/Sema/CSFix.h" #include "swift/Sema/ConstraintGraph.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/SolutionResult.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" diff --git a/lib/Sema/IDETypeCheckingRequests.cpp b/lib/Sema/IDETypeCheckingRequests.cpp index bca5cc2e43f05..5ba09dacb5706 100644 --- a/lib/Sema/IDETypeCheckingRequests.cpp +++ b/lib/Sema/IDETypeCheckingRequests.cpp @@ -15,10 +15,10 @@ #include "swift/AST/NameLookup.h" #include "swift/Basic/SourceManager.h" #include "swift/Frontend/Frontend.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/IDETypeCheckingRequests.h" #include "swift/Subsystems.h" #include "TypeChecker.h" -#include "ConstraintSystem.h" using namespace swift; diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index e032f594922b0..5660d71c0ff17 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -15,7 +15,6 @@ //===----------------------------------------------------------------------===// #include "MiscDiagnostics.h" -#include "ConstraintSystem.h" #include "TypeCheckAvailability.h" #include "TypeCheckConcurrency.h" #include "TypeChecker.h" @@ -29,6 +28,7 @@ #include "swift/Basic/StringExtras.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/Parser.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/IDETypeChecking.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringSwitch.h" diff --git a/lib/Sema/PreCheckExpr.cpp b/lib/Sema/PreCheckExpr.cpp index 32f7eb2838109..2867f7176643c 100644 --- a/lib/Sema/PreCheckExpr.cpp +++ b/lib/Sema/PreCheckExpr.cpp @@ -15,7 +15,6 @@ // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" #include "TypeChecker.h" #include "TypeCheckType.h" #include "TypoCorrection.h" @@ -31,6 +30,7 @@ #include "swift/AST/TypeCheckRequests.h" #include "swift/Parse/Confusables.h" #include "swift/Parse/Lexer.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" @@ -1899,4 +1899,4 @@ bool ConstraintSystem::preCheckExpression(Expr *&expr, DeclContext *dc, return false; } return true; -} \ No newline at end of file +} diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index 08f0c42dc3557..1649bdc7dcaf9 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -15,7 +15,6 @@ //===----------------------------------------------------------------------===// #include "swift/Subsystems.h" -#include "ConstraintSystem.h" #include "TypeChecker.h" #include "TypeCheckObjC.h" #include "TypeCheckType.h" @@ -42,6 +41,7 @@ #include "swift/Parse/Lexer.h" #include "swift/Sema/IDETypeChecking.h" #include "swift/Sema/CodeCompletionTypeChecking.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Strings.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 32b116167e431..ea471f7759455 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -16,7 +16,6 @@ // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" #include "MiscDiagnostics.h" #include "TypeChecker.h" #include "swift/AST/ASTVisitor.h" @@ -29,6 +28,7 @@ #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Statistic.h" #include "swift/Sema/CodeCompletionTypeChecking.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/SolutionResult.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallPtrSet.h" diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 9e44995acd561..e21f410849512 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -17,7 +17,6 @@ //===----------------------------------------------------------------------===// #include "CodeSynthesis.h" -#include "ConstraintSystem.h" #include "DerivedConformances.h" #include "TypeChecker.h" #include "TypeCheckAccess.h" diff --git a/lib/Sema/TypeCheckExpr.cpp b/lib/Sema/TypeCheckExpr.cpp index f2c289403cad7..8eb3a80c34bca 100644 --- a/lib/Sema/TypeCheckExpr.cpp +++ b/lib/Sema/TypeCheckExpr.cpp @@ -25,7 +25,6 @@ #include "swift/AST/SourceFile.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Parse/Lexer.h" -#include "ConstraintSystem.h" using namespace swift; diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 14f18c8a4f412..55ba6d23daec6 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -13,7 +13,6 @@ // This file implements semantic analysis for property wrappers. // //===----------------------------------------------------------------------===// -#include "ConstraintSystem.h" #include "TypeChecker.h" #include "TypeCheckType.h" #include "swift/AST/ASTContext.h" diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 89030a79f4321..06de7518176d2 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -15,7 +15,6 @@ //===----------------------------------------------------------------------===// #include "TypeCheckProtocol.h" -#include "ConstraintSystem.h" #include "DerivedConformances.h" #include "MiscDiagnostics.h" #include "TypeAccessScopeChecker.h" diff --git a/lib/Sema/TypeCheckProtocol.h b/lib/Sema/TypeCheckProtocol.h index 3a8ecf1e34693..5fec73a8b0f83 100644 --- a/lib/Sema/TypeCheckProtocol.h +++ b/lib/Sema/TypeCheckProtocol.h @@ -1,4 +1,4 @@ -//===--- ConstraintSystem.h - Constraint-based Type Checking ----*- C++ -*-===// +//===--- TypeCheckProtocol.h - Constraint-based Type Checking ----*- C++ -*-===// // // This source file is part of the Swift.org open source project // @@ -25,6 +25,7 @@ #include "swift/AST/Types.h" #include "swift/AST/Witness.h" #include "swift/Basic/Debug.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/ScopedHashTable.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" diff --git a/lib/Sema/TypeCheckType.h b/lib/Sema/TypeCheckType.h index ab6f58b1de4b6..4810dd0e344ba 100644 --- a/lib/Sema/TypeCheckType.h +++ b/lib/Sema/TypeCheckType.h @@ -17,6 +17,7 @@ #define SWIFT_SEMA_TYPE_CHECK_TYPE_H #include "swift/AST/Type.h" +#include "swift/AST/Types.h" #include "swift/AST/TypeResolutionStage.h" #include "llvm/ADT/None.h" diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 8f0fb2626f421..9d6f9b02d6753 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -16,7 +16,6 @@ //===----------------------------------------------------------------------===// #include "swift/Subsystems.h" -#include "ConstraintSystem.h" #include "TypeChecker.h" #include "TypeCheckObjC.h" #include "TypeCheckType.h" diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 9b14f3c3f5bc0..629cd1f136a20 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -17,7 +17,6 @@ #ifndef TYPECHECKING_H #define TYPECHECKING_H -#include "ConstraintSystem.h" #include "swift/AST/ASTContext.h" #include "swift/AST/AccessScope.h" #include "swift/AST/AnyFunctionRef.h" @@ -30,6 +29,7 @@ #include "swift/AST/TypeRefinementContext.h" #include "swift/Parse/Lexer.h" #include "swift/Basic/OptionSet.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Config.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/TinyPtrVector.h" From e6b140e32bc2c3af07804ce0755723f5a619863c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 8 Oct 2020 11:54:12 -0700 Subject: [PATCH 302/745] [Runtime] NFC: Fix `swift_runtime_unreachable` to `swift_unreachable` --- include/swift/Runtime/Concurrent.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/swift/Runtime/Concurrent.h b/include/swift/Runtime/Concurrent.h index 67101ecb9d4c6..c79bbb36e1b6d 100644 --- a/include/swift/Runtime/Concurrent.h +++ b/include/swift/Runtime/Concurrent.h @@ -668,7 +668,7 @@ template struct ConcurrentReadableHashMap { case sizeof(uint32_t): return (&IndexZero32)[i].load(order); default: - swift_runtime_unreachable("unknown index size"); + swift_unreachable("unknown index size"); } } @@ -683,7 +683,7 @@ template struct ConcurrentReadableHashMap { case sizeof(uint32_t): return (&IndexZero32)[i].store(value, order); default: - swift_runtime_unreachable("unknown index size"); + swift_unreachable("unknown index size"); } } }; From 630aff7b19b80f29445b645424e20fd1bb6bd551 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Wed, 7 Oct 2020 14:13:12 -0400 Subject: [PATCH 303/745] [Runtime] Change SimpleGlobalCache to use ConcurrentReadableHashMap instead of ConcurrentMap. This gives us faster lookups and a small advantage in memory usage. Most of these maps need stable addresses for their entries, so we add a level of indirection to ConcurrentReadableHashMap for these cases to accommodate that. This costs some extra memory, but it's still a net win. A new StableAddressConcurrentReadableHashMap type handles this indirection and adds a convenience getOrInsert to take advantage of it. ConcurrentReadableHashMap is tweaked to avoid any global constructors or destructors when using it as a global variable. ForeignWitnessTables does not need stable addresses and it now uses ConcurrentReadableHashMap directly. rdar://problem/70056398 --- include/swift/Runtime/Concurrent.h | 88 +++++++++++-- stdlib/public/runtime/HeapObject.cpp | 6 +- stdlib/public/runtime/Metadata.cpp | 178 +++++++++++++++++--------- stdlib/public/runtime/MetadataCache.h | 9 +- unittests/runtime/Concurrent.cpp | 14 ++ 5 files changed, 217 insertions(+), 78 deletions(-) diff --git a/include/swift/Runtime/Concurrent.h b/include/swift/Runtime/Concurrent.h index c79bbb36e1b6d..cf1b91d5b8c55 100644 --- a/include/swift/Runtime/Concurrent.h +++ b/include/swift/Runtime/Concurrent.h @@ -579,6 +579,11 @@ using llvm::hash_value; /// ensure that readers which started before the clear see valid (pre-clear) /// data. Readers which see any array as empty will produce no results, thus /// providing valid post-clear data. +/// +/// This is intended to be used for tables that exist for the life of the +/// process. It has no destructor, to avoid generating useless global destructor +/// calls. The memory it allocates can be freed by calling clear() with no +/// outstanding readers, but this won't destroy the static mutex it uses. template struct ConcurrentReadableHashMap { // We use memcpy and don't call destructors. Make sure the elements will put // up with this. @@ -724,7 +729,7 @@ template struct ConcurrentReadableHashMap { std::atomic Indices{nullptr}; /// The writer lock, which must be taken before any mutation of the table. - Mutex WriterLock; + StaticMutex WriterLock; /// The maximum number of elements that the current elements array can hold. uint32_t ElementCapacity{0}; @@ -841,20 +846,19 @@ template struct ConcurrentReadableHashMap { } public: + // Implicitly trivial constructor/destructor. + ConcurrentReadableHashMap() = default; + ~ConcurrentReadableHashMap() = default; + // This type cannot be safely copied or moved. ConcurrentReadableHashMap(const ConcurrentReadableHashMap &) = delete; ConcurrentReadableHashMap(ConcurrentReadableHashMap &&) = delete; ConcurrentReadableHashMap & operator=(const ConcurrentReadableHashMap &) = delete; - ConcurrentReadableHashMap() - : ReaderCount(0), ElementCount(0), Elements(nullptr), Indices(nullptr), - ElementCapacity(0) {} - - ~ConcurrentReadableHashMap() { - assert(ReaderCount.load(std::memory_order_acquire) == 0 && - "deallocating ConcurrentReadableHashMap with outstanding snapshots"); - FreeListNode::freeAll(&FreeList); + /// Returns whether there are outstanding readers. For testing purposes only. + bool hasActiveReaders() { + return ReaderCount.load(std::memory_order_relaxed) > 0; } /// Readers take a snapshot of the hash map, then work with the snapshot. @@ -945,7 +949,7 @@ template struct ConcurrentReadableHashMap { /// The return value is ignored when `created` is `false`. template void getOrInsert(KeyTy key, const Call &call) { - ScopedLock guard(WriterLock); + StaticScopedLock guard(WriterLock); auto *indices = Indices.load(std::memory_order_relaxed); if (!indices) @@ -955,7 +959,7 @@ template struct ConcurrentReadableHashMap { auto elementCount = ElementCount.load(std::memory_order_relaxed); auto *elements = Elements.load(std::memory_order_relaxed); - auto found = find(key, indices, elementCount, elements); + auto found = this->find(key, indices, elementCount, elements); if (found.first) { call(found.first, false); deallocateFreeListIfSafe(); @@ -996,7 +1000,7 @@ template struct ConcurrentReadableHashMap { /// Clear the hash table, freeing (when safe) all memory currently used for /// indices and elements. void clear() { - ScopedLock guard(WriterLock); + StaticScopedLock guard(WriterLock); auto *indices = Indices.load(std::memory_order_relaxed); auto *elements = Elements.load(std::memory_order_relaxed); @@ -1015,6 +1019,66 @@ template struct ConcurrentReadableHashMap { } }; +/// A wrapper type for indirect hash map elements. Stores a pointer to the real +/// element and forwards key matching and hashing. +template struct HashMapElementWrapper { + ElemTy *Ptr; + + template bool matchesKey(const KeyTy &key) { + return Ptr->matchesKey(key); + } + + friend llvm::hash_code hash_value(const HashMapElementWrapper &wrapper) { + return hash_value(*wrapper.Ptr); + } +}; + +/// A ConcurrentReadableHashMap that provides stable addresses for the elements +/// by allocating them separately and storing pointers to them. The elements of +/// the hash table are instances of HashMapElementWrapper. A new getOrInsert +/// method is provided that directly returns the stable element pointer. +template +struct StableAddressConcurrentReadableHashMap + : public ConcurrentReadableHashMap> { + // Implicitly trivial destructor. + ~StableAddressConcurrentReadableHashMap() = default; + + /// Get or insert an element for the given key and arguments. Returns the + /// pointer to the existing or new element, and a bool indicating whether the + /// element was created. When false, the element already existed before the + /// call. + template + std::pair getOrInsert(KeyTy key, ArgTys &&...args) { + // Optimize for the case where the value already exists. + if (auto wrapper = this->snapshot().find(key)) + return {wrapper->Ptr, false}; + + // No such element. Insert if needed. Note: another thread may have inserted + // it in the meantime, so we still have to handle both cases! + ElemTy *ptr = nullptr; + bool outerCreated = false; + ConcurrentReadableHashMap>::getOrInsert( + key, [&](HashMapElementWrapper *wrapper, bool created) { + if (created) { + // Created the indirect entry. Allocate the actual storage. + size_t allocSize = + sizeof(ElemTy) + ElemTy::getExtraAllocationSize(key, args...); + void *memory = Allocator().Allocate(allocSize, alignof(ElemTy)); + new (memory) ElemTy(key, std::forward(args)...); + wrapper->Ptr = reinterpret_cast(memory); + } + ptr = wrapper->Ptr; + outerCreated = created; + return true; // Keep the new entry. + }); + return {ptr, outerCreated}; + } + +private: + // Clearing would require deallocating elements, which we don't support. + void clear() = delete; +}; + } // end namespace swift #endif // SWIFT_RUNTIME_CONCURRENTUTILS_H diff --git a/stdlib/public/runtime/HeapObject.cpp b/stdlib/public/runtime/HeapObject.cpp index fc644c7a110c4..3df29f8c5ab5d 100644 --- a/stdlib/public/runtime/HeapObject.cpp +++ b/stdlib/public/runtime/HeapObject.cpp @@ -229,8 +229,10 @@ class BoxCacheEntry { return reinterpret_cast(Data.BoxedType); } - int compareWithKey(const Metadata *type) const { - return comparePointers(type, Data.BoxedType); + bool matchesKey(const Metadata *type) const { return type == Data.BoxedType; } + + friend llvm::hash_code hash_value(const BoxCacheEntry &value) { + return llvm::hash_value(value.Data.BoxedType); } static size_t getExtraAllocationSize(const Metadata *key) { diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 5fc6dafd8baac..a4c941cd0f3a3 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -998,8 +998,12 @@ namespace { return reinterpret_cast(Data.Class); } - int compareWithKey(const ClassMetadata *theClass) const { - return comparePointers(theClass, Data.Class); + bool matchesKey(const ClassMetadata *theClass) const { + return theClass == Data.Class; + } + + friend llvm::hash_code hash_value(const ObjCClassCacheEntry &value) { + return llvm::hash_value(value.Data.Class); } static size_t getExtraAllocationSize(const ClassMetadata *key) { @@ -1094,6 +1098,15 @@ class FunctionCacheEntry { auto flags = Flags.hasParameterFlags() ? ParameterFlags[index] : 0; return ParameterFlags::fromIntValue(flags); } + + friend llvm::hash_code hash_value(const Key &key) { + auto hash = llvm::hash_combine(key.Flags.getIntValue(), key.Result); + for (unsigned i = 0, e = key.getFlags().getNumParameters(); i != e; ++i) { + hash = llvm::hash_combine(hash, key.getParameter(i)); + hash = llvm::hash_combine(hash, key.getParameterFlags(i).getIntValue()); + } + return hash; + } }; FunctionCacheEntry(const Key &key); @@ -1102,28 +1115,27 @@ class FunctionCacheEntry { return 0; // No single meaningful value here. } - int compareWithKey(const Key &key) const { - auto keyFlags = key.getFlags(); - if (auto result = compareIntegers(keyFlags.getIntValue(), - Data.Flags.getIntValue())) - return result; - - if (auto result = comparePointers(key.getResult(), Data.ResultType)) - return result; - - for (unsigned i = 0, e = keyFlags.getNumParameters(); i != e; ++i) { - if (auto result = - comparePointers(key.getParameter(i), Data.getParameter(i))) - return result; - - if (auto result = - compareIntegers(key.getParameterFlags(i).getIntValue(), - Data.getParameterFlags(i).getIntValue())) - return result; + bool matchesKey(const Key &key) const { + if (key.getFlags().getIntValue() != Data.Flags.getIntValue()) + return false; + if (key.getResult() != Data.ResultType) + return false; + for (unsigned i = 0, e = key.getFlags().getNumParameters(); i != e; ++i) { + if (key.getParameter(i) != Data.getParameter(i)) + return false; + if (key.getParameterFlags(i).getIntValue() != + Data.getParameterFlags(i).getIntValue()) + return false; } + return true; + } - return 0; + friend llvm::hash_code hash_value(const FunctionCacheEntry &value) { + Key key = {value.Data.Flags, value.Data.getParameters(), + value.Data.getParameterFlags(), value.Data.ResultType}; + return hash_value(key); } + static size_t getExtraAllocationSize(const Key &key) { return getExtraAllocationSize(key.Flags); } @@ -1986,6 +1998,9 @@ namespace { bool operator==(const TypeContextIdentity &other) const { return Name == other.Name; } + friend llvm::hash_code hash_value(const TypeContextIdentity &value) { + return llvm::hash_value(value.Name); + } int compare(const TypeContextIdentity &other) const { return Name.compare(other.Name); } @@ -3261,8 +3276,12 @@ namespace { return reinterpret_cast(Data.InstanceType); } - int compareWithKey(const Metadata *instanceType) const { - return comparePointers(instanceType, Data.InstanceType); + bool matchesKey(const Metadata *instanceType) const { + return instanceType == Data.InstanceType; + } + + friend llvm::hash_code hash_value(const MetatypeCacheEntry &value) { + return llvm::hash_value(value.Data.InstanceType); } static size_t getExtraAllocationSize(const Metadata *instanceType) { @@ -3306,8 +3325,11 @@ class ExistentialMetatypeValueWitnessTableCacheEntry { return static_cast(getNumWitnessTables()); } - int compareWithKey(unsigned key) const { - return compareIntegers(key, getNumWitnessTables()); + bool matchesKey(unsigned key) const { return key == getNumWitnessTables(); } + + friend llvm::hash_code + hash_value(const ExistentialMetatypeValueWitnessTableCacheEntry &value) { + return llvm::hash_value(value.getNumWitnessTables()); } static size_t getExtraAllocationSize(unsigned numTables) { @@ -3328,8 +3350,13 @@ class ExistentialMetatypeCacheEntry { return reinterpret_cast(Data.InstanceType); } - int compareWithKey(const Metadata *instanceType) const { - return comparePointers(instanceType, Data.InstanceType); + bool matchesKey(const Metadata *instanceType) const { + return instanceType == Data.InstanceType; + } + + friend llvm::hash_code + hash_value(const ExistentialMetatypeCacheEntry &value) { + return llvm::hash_value(value.Data.InstanceType); } static size_t getExtraAllocationSize(const Metadata *key) { @@ -3442,6 +3469,14 @@ class ExistentialCacheEntry { ProtocolClassConstraint ClassConstraint : 1; uint32_t NumProtocols : 31; const ProtocolDescriptorRef *Protocols; + + friend llvm::hash_code hash_value(const Key &key) { + auto hash = llvm::hash_combine(key.SuperclassConstraint, + key.ClassConstraint, key.NumProtocols); + for (size_t i = 0; i != key.NumProtocols; i++) + hash = llvm::hash_combine(hash, key.Protocols[i].getRawData()); + return hash; + } }; ExistentialCacheEntry(Key key); @@ -3450,27 +3485,30 @@ class ExistentialCacheEntry { return 0; } - int compareWithKey(Key key) const { - if (auto result = compareIntegers(key.ClassConstraint, - Data.Flags.getClassConstraint())) - return result; + bool matchesKey(Key key) const { + if (key.ClassConstraint != Data.Flags.getClassConstraint()) + return false; - if (auto result = comparePointers(key.SuperclassConstraint, - Data.getSuperclassConstraint())) - return result; + if (key.SuperclassConstraint != Data.getSuperclassConstraint()) + return false; - if (auto result = compareIntegers(key.NumProtocols, - Data.NumProtocols)) - return result; + if (key.NumProtocols != Data.NumProtocols) + return false; auto dataProtocols = Data.getProtocols(); for (size_t i = 0; i != key.NumProtocols; ++i) { - if (auto result = compareIntegers(key.Protocols[i].getRawData(), - dataProtocols[i].getRawData())) - return result; + if (key.Protocols[i].getRawData() != dataProtocols[i].getRawData()) + return false; } - return 0; + return true; + } + + friend llvm::hash_code hash_value(const ExistentialCacheEntry &value) { + Key key = {value.Data.getSuperclassConstraint(), + value.Data.Flags.getClassConstraint(), value.Data.NumProtocols, + value.Data.getProtocols().data()}; + return hash_value(key); } static size_t getExtraAllocationSize(Key key) { @@ -3501,8 +3539,11 @@ class OpaqueExistentialValueWitnessTableCacheEntry { return getNumWitnessTables(); } - int compareWithKey(unsigned key) const { - return compareIntegers(key, getNumWitnessTables()); + bool matchesKey(unsigned key) const { return key == getNumWitnessTables(); } + + friend llvm::hash_code + hash_value(const OpaqueExistentialValueWitnessTableCacheEntry &value) { + return llvm::hash_value(value.getNumWitnessTables()); } static size_t getExtraAllocationSize(unsigned numTables) { @@ -3528,8 +3569,11 @@ class ClassExistentialValueWitnessTableCacheEntry { return getNumWitnessTables(); } - int compareWithKey(unsigned key) const { - return compareIntegers(key, getNumWitnessTables()); + bool matchesKey(unsigned key) const { return key == getNumWitnessTables(); } + + friend llvm::hash_code + hash_value(const ClassExistentialValueWitnessTableCacheEntry &value) { + return llvm::hash_value(value.getNumWitnessTables()); } static size_t getExtraAllocationSize(unsigned numTables) { @@ -4146,24 +4190,34 @@ namespace { struct Key { const TypeContextDescriptor *type; const ProtocolDescriptor *protocol; + + friend llvm::hash_code hash_value(const Key &value) { + return llvm::hash_combine(value.protocol, + TypeContextIdentity(value.type)); + } }; - const Key key; + const TypeContextDescriptor *type; + const ProtocolDescriptor *protocol; const WitnessTable *data; ForeignWitnessTableCacheEntry(const ForeignWitnessTableCacheEntry::Key k, const WitnessTable *d) - : key(k), data(d) {} + : type(k.type), protocol(k.protocol), data(d) {} intptr_t getKeyIntValueForDump() { - return reinterpret_cast(key.type); + return reinterpret_cast(type); } - int compareWithKey(const Key other) const { - if (auto r = comparePointers(other.protocol, key.protocol)) - return r; + bool matchesKey(const Key other) const { + return other.protocol == protocol && + TypeContextIdentity(other.type) == TypeContextIdentity(type); + } - return TypeContextIdentity(other.type).compare(TypeContextIdentity(key.type)); + friend llvm::hash_code + hash_value(const ForeignWitnessTableCacheEntry &value) { + Key key{value.type, value.protocol}; + return hash_value(key); } static size_t getExtraAllocationSize(const Key, @@ -4177,19 +4231,23 @@ namespace { }; } -static SimpleGlobalCache ForeignWitnessTables; +static ConcurrentReadableHashMap + ForeignWitnessTables; static const WitnessTable *_getForeignWitnessTable( const WitnessTable *witnessTableCandidate, const TypeContextDescriptor *contextDescriptor, const ProtocolDescriptor *protocol) { - auto result = - ForeignWitnessTables - .getOrInsert( - ForeignWitnessTableCacheEntry::Key{contextDescriptor, protocol}, - witnessTableCandidate) - .first->data; + const WitnessTable *result = nullptr; + ForeignWitnessTableCacheEntry::Key key{contextDescriptor, protocol}; + ForeignWitnessTables.getOrInsert( + key, [&](ForeignWitnessTableCacheEntry *entryPtr, bool created) { + if (created) + new (entryPtr) + ForeignWitnessTableCacheEntry(key, witnessTableCandidate); + result = entryPtr->data; + return true; + }); return result; } diff --git a/stdlib/public/runtime/MetadataCache.h b/stdlib/public/runtime/MetadataCache.h index 0c9a186f8b574..72fc1d665924b 100644 --- a/stdlib/public/runtime/MetadataCache.h +++ b/stdlib/public/runtime/MetadataCache.h @@ -51,16 +51,17 @@ class MetadataAllocator : public llvm::AllocatorBase { } }; -template -class TaggedMetadataAllocator: public MetadataAllocator { +template +class TaggedMetadataAllocator : public MetadataAllocator { public: constexpr TaggedMetadataAllocator() : MetadataAllocator(StaticTag) {} }; -/// A typedef for simple global caches. +/// A typedef for simple global caches with stable addresses for the entries. template using SimpleGlobalCache = - ConcurrentMap>; + StableAddressConcurrentReadableHashMap>; template class StaticOwningPointer { diff --git a/unittests/runtime/Concurrent.cpp b/unittests/runtime/Concurrent.cpp index 141b804f099d4..0739b3993ffbb 100644 --- a/unittests/runtime/Concurrent.cpp +++ b/unittests/runtime/Concurrent.cpp @@ -229,6 +229,9 @@ TEST(ConcurrentReadableHashMapTest, SingleThreaded) { check(1000000); map.clear(); check(0); + + ASSERT_FALSE(map.hasActiveReaders()); + map.clear(); } struct MultiThreadedKey { @@ -328,6 +331,9 @@ TEST(ConcurrentReadableHashMapTest, MultiThreaded) { else reader(); }); + + ASSERT_FALSE(map.hasActiveReaders()); + map.clear(); } // Test readers and writers while also constantly clearing the map. @@ -389,6 +395,9 @@ TEST(ConcurrentReadableHashMapTest, MultiThreaded2) { else clear(); }); + + ASSERT_FALSE(map.hasActiveReaders()); + map.clear(); } // Test readers and writers, with readers taking lots of snapshots. @@ -443,6 +452,9 @@ TEST(ConcurrentReadableHashMapTest, MultiThreaded3) { else reader(); }); + + ASSERT_FALSE(map.hasActiveReaders()); + map.clear(); } // Test readers and writers, with readers taking lots of snapshots, and @@ -498,4 +510,6 @@ TEST(ConcurrentReadableHashMapTest, MultiThreaded4) { else clear(); }); + ASSERT_FALSE(map.hasActiveReaders()); + map.clear(); } From 8470a6624f08283719af2cbc6fdc10a2163d4888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Sat, 3 Oct 2020 19:08:06 -0700 Subject: [PATCH 304/745] [windows] Avoid %r for quoting module-cache-path in Windows. %r returns a representation of the object that is valid Python syntax. For numbers and strings this representation is very compatible with Unix shells, but for Windows, the quoting performed by Python will not be compatible. For example the Windows path separator `\` will be escaped as `\\`, which is not necessary for Windows and might make some tests that try to match path fail. Python 3.3 has `shlex.quote`, which was previously available as `pipes.quote` in earlier Python versions. `pipes.quote` was indeed used in several points of the `lit.cfg` file. For Windows, one need to do their own quoting. This change introduces `shell_quote` which uses `shlex.quote` or `pipes.quote` in Unix depending on the Python version, and provides an implementation of `shell_quote` for Windows. It replaces every usage of `pipes.quotes` for `shell_quote`, and modifies the value of `mcp_opt` to use `shell_quote` and not `%r`. This should fix the test `Driver\working-directory.swift` in the Windows Visual Studio 2017 builder. --- test/Driver/working-directory.swift | 4 +- test/lit.cfg | 126 ++++++++++++++++++---------- 2 files changed, 84 insertions(+), 46 deletions(-) diff --git a/test/Driver/working-directory.swift b/test/Driver/working-directory.swift index 127ee028108a7..ce13e62ff6e6e 100644 --- a/test/Driver/working-directory.swift +++ b/test/Driver/working-directory.swift @@ -60,9 +60,9 @@ // -output-file-map itself // RUN: echo "{\"main.swift\": {\"object\": \"main-modified.o\"}}" > %t/ofmo2.json // RUN: touch %t/main.swift -// RUN: cd %S && %swiftc_driver -driver-print-jobs -working-directory %/t -c main.swift -output-file-map ofmo2.json | %FileCheck %s -check-prefix=OUTPUT_FILE_MAP_2 +// RUN: cd %S && %swiftc_driver -driver-print-jobs -working-directory %/t -c main.swift -output-file-map ofmo2.json | %FileCheck %s -check-prefix=OUTPUT_FILE_MAP_2 --enable-yaml-compatibility // -output-file-map= is an alias for -output-file-map -// RUN: cd %S && %swiftc_driver -driver-print-jobs -working-directory %/t -c main.swift -output-file-map=ofmo2.json | %FileCheck %s -check-prefix=OUTPUT_FILE_MAP_2 +// RUN: cd %S && %swiftc_driver -driver-print-jobs -working-directory %/t -c main.swift -output-file-map=ofmo2.json | %FileCheck %s -check-prefix=OUTPUT_FILE_MAP_2 --enable-yaml-compatibility // OUTPUT_FILE_MAP_2: BUILD_DIR{{.*}}main-modified.o // RUN: %empty-directory(%t/sub) diff --git a/test/lit.cfg b/test/lit.cfg index 53b3e05dbff63..ce463d08a81e2 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -110,6 +110,27 @@ def get_lldb_python_interpreter(lldb_build_root): return None return python_path +if not platform.system() == 'Windows': + # Python 3.3 has shlex.quote, while previous Python have pipes.quote + if sys.version_info[0:2] >= (3, 2): + shell_quote = shlex.quote + else: + shell_quote = pipes.quote +else: + # In Windows neither pipe.quote nor shlex.quote works. + def shell_quote(s): + # Quote the argument if it is empty, or contains a quote or a space. + if len(s) == 0 or re.search(r'["\s]', s): + s = '"' + s.replace('"', r'\"') + '"' + return s + +def escape_for_substitute_captures(s): + # SubstituteCaptures strings are used as replacement patterns for regular + # expressions. In them escapes like \1, \2 are used as references, but that + # means that simple \ will try to be interpreted, so we need to escape them + # with \\. + return s.replace("\\", "\\\\") + ### # Check that the object root is known. @@ -380,7 +401,7 @@ config.sil_test_options = os.environ.get('SIL_TEST_OPTIONS', '') config.clang_module_cache_path = make_path(config.swift_test_results_dir, "clang-module-cache") shutil.rmtree(config.clang_module_cache_path, ignore_errors=True) -mcp_opt = "-module-cache-path %r" % config.clang_module_cache_path +mcp_opt = "-module-cache-path %s" % shell_quote(config.clang_module_cache_path) clang_mcp_opt = "-fmodules-cache-path=%r" % config.clang_module_cache_path lit_config.note("Using Clang module cache: " + config.clang_module_cache_path) lit_config.note("Using test results dir: " + config.swift_test_results_dir) @@ -396,7 +417,7 @@ config.substitutions.append( ('%llvm_obj_root', config.llvm_obj_root) ) config.substitutions.append( ('%llvm_src_root', config.llvm_src_root) ) config.substitutions.append( ('%swift_obj_root', config.swift_obj_root) ) config.substitutions.append( ('%swift_src_root', config.swift_src_root) ) -config.substitutions.append( ('%{python}', pipes.quote(sys.executable)) ) +config.substitutions.append( ('%{python}', shell_quote(sys.executable)) ) config.substitutions.append( ('%{python.unquoted}', sys.executable) ) config.substitutions.append( ('%mcp_opt', mcp_opt) ) config.substitutions.append( ('%swift_driver_plain', "%r" % config.swift) ) @@ -1096,7 +1117,8 @@ elif run_os in ['windows-msvc']: subst_target_swift_frontend_mock_sdk_after = '' config.target_build_swift_dylib = \ - SubstituteCaptures("%s -parse-as-library -emit-library -o \\1" % (config.target_build_swift)) + SubstituteCaptures(r"%s -parse-as-library -emit-library -o \1" % ( + escape_for_substitute_captures(config.target_build_swift))) config.target_add_rpath = r'' config.target_clang = \ @@ -1280,7 +1302,7 @@ elif run_os == 'linux-androideabi' or run_os == 'linux-android': toolchain_directory = make_path( config.android_ndk_path, "toolchains", toolchain_directory_name, "prebuilt", prebuilt_directory) - tools_directory = pipes.quote(make_path( + tools_directory = shell_quote(make_path( toolchain_directory, ndk_platform_triple, "bin")) lit_config.note("Testing Android " + config.variant_triple) config.target_object_format = "elf" @@ -1290,20 +1312,20 @@ elif run_os == 'linux-androideabi' or run_os == 'linux-android': config.target_swift_autolink_extract = inferSwiftBinary("swift-autolink-extract") config.target_sdk_name = "android" android_link_paths_opt = "-L {} -L {} -L {}".format( - pipes.quote(make_path( + shell_quote(make_path( config.android_ndk_path, "sources", "cxx-stl", "llvm-libc++", "libs", ndk_platform_tuple)), - pipes.quote(make_path( + shell_quote(make_path( toolchain_directory, "lib", "gcc", ndk_platform_triple, "{}.x".format(config.android_ndk_gcc_version))), - pipes.quote(make_path( + shell_quote(make_path( toolchain_directory, ndk_platform_triple, "lib"))) # Since NDK r14 the headers are unified under $NDK_PATH/sysroot, so the -sdk # switch is not enough. Additionally we have to include both the unified # sysroot, and the architecture sysroot. - unified_android_include_path = pipes.quote(make_path( + unified_android_include_path = shell_quote(make_path( config.android_ndk_path, "sysroot", "usr", "include")) - architecture_android_include_path = pipes.quote(make_path( + architecture_android_include_path = shell_quote(make_path( config.android_ndk_path, "sysroot", "usr", "include", ndk_platform_triple)) android_include_paths_opt = "-I {} -I {}".format( @@ -1749,21 +1771,27 @@ if not kIsWindows: if not getattr(config, 'target_run_simple_swift', None): config.target_run_simple_swift_parameterized = SubstituteCaptures( - "%%empty-directory(%%t) && " - "%s %s %%s \\1 -o %%t/a.out -module-name main && " - "%s %%t/a.out && " - "%s %%t/a.out" - % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run) + r"%%empty-directory(%%t) && " + r"%s %s %%s \1 -o %%t/a.out -module-name main && " + r"%s %%t/a.out && " + r"%s %%t/a.out" + % (escape_for_substitute_captures(config.target_build_swift), + escape_for_substitute_captures(mcp_opt), + escape_for_substitute_captures(config.target_codesign), + escape_for_substitute_captures(config.target_run)) ) config.target_run_simple_swiftgyb_parameterized = SubstituteCaptures( - "%%empty-directory(%%t) && " - "%%gyb %%s -o %%t/main.swift && " - "%%line-directive %%t/main.swift -- " - "%s %s %%t/main.swift \\1 -o %%t/a.out -module-name main && " - "%s %%t/a.out && " - "%%line-directive %%t/main.swift -- " - "%s %%t/a.out" - % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run) + r"%%empty-directory(%%t) && " + r"%%gyb %%s -o %%t/main.swift && " + r"%%line-directive %%t/main.swift -- " + r"%s %s %%t/main.swift \1 -o %%t/a.out -module-name main && " + r"%s %%t/a.out && " + r"%%line-directive %%t/main.swift -- " + r"%s %%t/a.out" + % (escape_for_substitute_captures(config.target_build_swift), + escape_for_substitute_captures(mcp_opt), + escape_for_substitute_captures(config.target_codesign), + escape_for_substitute_captures(config.target_run)) ) config.target_run_simple_swift = ( @@ -1804,7 +1832,7 @@ config.target_resilience_test = ( '%s %s --target-build-swift "%s" --target-run "%s" --t %%t --S %%S ' '--s %%s --lib-prefix "%s" --lib-suffix "%s" --target-codesign "%s" ' '--additional-compile-flags "%s" --triple "%s"' - % (pipes.quote(sys.executable), config.rth, config.target_build_swift, + % (shell_quote(sys.executable), config.rth, config.target_build_swift, config.target_run, config.target_shared_library_prefix, config.target_shared_library_suffix, config.target_codesign, rth_flags, config.variant_triple)) @@ -1833,8 +1861,9 @@ config.substitutions.append(('%target-swift-emit-ir\(mock-sdk:([^)]+)\)', SubstituteCaptures(r'%target-swift-frontend(mock-sdk:\1) -emit-ir -verify-syntax-tree'))) config.substitutions.append(('%target-swift-emit-ir', '%target-swift-frontend -emit-ir -verify-syntax-tree')) config.substitutions.append(('%target-swift-frontend\(mock-sdk:([^)]+)\)', - SubstituteCaptures(r'%s \1 %s' % (subst_target_swift_frontend_mock_sdk, - subst_target_swift_frontend_mock_sdk_after)))) + SubstituteCaptures(r'%s \1 %s' % ( + escape_for_substitute_captures(subst_target_swift_frontend_mock_sdk), + escape_for_substitute_captures(subst_target_swift_frontend_mock_sdk_after))))) config.substitutions.append(('%target-swift-frontend', config.target_swift_frontend)) @@ -1872,22 +1901,23 @@ else: SubstituteCaptures(r'ln \1 \2 || cp \1 \2'))) config.substitutions.append(('%utils', config.swift_utils)) -config.substitutions.append(('%line-directive', '%s %s' % (pipes.quote(sys.executable), config.line_directive))) -config.substitutions.append(('%gyb', '%s %s' % (pipes.quote(sys.executable), config.gyb))) +config.substitutions.append(('%line-directive', '%s %s' % (shell_quote(sys.executable), config.line_directive))) +config.substitutions.append(('%gyb', '%s %s' % (shell_quote(sys.executable), config.gyb))) config.substitutions.append(('%round-trip-syntax-test', - '%s %s' % (pipes.quote(sys.executable), + '%s %s' % (shell_quote(sys.executable), config.round_trip_syntax_test))) -config.substitutions.append(('%rth', '%s %s' % (pipes.quote(sys.executable), config.rth))) +config.substitutions.append(('%rth', '%s %s' % (shell_quote(sys.executable), config.rth))) config.substitutions.append(('%scale-test', '{} {} --swiftc-binary={} --tmpdir=%t'.format( - pipes.quote(sys.executable), config.scale_test, + shell_quote(sys.executable), config.scale_test, config.swiftc))) config.substitutions.append(('%empty-directory\(([^)]+)\)', SubstituteCaptures(r'rm -rf "\1" && mkdir -p "\1"'))) config.substitutions.append(('%target-sil-opt\(mock-sdk:([^)]+)\)', - SubstituteCaptures(r'%s \1 %s' % (subst_target_sil_opt_mock_sdk, - subst_target_sil_opt_mock_sdk_after)))) + SubstituteCaptures(r'%s \1 %s' % ( + escape_for_substitute_captures(subst_target_sil_opt_mock_sdk), + escape_for_substitute_captures(subst_target_sil_opt_mock_sdk_after))))) # NOTE: This needs to be appended after the mock-sdk expansion to ensure that we # first expand the mock-sdk when lit is processing. config.substitutions.append(('%target-sil-opt', config.target_sil_opt)) @@ -1897,9 +1927,10 @@ config.substitutions.append(('%target-sil-llvm-gen', config.target_sil_llvm_gen) config.substitutions.append(('%target-sil-nm', config.target_sil_nm)) config.substitutions.append(('%target-swift-ide-test\(mock-sdk:([^)]+)\)', - SubstituteCaptures(r'%s \1 %s -swift-version %s' % (subst_target_swift_ide_test_mock_sdk, - subst_target_swift_ide_test_mock_sdk_after, - swift_version)))) + SubstituteCaptures(r'%s \1 %s -swift-version %s' % ( + escape_for_substitute_captures(subst_target_swift_ide_test_mock_sdk), + escape_for_substitute_captures(subst_target_swift_ide_test_mock_sdk_after), + escape_for_substitute_captures(swift_version))))) config.substitutions.append(('%target-swift-ide-test', "%s -swift-version %s" % (config.target_swift_ide_test, swift_version))) config.substitutions.append(('%target-swift-symbolgraph-extract', config.target_swift_symbolgraph_extract)) @@ -1944,8 +1975,9 @@ config.substitutions.append(('%target-object-format', config.target_object_forma config.substitutions.append(('%{target-shared-library-prefix}', config.target_shared_library_prefix)) config.substitutions.append(('%{target-shared-library-suffix}', config.target_shared_library_suffix)) config.substitutions.insert(0, ('%target-library-name\(([^)]+)\)', - SubstituteCaptures(r'%s\1%s' % (config.target_shared_library_prefix, - config.target_shared_library_suffix)))) + SubstituteCaptures(r'%s\1%s' % ( + escape_for_substitute_captures(config.target_shared_library_prefix), + escape_for_substitute_captures(config.target_shared_library_suffix))))) config.substitutions.append(('%target-rpath\(([^)]+)\)', SubstituteCaptures(config.target_add_rpath))) @@ -1958,14 +1990,20 @@ if hasattr(config, 'otool_classic'): config.substitutions.append(('%otool-classic', config.otool_classic)) config.substitutions.append(('%FileCheck', - '%s %r --sanitize BUILD_DIR=%r --sanitize SOURCE_DIR=%r --use-filecheck %r %s' % ( - pipes.quote(sys.executable), - config.PathSanitizingFileCheck, - swift_obj_root, - config.swift_src_root, - config.filecheck, + '%s %s --sanitize BUILD_DIR=%s --sanitize SOURCE_DIR=%s --use-filecheck %s %s' % ( + shell_quote(sys.executable), + shell_quote(config.PathSanitizingFileCheck), + # LLVM Lit performs realpath with the config path, so all paths are relative + # to the real path. swift_obj_root and swift_src_root come from CMake, which + # might not do real path. Because we have to match what Lit uses against what + # we provide we use realpath here. Because PathSanitizingFileCheck only + # understands sanitize patterns with forward slashes, and realpath normalizes + # the slashes, we have to replace them back to forward slashes. + shell_quote(os.path.realpath(swift_obj_root).replace("\\", "/")), + shell_quote(os.path.realpath(config.swift_src_root).replace("\\", "/")), + shell_quote(config.filecheck), '--enable-windows-compatibility' if kIsWindows else ''))) -config.substitutions.append(('%raw-FileCheck', pipes.quote(config.filecheck))) +config.substitutions.append(('%raw-FileCheck', shell_quote(config.filecheck))) config.substitutions.append(('%import-libdispatch', getattr(config, 'import_libdispatch', ''))) if config.lldb_build_root != "": From c5e138824544531fdf55935061afea1e999cc4b1 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 7 Oct 2020 16:45:39 -0400 Subject: [PATCH 305/745] ASTScope: Remove cull() --- lib/AST/ASTScopeCreation.cpp | 101 ++++------------------------------- 1 file changed, 11 insertions(+), 90 deletions(-) diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 1dfaf3c5eb6ab..f8fd455e97bb3 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -180,62 +180,6 @@ class ScopeCreator final { addToScopeTreeAndReturnInsertionPoint(ASTNode, ASTScopeImpl *parent, Optional endLoc); - bool isWorthTryingToCreateScopeFor(ASTNode n) const { - if (!n) - return false; - if (n.is()) - return true; - // Cannot ignore implicit statements because implict return can contain - // scopes in the expression, such as closures. - // But must ignore other implicit statements, e.g. brace statments - // if they can have no children and no stmt source range. - // Deal with it in visitBraceStmt - if (n.is()) - return true; - - auto *const d = n.get(); - // Implicit nodes may not have source information for name lookup. - if (!isLocalizable(d)) - return false; - - // Commented out for - // validation-test/compiler_crashers_fixed/27962-swift-rebindselfinconstructorexpr-getcalledconstructor.swift - // In that test the invalid PBD -> var decl which contains the desired - // closure scope - // if (const auto *PBD = dyn_cast(d)) - // if (!isLocalizable(PBD)) - // return false; - /// In - /// \code - /// @propertyWrapper - /// public struct Wrapper { - /// public var value: T - /// - /// public init(body: () -> T) { - /// self.value = body() - /// } - /// } - /// - /// let globalInt = 17 - /// - /// @Wrapper(body: { globalInt }) - /// public var y: Int - /// \endcode - /// I'm seeing a dumped AST include: - /// (pattern_binding_decl range=[test.swift:13:8 - line:12:29] - const auto &SM = d->getASTContext().SourceMgr; - - // Once we allow invalid PatternBindingDecls (see - // isWorthTryingToCreateScopeFor), then - // IDE/complete_property_delegate_attribute.swift fails because we try to - // expand a member whose source range is backwards. - (void)SM; - ASTScopeAssert(d->getStartLoc().isInvalid() || - !SM.isBeforeInBuffer(d->getEndLoc(), d->getStartLoc()), - "end-before-start will break tree search via location"); - return true; - } - template ASTScopeImpl *constructExpandAndInsert(ASTScopeImpl *parent, Args... args) { auto *child = new (ctx) Scope(args...); @@ -339,26 +283,6 @@ class ScopeCreator final { ASTScopeImpl *parent, Optional endLoc); - /// Remove VarDecls because we'll find them when we expand the - /// PatternBindingDecls. Remove EnunCases - /// because they overlap EnumElements and AST includes the elements in the - /// members. - std::vector cull(ArrayRef input) const { - // TODO: Investigate whether to move the real EndLoc tracking of - // SubscriptDecl up into AbstractStorageDecl. May have to cull more. - std::vector culled; - llvm::copy_if(input, std::back_inserter(culled), [&](ASTNode n) { - ASTScopeAssert( - !n.isDecl(DeclKind::Accessor), - "Should not find accessors in iterable types or brace statements"); - return isLocalizable(n) && - !n.isDecl(DeclKind::Var) && - !n.isDecl(DeclKind::EnumCase) && - !n.isDecl(DeclKind::IfConfig); - }); - return culled; - } - SWIFT_DEBUG_DUMP { print(llvm::errs()); } void print(raw_ostream &out) const { @@ -466,6 +390,10 @@ class NodeAdder VISIT_AND_IGNORE(PoundDiagnosticDecl) VISIT_AND_IGNORE(MissingMemberDecl) + // Only members of the active clause are in scope, and those + // are visited separately. + VISIT_AND_IGNORE(IfConfigDecl) + // This declaration is handled from the PatternBindingDecl VISIT_AND_IGNORE(VarDecl) @@ -612,14 +540,6 @@ class NodeAdder return p; } - ASTScopeImpl *visitIfConfigDecl(IfConfigDecl *icd, - ASTScopeImpl *p, - ScopeCreator &scopeCreator) { - ASTScope_unreachable( - "Should be handled inside of " - "expandIfConfigClausesThenCullAndSortElementsOrMembers"); - } - ASTScopeImpl *visitReturnStmt(ReturnStmt *rs, ASTScopeImpl *p, ScopeCreator &scopeCreator) { if (rs->hasResult()) @@ -657,9 +577,13 @@ ASTScopeImpl * ScopeCreator::addToScopeTreeAndReturnInsertionPoint(ASTNode n, ASTScopeImpl *parent, Optional endLoc) { - if (!isWorthTryingToCreateScopeFor(n)) + if (!n) return parent; + if (auto *d = n.dyn_cast()) + if (d->isImplicit()) + return parent; + NodeAdder adder(endLoc); if (auto *p = n.dyn_cast()) return adder.visit(p, parent, *this); @@ -874,8 +798,7 @@ ASTSourceFileScope::expandAScopeThatCreatesANewInsertionPoint( std::vector newNodes(decls.begin(), decls.end()); insertionPoint = scopeCreator.addSiblingsToScopeTree(insertionPoint, - scopeCreator.cull(newNodes), - endLoc); + newNodes, endLoc); // Too slow to perform all the time: // ASTScopeAssert(scopeCreator->containsAllDeclContextsFromAST(), @@ -1002,8 +925,7 @@ BraceStmtScope::expandAScopeThatCreatesANewInsertionPoint( // elements in source order auto *insertionPoint = scopeCreator.addSiblingsToScopeTree(this, - scopeCreator.cull( - stmt->getElements()), + stmt->getElements(), endLoc); if (auto *s = scopeCreator.getASTContext().Stats) ++s->getFrontendCounters().NumBraceStmtASTScopeExpansions; @@ -1365,7 +1287,6 @@ void GenericTypeOrExtensionScope::expandBody(ScopeCreator &) {} void IterableTypeScope::expandBody(ScopeCreator &scopeCreator) { auto nodes = asNodeVector(getIterableDeclContext().get()->getMembers()); - nodes = scopeCreator.cull(nodes); scopeCreator.addSiblingsToScopeTree(this, nodes, None); if (auto *s = scopeCreator.getASTContext().Stats) ++s->getFrontendCounters().NumIterableTypeBodyASTScopeExpansions; From 89ea51e90ce69e62cd169b453db2e2d4d63b6728 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 7 Oct 2020 17:11:26 -0400 Subject: [PATCH 306/745] ASTScope: Remove isLocalizable() --- lib/AST/ASTScopeCreation.cpp | 101 +++++------------------------------ 1 file changed, 12 insertions(+), 89 deletions(-) diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index f8fd455e97bb3..34cb726f9ba82 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -39,89 +39,6 @@ using namespace swift; using namespace ast_scope; -#pragma mark source range utilities -static bool rangeableIsIgnored(const Decl *d) { return d->isImplicit(); } -static bool rangeableIsIgnored(const Expr *d) { - return false; // implicit expr may contain closures -} -static bool rangeableIsIgnored(const Stmt *d) { - return false; // ?? -} -static bool rangeableIsIgnored(const ASTNode n) { - return (n.is() && rangeableIsIgnored(n.get())) || - (n.is() && rangeableIsIgnored(n.get())) || - (n.is() && rangeableIsIgnored(n.get())); -} - -template -static SourceRange getRangeableSourceRange(const Rangeable *const p) { - return p->getSourceRange(); -} -static SourceRange getRangeableSourceRange(const ASTNode n) { - return n.getSourceRange(); -} - -template -static bool isLocalizable(const Rangeable astElement) { - return !rangeableIsIgnored(astElement) && - getRangeableSourceRange(astElement).isValid(); -} - -template -static void dumpRangeable(const Rangeable r, llvm::raw_ostream &f) { - r.dump(f); -} -template -static void dumpRangeable(const Rangeable *r, llvm::raw_ostream &f) { - r->dump(f); -} -template -static void dumpRangeable(Rangeable *r, llvm::raw_ostream &f) { - r->dump(f); -} - -static void dumpRangeable(const SpecializeAttr *r, - llvm::raw_ostream &f) LLVM_ATTRIBUTE_USED; -static void dumpRangeable(const SpecializeAttr *r, llvm::raw_ostream &f) { - llvm::errs() << "SpecializeAttr\n"; -} -static void dumpRangeable(SpecializeAttr *r, - llvm::raw_ostream &f) LLVM_ATTRIBUTE_USED; -static void dumpRangeable(SpecializeAttr *r, llvm::raw_ostream &f) { - llvm::errs() << "SpecializeAttr\n"; -} - -static void dumpRangeable(const DifferentiableAttr *a, - llvm::raw_ostream &f) LLVM_ATTRIBUTE_USED; -static void dumpRangeable(const DifferentiableAttr *a, llvm::raw_ostream &f) { - llvm::errs() << "DifferentiableAttr\n"; -} -static void dumpRangeable(DifferentiableAttr *a, - llvm::raw_ostream &f) LLVM_ATTRIBUTE_USED; -static void dumpRangeable(DifferentiableAttr *a, llvm::raw_ostream &f) { - llvm::errs() << "DifferentiableAttr\n"; -} - -/// For Debugging -template -bool doesRangeableRangeMatch(const T *x, const SourceManager &SM, - unsigned start, unsigned end, - StringRef file = "") { - auto const r = getRangeableSourceRange(x); - if (r.isInvalid()) - return false; - if (start && SM.getLineAndColumnInBuffer(r.Start).first != start) - return false; - if (end && SM.getLineAndColumnInBuffer(r.End).first != end) - return false; - if (file.empty()) - return true; - const auto buf = SM.findBufferContainingLoc(r.Start); - return SM.getIdentifierForBuffer(buf).endswith(file); -} - -#pragma mark end of rangeable - static std::vector asNodeVector(DeclRange dr) { std::vector nodes; llvm::transform(dr, std::back_inserter(nodes), @@ -832,8 +749,10 @@ PatternEntryDeclScope::expandAScopeThatCreatesANewInsertionPoint( // so compute it ourselves. // Even if this predicate fails, there may be an initContext but // we cannot make a scope for it, since no source range. - if (patternEntry.getOriginalInit() && - isLocalizable(patternEntry.getOriginalInit())) { + if (patternEntry.getOriginalInit()) { + ASTScopeAssert( + patternEntry.getOriginalInit()->getSourceRange().isValid(), + "pattern initializer has invalid source range"); ASTScopeAssert( !getSourceManager().isBeforeInBuffer( patternEntry.getOriginalInit()->getStartLoc(), decl->getStartLoc()), @@ -1053,8 +972,10 @@ void SwitchStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint( scopeCreator.addToScopeTree(stmt->getSubjectExpr(), this); for (auto caseStmt : stmt->getCases()) { - if (isLocalizable(caseStmt)) - scopeCreator.constructExpandAndInsert(this, caseStmt); + ASTScopeAssert( + caseStmt->getSourceRange().isValid(), + "pattern initializer has invalid source range"); + scopeCreator.constructExpandAndInsert(this, caseStmt); } } @@ -1068,8 +989,10 @@ void ForEachStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint( // the body is implicit and it would overlap the source range of the expr // above. if (!stmt->getBody()->isImplicit()) { - if (isLocalizable(stmt->getBody())) - scopeCreator.constructExpandAndInsert(this, stmt); + ASTScopeAssert( + stmt->getBody()->getSourceRange().isValid(), + "pattern initializer has invalid source range"); + scopeCreator.constructExpandAndInsert(this, stmt); } } From ea9f84f66ee9f010cbedb2eff17e956ffe5db450 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 7 Oct 2020 17:20:22 -0400 Subject: [PATCH 307/745] ASTScope: Remove addSiblingsToScopeTree() --- include/swift/AST/ASTScope.h | 1 - lib/AST/ASTScopeCreation.cpp | 56 +++++++++++------------------------- 2 files changed, 16 insertions(+), 41 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index a1145d3f96e45..6bf8e6857440c 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -397,7 +397,6 @@ class ASTSourceFileScope final : public ASTScopeImpl { public: SourceFile *const SF; ScopeCreator *const scopeCreator; - ASTScopeImpl *insertionPoint; ASTSourceFileScope(SourceFile *SF, ScopeCreator *scopeCreator); diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 34cb726f9ba82..9a947dfd4b58c 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -39,13 +39,6 @@ using namespace swift; using namespace ast_scope; -static std::vector asNodeVector(DeclRange dr) { - std::vector nodes; - llvm::transform(dr, std::back_inserter(nodes), - [&](Decl *d) { return ASTNode(d); }); - return nodes; -} - namespace swift { namespace ast_scope { @@ -67,22 +60,6 @@ class ScopeCreator final { ScopeCreator(const ScopeCreator &) = delete; // ensure no copies ScopeCreator(const ScopeCreator &&) = delete; // ensure no moves - /// Given an array of ASTNodes or Decl pointers, add them - /// Return the resultant insertionPoint - /// - /// \param endLoc The end location for any "scopes until the end" that - /// we introduce here, such as PatternEntryDeclScope and GuardStmtScope - ASTScopeImpl * - addSiblingsToScopeTree(ASTScopeImpl *const insertionPoint, - ArrayRef nodesOrDeclsToAdd, - Optional endLoc) { - auto *ip = insertionPoint; - for (auto nd : nodesOrDeclsToAdd) { - ip = addToScopeTreeAndReturnInsertionPoint(nd, ip, endLoc); - } - return ip; - } - public: /// For each of searching, call this unless the insertion point is needed void addToScopeTree(ASTNode n, ASTScopeImpl *parent) { @@ -270,7 +247,7 @@ void ASTSourceFileScope::expandFunctionBody(AbstractFunctionDecl *AFD) { ASTSourceFileScope::ASTSourceFileScope(SourceFile *SF, ScopeCreator *scopeCreator) - : SF(SF), scopeCreator(scopeCreator), insertionPoint(this) {} + : SF(SF), scopeCreator(scopeCreator) {} #pragma mark NodeAdder @@ -708,18 +685,15 @@ AnnotatedInsertionPoint ASTSourceFileScope::expandAScopeThatCreatesANewInsertionPoint( ScopeCreator &scopeCreator) { ASTScopeAssert(SF, "Must already have a SourceFile."); - ArrayRef decls = SF->getTopLevelDecls(); SourceLoc endLoc = getSourceRangeOfThisASTNode().End; - std::vector newNodes(decls.begin(), decls.end()); - insertionPoint = - scopeCreator.addSiblingsToScopeTree(insertionPoint, - newNodes, endLoc); + ASTScopeImpl *insertionPoint = this; + for (auto *d : SF->getTopLevelDecls()) { + insertionPoint = scopeCreator.addToScopeTreeAndReturnInsertionPoint( + ASTNode(d), insertionPoint, endLoc); + } - // Too slow to perform all the time: - // ASTScopeAssert(scopeCreator->containsAllDeclContextsFromAST(), - // "ASTScope tree missed some DeclContexts or made some up"); return {insertionPoint, "Next time decls are added they go here."}; } @@ -840,14 +814,15 @@ GenericTypeOrExtensionScope::expandAScopeThatCreatesANewInsertionPoint( AnnotatedInsertionPoint BraceStmtScope::expandAScopeThatCreatesANewInsertionPoint( ScopeCreator &scopeCreator) { - // TODO: remove the sort after fixing parser to create brace statement - // elements in source order - auto *insertionPoint = - scopeCreator.addSiblingsToScopeTree(this, - stmt->getElements(), - endLoc); + ASTScopeImpl *insertionPoint = this; + for (auto nd : stmt->getElements()) { + insertionPoint = scopeCreator.addToScopeTreeAndReturnInsertionPoint( + nd, insertionPoint, endLoc); + } + if (auto *s = scopeCreator.getASTContext().Stats) ++s->getFrontendCounters().NumBraceStmtASTScopeExpansions; + return { insertionPoint, "For top-level code decls, need the scope under, say a guard statment."}; @@ -1209,8 +1184,9 @@ void FunctionBodyScope::expandBody(ScopeCreator &scopeCreator) { void GenericTypeOrExtensionScope::expandBody(ScopeCreator &) {} void IterableTypeScope::expandBody(ScopeCreator &scopeCreator) { - auto nodes = asNodeVector(getIterableDeclContext().get()->getMembers()); - scopeCreator.addSiblingsToScopeTree(this, nodes, None); + for (auto *d : getIterableDeclContext().get()->getMembers()) + scopeCreator.addToScopeTree(ASTNode(d), this); + if (auto *s = scopeCreator.getASTContext().Stats) ++s->getFrontendCounters().NumIterableTypeBodyASTScopeExpansions; } From 885f6ebba3c39fe2b097631769b2ac8451e01e82 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 7 Oct 2020 17:20:34 -0400 Subject: [PATCH 308/745] ASTScope: Rename addChildrenForAllLocalizableAccessorsInSourceOrder() --- lib/AST/ASTScopeCreation.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 9a947dfd4b58c..c7fd9f941cea2 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -159,8 +159,8 @@ class ScopeCreator final { } void - addChildrenForAllLocalizableAccessorsInSourceOrder(AbstractStorageDecl *asd, - ASTScopeImpl *parent); + addChildrenForParsedAccessors(AbstractStorageDecl *asd, + ASTScopeImpl *parent); void addChildrenForKnownAttributes(ValueDecl *decl, ASTScopeImpl *parent); @@ -339,7 +339,7 @@ class NodeAdder #undef VISIT_AND_CREATE_WHOLE_PORTION // This declaration is handled from - // addChildrenForAllLocalizableAccessorsInSourceOrder + // addChildrenForParsedAccessors ASTScopeImpl *visitAccessorDecl(AccessorDecl *ad, ASTScopeImpl *p, ScopeCreator &scopeCreator) { return visitAbstractFunctionDecl(ad, p, scopeCreator); @@ -487,7 +487,7 @@ ScopeCreator::addToScopeTreeAndReturnInsertionPoint(ASTNode n, return adder.visit(p, parent, *this); } -void ScopeCreator::addChildrenForAllLocalizableAccessorsInSourceOrder( +void ScopeCreator::addChildrenForParsedAccessors( AbstractStorageDecl *asd, ASTScopeImpl *parent) { asd->visitParsedAccessors([&](AccessorDecl *ad) { assert(asd == ad->getStorage()); @@ -738,7 +738,7 @@ PatternEntryDeclScope::expandAScopeThatCreatesANewInsertionPoint( // Add accessors for the variables in this pattern. patternEntry.getPattern()->forEachVariable([&](VarDecl *var) { - scopeCreator.addChildrenForAllLocalizableAccessorsInSourceOrder(var, this); + scopeCreator.addChildrenForParsedAccessors(var, this); }); // In local context, the PatternEntryDeclScope becomes the insertion point, so @@ -1007,7 +1007,7 @@ void SubscriptDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint( decl, decl->getGenericParams(), this); scopeCreator.constructExpandAndInsert( leaf, decl->getIndices(), decl->getAccessor(AccessorKind::Get)); - scopeCreator.addChildrenForAllLocalizableAccessorsInSourceOrder(decl, leaf); + scopeCreator.addChildrenForParsedAccessors(decl, leaf); } void CaptureListScope::expandAScopeThatDoesNotCreateANewInsertionPoint( From b720c04f4b8f03dc93a00943ca9c3048f6305d42 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 7 Oct 2020 17:29:53 -0400 Subject: [PATCH 309/745] ASTScope: Simplify a couple of getSourceRangeOfThisASTNode() methods --- include/swift/AST/ASTScope.h | 1 - lib/AST/ASTScopeSourceRange.cpp | 24 +++--------------------- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 6bf8e6857440c..4495015674112 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -797,7 +797,6 @@ class ParameterListScope final : public ASTScopeImpl { private: void expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator &); - SourceLoc fixupEndForBadInput(SourceRange) const; public: std::string getClassName() const override; diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index ecd6892623987..7cbc5ce584403 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -128,19 +128,14 @@ SourceRange AbstractStmtScope::getSourceRangeOfThisASTNode( SourceRange DefaultArgumentInitializerScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - if (auto *dv = decl->getStructuralDefaultExpr()) - return dv->getSourceRange(); - return SourceRange(); + return decl->getStructuralDefaultExpr()->getSourceRange(); } SourceRange PatternEntryDeclScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { SourceRange range = getPatternEntry().getSourceRange(); - if (endLoc.hasValue()) { - ASTScopeAssert(endLoc->isValid(), - "BraceStmt ends before pattern binding entry?"); + if (endLoc.hasValue()) range.End = *endLoc; - } return range; } @@ -244,20 +239,7 @@ SourceRange AbstractFunctionDeclScope::getSourceRangeOfThisASTNode( SourceRange ParameterListScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - auto rangeForGoodInput = params->getSourceRange(); - auto r = SourceRange(rangeForGoodInput.Start, - fixupEndForBadInput(rangeForGoodInput)); - ASTScopeAssert(getSourceManager().rangeContains( - getParent().get()->getSourceRangeOfThisASTNode(true), r), - "Parameters not within function?!"); - return r; -} - -SourceLoc ParameterListScope::fixupEndForBadInput( - const SourceRange rangeForGoodInput) const { - const auto s = rangeForGoodInput.Start; - const auto e = rangeForGoodInput.End; - return getSourceManager().isBeforeInBuffer(s, e) ? e : s; + return params->getSourceRange(); } SourceRange ForEachPatternScope::getSourceRangeOfThisASTNode( From 4b0d39c49d96c32f1ec0a1bdc8b54c9ef5c7365f Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 8 Oct 2020 16:15:21 -0400 Subject: [PATCH 310/745] AST: Clean up and write better comments for source range assertions in addMember() --- lib/AST/DeclContext.cpp | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index 2ef4cd00deb9c..8edd07131b887 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -774,8 +774,18 @@ void IterableDeclContext::addMemberPreservingSourceOrder(Decl *member) { if (existingMember->isImplicit()) continue; - if (isa(existingMember) || - isa(existingMember)) + // An EnumCaseDecl contains one or more EnumElementDecls, + // but the EnumElementDecls are also added as members of + // the parent enum. We ignore the EnumCaseDecl since its + // source range overlaps with that of the EnumElementDecls. + if (isa(existingMember)) + continue; + + // The elements of the active clause of an IfConfigDecl + // are added to the parent type. We ignore the IfConfigDecl + // since its source range overlaps with the source ranges + // of the active elements. + if (isa(existingMember)) continue; if (!SM.isBeforeInBuffer(existingMember->getEndLoc(), start)) @@ -819,14 +829,24 @@ void IterableDeclContext::addMemberSilently(Decl *member, Decl *hint, assert(!member->NextDecl && "Already added to a container"); #ifndef NDEBUG + // Assert that new declarations are always added in source order. auto checkSourceRange = [&](Decl *prev, Decl *next) { + // SKip these checks for imported and deserialized decls. if (!member->getDeclContext()->getParentSourceFile()) return; auto shouldSkip = [](Decl *d) { - if (isa(d) || isa(d) || isa(d)) + // PatternBindingDecl source ranges overlap with VarDecls, + // EnumCaseDecl source ranges overlap with EnumElementDecls, + // and IfConfigDecl source ranges overlap with the elements + // of the active clause. Skip them all here to avoid + // spurious assertions. + if (isa(d) || + isa(d) || + isa(d)) return true; + // Ignore source location information of implicit declarations. if (d->isImplicit()) return true; @@ -839,8 +859,10 @@ void IterableDeclContext::addMemberSilently(Decl *member, Decl *hint, SourceLoc prevEnd = prev->getEndLoc(); SourceLoc nextStart = next->getStartLoc(); - if (!prevEnd.isValid() || !nextStart.isValid()) - return; + assert(prevEnd.isValid() && + "Only implicit decls can have invalid source location"); + assert(nextStart.isValid() && + "Only implicit decls can have invalid source location"); if (getASTContext().SourceMgr.isBeforeInBuffer(prevEnd, nextStart)) return; From d8f46ddc3c983511d0f7f6f3972636771b2820fd Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 8 Oct 2020 17:50:55 -0400 Subject: [PATCH 311/745] Fix a ptrauth test that I broke in #34213. Still not sure how the test run didn't catch this. rdar://70104537 --- test/IRGen/ptrauth-protocols.sil | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/IRGen/ptrauth-protocols.sil b/test/IRGen/ptrauth-protocols.sil index 50a1e250c4964..eea3a5916c9e9 100644 --- a/test/IRGen/ptrauth-protocols.sil +++ b/test/IRGen/ptrauth-protocols.sil @@ -71,6 +71,7 @@ bb0: // CHECK-LABEL: define swiftcc void @test_accesses(%swift.type* %T, i8** %T.Q) // Fetch T.Assoc. // CHECK: %T.Assoc = extractvalue %swift.metadata_response [[TMP:%.*]], 0 +// CHECK-NEXT: {{%.*}} = extractvalue %swift.metadata_response [[TMP]], 1 // Fetch T.Assoc : P. // CHECK-NEXT: %T.Assoc.P = call swiftcc i8** @swift_getAssociatedConformanceWitness(i8** %T.Q, %swift.type* %T, %swift.type* %T.Assoc // Fetch T.Assoc.foo @@ -79,7 +80,10 @@ bb0: // CHECK-NEXT: [[FOO:%.*]] = bitcast i8* [[T1]] to void (%swift.type*, %swift.type*, i8**)* // CHECK-NEXT: [[T1:%.*]] = ptrtoint i8** [[T0]] to i64 // CHECK-NEXT: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T1]], i64 53700) -// CHECK-NEXT: call swiftcc void [[FOO]](%swift.type* swiftself %T.Assoc, %swift.type* %T.Assoc, i8** %T.Assoc.P) [ "ptrauth"(i32 0, i64 [[DISC]]) ] +// TODO: be smart about this and do a complete-metadata fetch in the first place +// CHECK-NEXT: [[ASSOC_RESPONSE:%.*]] = call swiftcc %swift.metadata_response @swift_checkMetadataState(i64 0, %swift.type* %T.Assoc) +// CHECK-NEXT: [[ASSOC:%.*]] = extractvalue %swift.metadata_response [[ASSOC_RESPONSE]], 0 +// CHECK-NEXT: call swiftcc void [[FOO]](%swift.type* swiftself [[ASSOC]], %swift.type* [[ASSOC]], i8** %T.Assoc.P) [ "ptrauth"(i32 0, i64 [[DISC]]) ] // CHECK-NEXT: ret void sil @use_conformances : $@convention(thin) () -> () { From ef26ecfb13754ca9d641bc2081410564ec4d0935 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 8 Oct 2020 18:59:30 -0400 Subject: [PATCH 312/745] ASTScope: Allocate list of local bindings for BraceStmt in the ASTContext Scopes need to register cleanups for non-trivial ivars in the ASTContext. Instead of doing that here, let's just change the SmallVectors into ArrayRefs. Fixes . --- include/swift/AST/ASTScope.h | 8 ++++---- lib/AST/ASTScopeCreation.cpp | 7 +++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index a1145d3f96e45..57eb515d0e39d 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -1534,11 +1534,11 @@ class BraceStmtScope final : public AbstractStmtScope { BraceStmt *const stmt; /// Declarations which are in scope from the beginning of the statement. - SmallVector localFuncsAndTypes; + ArrayRef localFuncsAndTypes; /// Declarations that are normally in scope only after their /// definition. - SmallVector localVars; + ArrayRef localVars; /// The end location for bindings introduced in this scope. This can /// extend past the actual end of the BraceStmt in top-level code, @@ -1548,8 +1548,8 @@ class BraceStmtScope final : public AbstractStmtScope { public: BraceStmtScope(BraceStmt *e, - SmallVector localFuncsAndTypes, - SmallVector localVars, + ArrayRef localFuncsAndTypes, + ArrayRef localVars, SourceLoc endLoc) : stmt(e), localFuncsAndTypes(localFuncsAndTypes), diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 1dfaf3c5eb6ab..56d304fb82e28 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -588,12 +588,15 @@ class NodeAdder if (endLoc.hasValue()) endLocForBraceStmt = *endLoc; - if (auto *s = scopeCreator.getASTContext().Stats) + ASTContext &ctx = scopeCreator.getASTContext(); + if (auto *s = ctx.Stats) ++s->getFrontendCounters().NumBraceStmtASTScopes; return scopeCreator.constructExpandAndInsert( - p, bs, std::move(localFuncsAndTypes), std::move(localVars), + p, bs, + ctx.AllocateCopy(localFuncsAndTypes), + ctx.AllocateCopy(localVars), endLocForBraceStmt); } From 9bcec37db326bda80d698cc7b74f550c10a3d0ae Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 8 Oct 2020 21:53:13 -0400 Subject: [PATCH 313/745] Add testcase for rdar://problem/17503169 / https://bugs.swift.org/browse/SR-10069 This now works thanks to ASTScope lookup. Also add a release note. --- CHANGELOG.md | 15 +++++ test/decl/func/local-function-overload.swift | 63 ++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 test/decl/func/local-function-overload.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 569a2069a9dda..a97a6a59746ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,20 @@ CHANGELOG Swift Next ---------- +* [SR-10069][]: + + Function overloading now works in local contexts, making the following valid: + + ```swift + func outer(x: Int, y: String) { + func doIt(_: Int) {} + func doIt(_: String) {} + + doIt(x) // calls the first 'doIt(_:)' with an Int value + doIt(y) // calls the second 'doIt(_:)' with a String value + } + ``` + * [SE-0284][]: Functions, subscripts, and initializers may now have more than one variadic parameter, as long as all parameters which follow variadic parameters are labeled. This makes declarations like the following valid: @@ -8196,6 +8210,7 @@ Swift 1.0 [SR-8974]: [SR-9043]: [SR-9827]: +[SR-10069]: [SR-11298]: [SR-11429]: [SR-11700]: diff --git a/test/decl/func/local-function-overload.swift b/test/decl/func/local-function-overload.swift new file mode 100644 index 0000000000000..d5282632d8ed0 --- /dev/null +++ b/test/decl/func/local-function-overload.swift @@ -0,0 +1,63 @@ +// RUN: %target-typecheck-verify-swift -disable-parser-lookup + +func valid1() { + func inner(_: Int) {} + func inner(_: String) {} + + func inner(label: Int) {} + func inner(label: String) {} + + inner(123) + inner("hello") + + inner(label: 123) + inner(label: "hello") +} + +func valid2() { + func inner(_: Int = 0) {} + func inner() -> Bool {} + func inner(first: Int, second: Int = 0) {} + + let _: Bool = inner() + let _ = inner() + + inner(first: 123) +} + +func invalid1() { + func inner(_: Int) {} + // expected-note@-1 {{'inner' previously declared here}} + func inner(_: Int) {} + // expected-error@-1 {{invalid redeclaration of 'inner'}} +} + +func invalid2() { + func inner(_: Int) {} + // expected-note@-1 {{candidate expects value of type 'Int' for parameter #1}} + // expected-note@-2 {{found this candidate}} + // expected-note@-3 {{did you mean 'inner'?}} + func inner(_: String) {} + // expected-note@-1 {{candidate expects value of type 'String' for parameter #1}} + // expected-note@-2 {{found this candidate}} + // expected-note@-3 {{did you mean 'inner'?}} + + func inner(label: Int) {} + // expected-note@-1 {{found this candidate}} + // expected-note@-2 {{did you mean 'inner'?}} + + inner([]) + // expected-error@-1 {{no exact matches in call to local function 'inner'}} + + inner(label: "hi") + // expected-error@-1 {{cannot convert value of type 'String' to expected argument type 'Int'}} + + _ = inner + // expected-error@-1 {{ambiguous use of 'inner'}} + + _ = inner(label:) // no-error + + // FIXME: This isn't as good as in the non-local function case? + _ = inner(invalidLabel:) + // expected-error@-1 {{cannot find 'inner(invalidLabel:)' in scope}} +} From be231208f36e91d9b20d8bc52fb8d5012ce1277b Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Thu, 8 Oct 2020 14:06:17 -0700 Subject: [PATCH 314/745] [CodeCompletion][CSGen] Treat ErrorExprs as holes when generating constraints for code completion This lets us still provide member completions when the base expression contains parse errors or unresolved decls e.g. returnsAString(undefined). --- include/swift/AST/Types.h | 4 +++- lib/AST/ASTDumper.cpp | 2 ++ lib/AST/ASTPrinter.cpp | 2 ++ lib/Sema/CSGen.cpp | 17 +++++++++++++++-- lib/Sema/TypeCheckCodeCompletion.cpp | 1 + test/IDE/complete_ambiguous.swift | 6 ++++++ test/IDE/complete_assignment.swift | 2 +- test/IDE/complete_func_body_typechecking.swift | 6 +----- 8 files changed, 31 insertions(+), 9 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index c58a598ec96b9..958108b4a712c 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -87,6 +87,7 @@ class ModuleType; class ProtocolConformance; enum PointerTypeKind : unsigned; struct ValueOwnershipKind; +class ErrorExpr; typedef CanTypeWrapper CanSILFunctionType; @@ -5738,7 +5739,8 @@ DEFINE_EMPTY_CAN_TYPE_WRAPPER(TypeVariableType, Type) /// constraint solver and transformed into UnresolvedType to be used in AST. class HoleType : public TypeBase { using Originator = llvm::PointerUnion; + DependentMemberType *, VarDecl *, + ErrorExpr *>; Originator O; diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 5ca3bedb7794c..aff01f211fce0 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -3523,6 +3523,8 @@ namespace { printRec("type_variable", typeVar); } else if (auto *VD = originator.dyn_cast()) { VD->dumpRef(PrintWithColorRAII(OS, DeclColor).getOS()); + } else if (auto *EE = originator.dyn_cast()) { + printFlag("error_expr"); } else { printRec("dependent_member_type", originator.get()); diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index e07febf037f29..f8a7301f24706 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3828,6 +3828,8 @@ class TypePrinter : public TypeVisitor { } else if (auto *VD = originator.dyn_cast()) { Printer << "decl = "; Printer << VD->getName(); + } else if (auto *EE = originator.dyn_cast()) { + Printer << "error_expr"; } else { visit(originator.get()); } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index e37c5faa13bda..ec43f025e86fd 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1003,8 +1003,21 @@ namespace { ConstraintSystem &getConstraintSystem() const { return CS; } virtual Type visitErrorExpr(ErrorExpr *E) { - // FIXME: Can we do anything with error expressions at this point? - return nullptr; + if (!CS.isForCodeCompletion()) + return nullptr; + + // For code completion, treat error expressions that don't contain + // the completion location itself as holes. If an ErrorExpr contains the + // code completion location, a fallback typecheck is called on the + // ErrorExpr's OriginalExpr (valid sub-expression) if it had one, + // independent of the wider expression containing the ErrorExpr, so + // there's no point attempting to produce a solution for it. + SourceRange range = E->getSourceRange(); + if (range.isInvalid() || + CS.getASTContext().SourceMgr.rangeContainsCodeCompletionLoc(range)) + return nullptr; + + return HoleType::get(CS.getASTContext(), E); } virtual Type visitCodeCompletionExpr(CodeCompletionExpr *E) { diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index 08f0c42dc3557..acce2500c70b2 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -786,6 +786,7 @@ bool TypeChecker::typeCheckForCodeCompletion( ConstraintSystemOptions options; options |= ConstraintSystemFlags::AllowFixes; options |= ConstraintSystemFlags::SuppressDiagnostics; + options |= ConstraintSystemFlags::ForCodeCompletion; ConstraintSystem cs(DC, options); diff --git a/test/IDE/complete_ambiguous.swift b/test/IDE/complete_ambiguous.swift index b386ffada7fcd..3f6d9a8990e50 100644 --- a/test/IDE/complete_ambiguous.swift +++ b/test/IDE/complete_ambiguous.swift @@ -4,6 +4,7 @@ // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=RELATED | %FileCheck %s --check-prefix=RELATED // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=RELATED_EXTRAARG | %FileCheck %s --check-prefix=RELATED // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=RELATED_INERROREXPR | %FileCheck %s --check-prefix=RELATED +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=ERROR_IN_BASE | %FileCheck %s --check-prefix=SIMPLE // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC | %FileCheck %s --check-prefix=GENERIC // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_MISSINGARG | %FileCheck %s --check-prefix=NORESULTS // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=CLOSURE_MISSINGARG | %FileCheck %s --check-prefix=POINT_MEMBER @@ -34,6 +35,11 @@ struct HasMembers { HasMembers().overloadedReturn().#^SIMPLE_MEMBERS^# +func givenErrorExpr(_ a: String) -> A {} +func givenErrorExpr(_ b: Int) -> B {} + +givenErrorExpr(undefined).#^ERROR_IN_BASE^# + // SIMPLE: Begin completions, 4 items // SIMPLE-DAG: Keyword[self]/CurrNominal: self[#A#]{{; name=.+$}} // SIMPLE-DAG: Decl[InstanceMethod]/CurrNominal: doAThings()[#A#]{{; name=.+$}} diff --git a/test/IDE/complete_assignment.swift b/test/IDE/complete_assignment.swift index 871ef3db1dd2c..2e3b33bab2680 100644 --- a/test/IDE/complete_assignment.swift +++ b/test/IDE/complete_assignment.swift @@ -202,7 +202,7 @@ func f2() { // ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] // ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] // ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] -// ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: VoidGen()[#Void#] +// ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: VoidGen()[#Void#] // ASSIGN_11-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f12() { diff --git a/test/IDE/complete_func_body_typechecking.swift b/test/IDE/complete_func_body_typechecking.swift index 8856a9dfb9a3c..d0176c3525a57 100644 --- a/test/IDE/complete_func_body_typechecking.swift +++ b/test/IDE/complete_func_body_typechecking.swift @@ -4,7 +4,7 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TC_VAR_4 | %FileCheck %s -check-prefix=FOO_STRUCT_COMMON // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TC_VAR_5 | %FileCheck %s -check-prefix=FOO_STRUCT_COMMON // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TC_VAR_6 | %FileCheck %s -check-prefix=FOO_STRUCT_COMMON -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TC_VAR_7 | %FileCheck %s -check-prefix=ERROR_COMMON +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TC_VAR_7 | %FileCheck %s -check-prefix=FOO_STRUCT_COMMON // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TC_VAR_IN_CONSTRUCTOR_1 | %FileCheck %s -check-prefix=FOO_STRUCT_COMMON // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TC_VAR_IN_CONSTRUCTOR_2 | %FileCheck %s -check-prefix=FOO_STRUCT_COMMON @@ -91,10 +91,6 @@ func testTypecheckVar6() { } func testTypecheckVar7() { - // FIXME: We don't display any useful completions here, although we could -- - // it is obvious that the expression could only have type 'FooStruct'. - // - // In any case, ensure that we don't crash. var localInt = 42 FooStruct(localInt).builderFunc2(unknown_var).#^TC_VAR_7^# } From 771738c1eb7f55196ad4443eaa24abce85de11f2 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 9 Oct 2020 03:02:59 -0700 Subject: [PATCH 315/745] [utils/build-parser-lib] Add '--no-install' option to skip installing. --- utils/build-parser-lib | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/utils/build-parser-lib b/utils/build-parser-lib index de9da5609199a..f32b6f50b76b0 100755 --- a/utils/build-parser-lib +++ b/utils/build-parser-lib @@ -74,6 +74,7 @@ class Builder(object): shell.call(command, env=env, dry_run=self.dry_run, echo=self.verbose) def configure(self, enable_debuginfo, instrumentation=None, profile_data=None): + environment = {} cmake_args = [self.toolchain.cmake, "-G", "Ninja"] cmake_args += ["-DCMAKE_MAKE_PROGRAM=" + self.ninja_path] @@ -90,6 +91,7 @@ class Builder(object): "-DCMAKE_OSX_DEPLOYMENT_TARGET=" + deployment_version, "-DSWIFT_DARWIN_DEPLOYMENT_VERSION_OSX=" + deployment_version, ] + environment["SDKROOT"] = "macosx" elif self.host == "linux": host_triple = "%s-unknown-linux" % (self.arch) @@ -252,7 +254,7 @@ class Builder(object): "-DSWIFT_INCLUDE_TESTS=FALSE", ] cmake_args += [os.path.join(SWIFT_SOURCE_ROOT, "llvm-project", "llvm")] - self.call(cmake_args) + self.call(cmake_args, env=environment) def build_target(self, build_dir, target, env=None): invocation = [self.toolchain.cmake, "--build", build_dir] @@ -441,6 +443,7 @@ Example invocations: help="space-separated list of architectures to build for. (default = %s)" % default_architectures, ) + option("--no-install", store_true, help="disable install step") option( "--install-symroot", store_path, help="the path to install debug symbols into" ) @@ -467,7 +470,7 @@ Example invocations: parser = optbuilder.build() args = parser.parse_args() - if not args.install_destdir: + if not args.install_destdir and not args.no_install: args.install_destdir = os.path.join(args.build_dir, "install") swift_src_path = os.path.join(SWIFT_SOURCE_ROOT, "swift") @@ -503,8 +506,9 @@ Example invocations: arch = architectures.pop(0) tmpargs = copy.copy(args) tmpargs.build_dir = os.path.join(objroot, arch, "obj") - tmpargs.install_destdir = os.path.join(objroot, arch, "dst") - tmpargs.install_prefix = "/" + if not args.no_install: + tmpargs.install_destdir = os.path.join(objroot, arch, "dst") + tmpargs.install_prefix = "/" native_build_dir = tmpargs.build_dir dst_dirs.append(tmpargs.install_destdir) @@ -544,8 +548,9 @@ Example invocations: for arch in architectures: args.build_dir = os.path.join(objroot, arch, "obj") - args.install_destdir = os.path.join(objroot, arch, "dst") - args.install_prefix = "/" + if not args.no_install: + args.install_destdir = os.path.join(objroot, arch, "dst") + args.install_prefix = "/" dst_dirs.append(args.install_destdir) @@ -559,11 +564,12 @@ Example invocations: ) builder.run() - lipo = os.path.join(SWIFT_SOURCE_ROOT, "swift", "utils", "recursive-lipo") - shell.call( - [lipo, "-v", "--destination", os.path.join(dstroot, "./" + prefix)] - + dst_dirs - ) + if not args.no_install: + lipo = os.path.join(SWIFT_SOURCE_ROOT, "swift", "utils", "recursive-lipo") + shell.call( + [lipo, "-v", "--destination", os.path.join(dstroot, "./" + prefix)] + + dst_dirs + ) if args.install_symroot: extract_symbols(dstroot, prefix, symroot, args.build_jobs) From 98903b7cd2671e9147002b46b391a97885551202 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sun, 4 Oct 2020 21:17:34 -0700 Subject: [PATCH 316/745] [Concurrency] Add globalActor attribute. The globalActor attribute indicates that a particular type describes a global actor. Global actors allow the notion of actor state isolation to be spread across various declarations throughout a program, rather than being centered around a single actor class. There are useful primarily for existing notions such as "main thread" or subsystems accessed through global/singleton state. --- include/swift/AST/Attr.def | 6 + include/swift/AST/Decl.h | 14 +++ include/swift/AST/DiagnosticsSema.def | 16 +++ include/swift/AST/KnownIdentifiers.def | 1 + include/swift/AST/TypeCheckRequests.h | 22 ++++ include/swift/AST/TypeCheckerTypeIDZone.def | 3 + lib/AST/Decl.cpp | 7 ++ lib/Sema/TypeCheckAttr.cpp | 8 ++ lib/Sema/TypeCheckConcurrency.cpp | 123 ++++++++++++++++++++ lib/Sema/TypeCheckDeclOverride.cpp | 1 + test/attr/global_actor.swift | 45 +++++++ 11 files changed, 246 insertions(+) create mode 100644 test/attr/global_actor.swift diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index bd0af44538512..5f2c0a39d92a3 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -578,6 +578,12 @@ SIMPLE_DECL_ATTR(actorIndependent, ActorIndependent, APIStableToAdd | APIBreakingToRemove, 103) +SIMPLE_DECL_ATTR(globalActor, GlobalActor, + OnClass | OnStruct | OnEnum | ConcurrencyOnly | + ABIStableToAdd | ABIBreakingToRemove | + APIStableToAdd | APIBreakingToRemove, + 104) + #undef TYPE_ATTR #undef DECL_ATTR_ALIAS #undef CONTEXTUAL_DECL_ATTR_ALIAS diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 8e899934bc1b4..96b48ab14ddd8 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3172,6 +3172,20 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { void synthesizeSemanticMembersIfNeeded(DeclName member); + /// Retrieves the static 'shared' property of a global actor type, which + /// is used to extract the actor instance. + /// + /// \returns the static 'shared' property for a global actor, or \c nullptr + /// for types that are not global actors. + VarDecl *getGlobalActorInstance() const; + + /// Whether this type is a global actor, which can be used as an + /// attribute to decorate declarations for inclusion in the actor-isolated + /// state denoted by this type. + bool isGlobalActor() const { + return getGlobalActorInstance() != nullptr; + } + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return D->getKind() >= DeclKind::First_NominalTypeDecl && diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index ebdd9bd5caeb0..ebf53e1309862 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4205,6 +4205,22 @@ ERROR(enqueue_partial_task_not_in_context,none, "'enqueue(partialTask:)' can only be implemented in the definition of " "actor class %0", (Type)) +ERROR(global_actor_missing_shared,none, + "global actor %0 requires a static property 'shared' that produces an " + "actor instance", (Identifier)) +NOTE(global_actor_shared_not_static,none, + "'shared' property in global actor is not 'static'", ()) +NOTE(global_actor_shared_inaccessible,none, + "'shared' property has more restrictive access (%0) than its global actor " + "(%1)", + (StringRef, StringRef)) +NOTE(global_actor_shared_constrained_extension,none, + "'shared' property in global actor cannot be in a constrained extension", + ()) +NOTE(global_actor_shared_non_actor_type,none, + "'shared' property type %0 does not conform to the 'Actor' protocol", + (Type)) + //------------------------------------------------------------------------------ // MARK: Type Check Types //------------------------------------------------------------------------------ diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index a9c4d53248615..261683cb7bf43 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -119,6 +119,7 @@ IDENTIFIER(Selector) IDENTIFIER(self) IDENTIFIER(Self) IDENTIFIER(setObject) +IDENTIFIER(shared) IDENTIFIER(simd) IDENTIFIER(storage) IDENTIFIER(stringValue) diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 9546b7dfab229..149ea026d72cb 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -858,6 +858,28 @@ class IsActorRequest : bool isCached() const { return true; } }; +/// Retrieve the static "shared" property within a global actor that provides +/// the actor instance representing the global actor. +/// +/// Global actors can be applied to a declaration to indicate that the +/// declaration operations on state that is protected by the global actor. +class GlobalActorInstanceRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + VarDecl *evaluate(Evaluator &evaluator, NominalTypeDecl *nominal) const; + +public: + // Caching + bool isCached() const { return true; } +}; + /// Determine the actor isolation for the given declaration. class ActorIsolationRequest : public SimpleRequest(this); + return evaluateOrDefault(getASTContext().evaluator, + GlobalActorInstanceRequest{mutableThis}, + nullptr); +} + ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc, MutableArrayRef Inherited, GenericParamList *GenericParams, DeclContext *Parent) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index ff1db56c28832..eceb6dc657fac 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -323,6 +323,14 @@ class AttributeChecker : public AttributeVisitor { return; } } + + void visitGlobalActorAttr(GlobalActorAttr *attr) { + auto nominal = dyn_cast(D); + if (!nominal) + return; // already diagnosed + + (void)nominal->isGlobalActor(); + } }; } // end anonymous namespace diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index e40e5656d8fc5..51872cee74bef 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -204,6 +204,129 @@ bool IsActorRequest::evaluate( return actorAttr != nullptr; } +static bool isDeclNotAsAccessibleAsParent(ValueDecl *decl, + NominalTypeDecl *parent) { + return decl->getFormalAccess() < + std::min(parent->getFormalAccess(), AccessLevel::Public); +} + +VarDecl *GlobalActorInstanceRequest::evaluate( + Evaluator &evaluator, NominalTypeDecl *nominal) const { + auto globalActorAttr = nominal->getAttrs().getAttribute(); + if (!globalActorAttr) + return nullptr; + + // Ensure that the actor protocol has been loaded. + ASTContext &ctx = nominal->getASTContext(); + auto actorProto = ctx.getProtocol(KnownProtocolKind::Actor); + if (!actorProto) { + nominal->diagnose(diag::concurrency_lib_missing, "Actor"); + return nullptr; + } + + // Global actors have a static property "shared" that provides an actor + // instance. The value must + SmallVector decls; + nominal->lookupQualified( + nominal, DeclNameRef(ctx.Id_shared), NL_QualifiedDefault, decls); + VarDecl *sharedVar = nullptr; + llvm::TinyPtrVector candidates; + for (auto decl : decls) { + auto var = dyn_cast(decl); + if (!var) + continue; + + auto varDC = var->getDeclContext(); + if (var->isStatic() && + !isDeclNotAsAccessibleAsParent(var, nominal) && + !(isa(varDC) && + cast(varDC)->isConstrainedExtension()) && + TypeChecker::conformsToProtocol( + varDC->mapTypeIntoContext(var->getValueInterfaceType()), + actorProto, nominal)) { + sharedVar = var; + break; + } + + candidates.push_back(var); + } + + // If we found a suitable candidate, we're done. + if (sharedVar) + return sharedVar; + + // Complain about the lack of a suitable 'shared' property. + { + auto primaryDiag = nominal->diagnose( + diag::global_actor_missing_shared, nominal->getName()); + + // If there were no candidates, provide a Fix-It with a prototype. + if (candidates.empty() && nominal->getBraces().Start.isValid()) { + // Figure out the indentation we need. + SourceLoc sharedInsertionLoc = Lexer::getLocForEndOfToken( + ctx.SourceMgr, nominal->getBraces().Start); + + StringRef extraIndent; + StringRef currentIndent = Lexer::getIndentationForLine( + ctx.SourceMgr, sharedInsertionLoc, &extraIndent); + std::string stubIndent = (currentIndent + extraIndent).str(); + + // From the string to add the declaration. + std::string sharedDeclString = "\n" + stubIndent; + if (nominal->getFormalAccess() >= AccessLevel::Public) + sharedDeclString += "public "; + + sharedDeclString += "static let shared = <#actor instance#>"; + + primaryDiag.fixItInsert(sharedInsertionLoc, sharedDeclString); + } + } + + // Remark about all of the candidates that failed (and why). + for (auto candidate : candidates) { + if (!candidate->isStatic()) { + candidate->diagnose(diag::global_actor_shared_not_static) + .fixItInsert(candidate->getAttributeInsertionLoc(true), "static "); + continue; + } + + if (isDeclNotAsAccessibleAsParent(candidate, nominal)) { + AccessLevel needAccessLevel = std::min( + nominal->getFormalAccess(), AccessLevel::Public); + auto diag = candidate->diagnose( + diag::global_actor_shared_inaccessible, + getAccessLevelSpelling(candidate->getFormalAccess()), + getAccessLevelSpelling(needAccessLevel)); + if (auto attr = candidate->getAttrs().getAttribute()) { + if (needAccessLevel == AccessLevel::Internal) { + diag.fixItRemove(attr->getRange()); + } else { + diag.fixItReplace( + attr->getRange(), getAccessLevelSpelling(needAccessLevel)); + } + } else { + diag.fixItInsert( + candidate->getAttributeInsertionLoc(true), + getAccessLevelSpelling(needAccessLevel)); + } + continue; + } + + if (auto ext = dyn_cast(candidate->getDeclContext())) { + if (ext->isConstrainedExtension()) { + candidate->diagnose(diag::global_actor_shared_constrained_extension); + continue; + } + } + + Type varType = candidate->getDeclContext()->mapTypeIntoContext( + candidate->getValueInterfaceType()); + candidate->diagnose(diag::global_actor_shared_non_actor_type, varType); + } + + return nullptr; +} + namespace { /// The isolation restriction in effect for a given declaration that is diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index ca957db149544..99de01af99b28 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1514,6 +1514,7 @@ namespace { UNINTERESTING_ATTR(OriginallyDefinedIn) UNINTERESTING_ATTR(Actor) UNINTERESTING_ATTR(ActorIndependent) + UNINTERESTING_ATTR(GlobalActor) #undef UNINTERESTING_ATTR void visitAvailableAttr(AvailableAttr *attr) { diff --git a/test/attr/global_actor.swift b/test/attr/global_actor.swift new file mode 100644 index 0000000000000..859d8143a9a48 --- /dev/null +++ b/test/attr/global_actor.swift @@ -0,0 +1,45 @@ +// RUN: %target-swift-frontend -typecheck -verify %s -enable-experimental-concurrency +// REQUIRES: concurrency + +import _Concurrency + +actor class SomeActor { } + +// Well-formed global actor. +@globalActor +struct GA1 { + static let shared = SomeActor() +} + +// Ill-formed global actors. +@globalActor +open class GA2 { // expected-error{{global actor 'GA2' requires a static property 'shared' that produces an actor instance}}{{17-17=\n public static let shared = <#actor instance#>}} +} + +@globalActor +struct GA3 { // expected-error{{global actor 'GA3' requires a static property 'shared' that produces an actor instance}} + let shared = SomeActor() // expected-note{{'shared' property in global actor is not 'static'}}{{3-3=static }} +} + +@globalActor +struct GA4 { // expected-error{{global actor 'GA4' requires a static property 'shared' that produces an actor instance}} + private static let shared = SomeActor() // expected-note{{'shared' property has more restrictive access (private) than its global actor (internal)}}{{3-11=}} +} + +@globalActor +open class GA5 { // expected-error{{global actor 'GA5' requires a static property 'shared' that produces an actor instance}} + static let shared = SomeActor() // expected-note{{'shared' property has more restrictive access (internal) than its global actor (public)}}{{3-3=public}} +} + +@globalActor +struct GA6 { // expected-error{{global actor 'GA6' requires a static property 'shared' that produces an actor instance}} +} + +extension GA6 where T: Equatable { + static var shared: SomeActor { SomeActor() } // expected-note{{'shared' property in global actor cannot be in a constrained extension}} +} + +@globalActor +class GA7 { // expected-error{{global actor 'GA7' requires a static property 'shared' that produces an actor instance}} + static let shared = 5 // expected-note{{'shared' property type 'Int' does not conform to the 'Actor' protocol}} +} From b848bf785dd95350508f7115d32d7f57ff5181d7 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 9 Oct 2020 10:01:45 -0700 Subject: [PATCH 317/745] [parser lib build] For the "parser lib only" build, avoid building unnecessary llvm libraries that clangBasic brings --- lib/AST/CMakeLists.txt | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index e9e4ab76ccb30..815d679a8effc 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -126,15 +126,14 @@ target_link_libraries(swiftAST PUBLIC swiftBasic PRIVATE swiftSyntax) if(SWIFT_BUILD_ONLY_SYNTAXPARSERLIB) - # Add clangBasic as a single direct dependency to avoid bringing along some - # llvm libraries that we don't need. - if("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "WINDOWS") - target_link_libraries(swiftAST PRIVATE - "${LLVM_LIBRARY_OUTPUT_INTDIR}/clangBasic.lib") - else() - target_link_libraries(swiftAST PRIVATE - "${LLVM_LIBRARY_OUTPUT_INTDIR}/libclangBasic.a") - endif() + # Remove dependencies from clangBasic to avoid bringing along some llvm + # libraries that we don't need to be building. + set_property(TARGET clangBasic PROPERTY LINK_LIBRARIES "") + set_property(TARGET clangBasic PROPERTY LINK_DEPENDS "") + set_property(TARGET clangBasic PROPERTY INTERFACE_LINK_LIBRARIES "") + set_property(TARGET clangBasic PROPERTY INTERFACE_LINK_DEPENDS "") + target_link_libraries(swiftAST + PRIVATE clangBasic) target_compile_definitions(swiftAST PRIVATE SWIFT_BUILD_ONLY_SYNTAXPARSERLIB=1) else() From df883f89ad69c6e5f999614d476c38218081ef8a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 5 Oct 2020 07:50:07 -0700 Subject: [PATCH 318/745] [Concurrency] Allow global actor annotations on declarations. Global actor types can be used as attributes on various kinds of declarations to indicate that those declarations are part of the isolated state of that global actor. Allow such annotation and perform basic correctness checks. --- include/swift/AST/Decl.h | 10 +++ include/swift/AST/DiagnosticsSema.def | 10 +++ include/swift/AST/TypeCheckRequests.h | 24 +++++++ include/swift/AST/TypeCheckerTypeIDZone.def | 2 + lib/AST/Decl.cpp | 8 +++ lib/Sema/TypeCheckAttr.cpp | 7 ++ lib/Sema/TypeCheckConcurrency.cpp | 72 +++++++++++++++++++++ test/attr/global_actor.swift | 38 +++++++++++ 8 files changed, 171 insertions(+) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 96b48ab14ddd8..be81585971c92 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -950,6 +950,16 @@ class alignas(1 << DeclAlignInBits) Decl { /// If this returns true, the decl can be safely casted to ValueDecl. bool isPotentiallyOverridable() const; + /// Returns true if this Decl cannot be seen by any other source file + bool isPrivateToEnclosingFile() const; + + /// Retrieve the global actor attribute that applies to this declaration, + /// if any. + /// + /// This is the "raw" global actor attribute as written directly on the + /// declaration, with any inference rules applied. + CustomAttr *getGlobalActorAttr() const; + /// If an alternative module name is specified for this decl, e.g. using /// @_originalDefinedIn attribute, this function returns this module name. StringRef getAlternateModuleName() const; diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index ebf53e1309862..14fbb765f79d7 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4221,6 +4221,16 @@ NOTE(global_actor_shared_non_actor_type,none, "'shared' property type %0 does not conform to the 'Actor' protocol", (Type)) +ERROR(multiple_global_actors,none, + "declaration can not have multiple global actor attributes (%0 and %1)", + (Identifier, Identifier)) +ERROR(global_actor_disallowed,none, + "%0 cannot have a global actor", (DescriptiveDeclKind)) +ERROR(global_actor_on_actor_class,none, + "actor class %0 cannot have a global actor", (Identifier)) +ERROR(global_actor_on_local_variable,none, + "local variable %0 cannot have a global actor", (DeclName)) + //------------------------------------------------------------------------------ // MARK: Type Check Types //------------------------------------------------------------------------------ diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 149ea026d72cb..8158673d867fe 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -880,6 +880,30 @@ class GlobalActorInstanceRequest : bool isCached() const { return true; } }; +/// Request the custom attribute which denotes the global actor for the given +/// declaration. +/// +/// This is the "raw" global actor attribute as written directly on the +/// declaration, with any inference rules applied. +class GlobalActorAttributeRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + CustomAttr * + evaluate(Evaluator &evaluator, Decl *decl) const; + +public: + // Caching + bool isCached() const { return true; } +}; + /// Determine the actor isolation for the given declaration. class ActorIsolationRequest : public SimpleRequest(this); + return evaluateOrDefault(ctx.evaluator, + GlobalActorAttributeRequest{mutableThis}, + nullptr); +} + Expr *AbstractFunctionDecl::getSingleExpressionBody() const { assert(hasSingleExpressionBody() && "Not a single-expression body"); auto braceStmt = getBody(); diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index eceb6dc657fac..fddbfa77847de 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -3065,6 +3065,13 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { return; } + // If the nominal type is a global actor, let the global actor attribute + // retrieval request perform checking for us. + if (nominal->isGlobalActor()) { + (void)D->getGlobalActorAttr(); + return; + } + diagnose(attr->getLocation(), diag::nominal_type_not_attribute, nominal->getDescriptiveKind(), nominal->getName()); nominal->diagnose(diag::decl_declared_here, nominal->getName()); diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 51872cee74bef..0735e590202a4 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -18,6 +18,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/TypeCheckRequests.h" using namespace swift; @@ -327,6 +328,77 @@ VarDecl *GlobalActorInstanceRequest::evaluate( return nullptr; } +CustomAttr *GlobalActorAttributeRequest::evaluate( + Evaluator &evaluator, Decl *decl) const { + ASTContext &ctx = decl->getASTContext(); + auto dc = decl->getDeclContext(); + CustomAttr *globalActorAttr = nullptr; + NominalTypeDecl *globalActorNominal = nullptr; + + for (auto attr : decl->getAttrs().getAttributes()) { + auto mutableAttr = const_cast(attr); + // Figure out which nominal declaration this custom attribute refers to. + auto nominal = evaluateOrDefault(ctx.evaluator, + CustomAttrNominalRequest{mutableAttr, dc}, + nullptr); + + // Ignore unresolvable custom attributes. + if (!nominal) + continue; + + // We are only interested in global actor types. + if (!nominal->isGlobalActor()) + continue; + + // Only a single global actor can be applied to a given declaration. + if (globalActorAttr) { + decl->diagnose( + diag::multiple_global_actors, globalActorNominal->getName(), + nominal->getName()); + continue; + } + + globalActorAttr = const_cast(attr); + globalActorNominal = nominal; + } + + if (!globalActorAttr) + return nullptr; + + // Check that a global actor attribute makes sense on this kind of + // declaration. + if (auto nominal = dyn_cast(decl)) { + // Nominal types are okay... + if (auto classDecl = dyn_cast(nominal)){ + if (classDecl->isActor()) { + // ... except for actor classes. + nominal->diagnose(diag::global_actor_on_actor_class, nominal->getName()) + .highlight(globalActorAttr->getRangeWithAt()); + return nullptr; + } + } + } else if (auto storage = dyn_cast(decl)) { + // Subscripts and properties are fine... + if (auto var = dyn_cast(storage)) { + if (var->getDeclContext()->isLocalContext()) { + var->diagnose(diag::global_actor_on_local_variable, var->getName()) + .highlight(globalActorAttr->getRangeWithAt()); + return nullptr; + } + } + } else if (isa(decl)) { + // Extensions are okay. + } else if (isa(decl) || isa(decl)) { + // Functions are okay. + } else { + // Everything else is disallowed. + decl->diagnose(diag::global_actor_disallowed, decl->getDescriptiveKind()); + return nullptr; + } + + return globalActorAttr; +} + namespace { /// The isolation restriction in effect for a given declaration that is diff --git a/test/attr/global_actor.swift b/test/attr/global_actor.swift index 859d8143a9a48..52d027b1ca1c1 100644 --- a/test/attr/global_actor.swift +++ b/test/attr/global_actor.swift @@ -5,6 +5,10 @@ import _Concurrency actor class SomeActor { } +// ----------------------------------------------------------------------- +// @globalActor attribute itself. +// ----------------------------------------------------------------------- + // Well-formed global actor. @globalActor struct GA1 { @@ -43,3 +47,37 @@ extension GA6 where T: Equatable { class GA7 { // expected-error{{global actor 'GA7' requires a static property 'shared' that produces an actor instance}} static let shared = 5 // expected-note{{'shared' property type 'Int' does not conform to the 'Actor' protocol}} } + +// ----------------------------------------------------------------------- +// Applying global actors to entities. +// ----------------------------------------------------------------------- +@globalActor +struct OtherGlobalActor { + static let shared = SomeActor() +} + +@GA1 func f() { + @GA1 let x = 17 // expected-error{{local variable 'x' cannot have a global actor}} + _ = x +} + +@GA1 struct X { } + +struct Y { + @GA1 subscript(i: Int) -> Int { i } +} + +@GA1 extension Y { } + +@GA1 func g() { } + +class SomeClass { + @GA1 init() { } + @GA1 deinit { } // expected-error{{deinitializer cannot have a global actor}} +} + +@GA1 typealias Integer = Int // expected-error{{type alias cannot have a global actor}} + +@GA1 actor class ActorInTooManyPlaces { } // expected-error{{actor class 'ActorInTooManyPlaces' cannot have a global actor}} + +@GA1 @OtherGlobalActor func twoGlobalActors() { } // expected-error{{declaration can not have multiple global actor attributes ('OtherGlobalActor' and 'GA1')}} From e61ffea42c943044fe4fc8c35d5040bc8815e420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Mon, 31 Aug 2020 12:09:33 -0700 Subject: [PATCH 319/745] [Sema] Check availability in inlinable code using the explicit version Inlinable functions can be inlined in clients with a lower OS target version than the framework defining the function. For this reason, the availability in inlinable functions should always be checked using the explicit introduction OS version as lower bound and not the minimum deployment version. rdar://problem/67975153 --- include/swift/AST/TypeRefinementContext.h | 24 ++++++++++-- lib/AST/TypeRefinementContext.cpp | 26 ++++++------ lib/Sema/TypeCheckAvailability.cpp | 27 +++++++++---- test/Sema/availability_inlinable.swift | 48 +++++++++++++++++++++++ 4 files changed, 103 insertions(+), 22 deletions(-) create mode 100644 test/Sema/availability_inlinable.swift diff --git a/include/swift/AST/TypeRefinementContext.h b/include/swift/AST/TypeRefinementContext.h index 2dca10739444e..c0851969fd712 100644 --- a/include/swift/AST/TypeRefinementContext.h +++ b/include/swift/AST/TypeRefinementContext.h @@ -154,16 +154,23 @@ class TypeRefinementContext { SourceRange SrcRange; + /// Runtime availability information for the code in this context. AvailabilityContext AvailabilityInfo; + /// Runtime availability information as explicitly declared by attributes + /// for the inlinable code in this context. Compared to AvailabilityInfo, + /// this is not bounded to the minimum deployment OS version. + AvailabilityContext AvailabilityInfoExplicit; + std::vector Children; TypeRefinementContext(ASTContext &Ctx, IntroNode Node, TypeRefinementContext *Parent, SourceRange SrcRange, - const AvailabilityContext &Info); + const AvailabilityContext &Info, + const AvailabilityContext &InfoExplicit); public: - + /// Create the root refinement context for the given SourceFile. static TypeRefinementContext *createRoot(SourceFile *SF, const AvailabilityContext &Info); @@ -172,8 +179,9 @@ class TypeRefinementContext { static TypeRefinementContext *createForDecl(ASTContext &Ctx, Decl *D, TypeRefinementContext *Parent, const AvailabilityContext &Info, + const AvailabilityContext &InfoExplicit, SourceRange SrcRange); - + /// Create a refinement context for the Then branch of the given IfStmt. static TypeRefinementContext * createForIfStmtThen(ASTContext &Ctx, IfStmt *S, TypeRefinementContext *Parent, @@ -240,11 +248,19 @@ class TypeRefinementContext { SourceRange getSourceRange() const { return SrcRange; } /// Returns the information on what can be assumed present at run time when - /// running code contained in this context. + /// running code contained in this context, taking into account the minimum + /// deployment target. const AvailabilityContext &getAvailabilityInfo() const { return AvailabilityInfo; } + /// Returns the information on what can be assumed present at run time when + /// running code contained in this context if it were to be inlined, + /// without considering the minimum deployment target. + const AvailabilityContext &getAvailabilityInfoExplicit() const { + return AvailabilityInfoExplicit; + } + /// Adds a child refinement context. void addChild(TypeRefinementContext *Child) { assert(Child->getSourceRange().isValid()); diff --git a/lib/AST/TypeRefinementContext.cpp b/lib/AST/TypeRefinementContext.cpp index c5bcaeb4e0d54..758c6825f3d86 100644 --- a/lib/AST/TypeRefinementContext.cpp +++ b/lib/AST/TypeRefinementContext.cpp @@ -28,12 +28,14 @@ using namespace swift; TypeRefinementContext::TypeRefinementContext(ASTContext &Ctx, IntroNode Node, TypeRefinementContext *Parent, SourceRange SrcRange, - const AvailabilityContext &Info) - : Node(Node), SrcRange(SrcRange), AvailabilityInfo(Info) { + const AvailabilityContext &Info, + const AvailabilityContext &InfoExplicit) + : Node(Node), SrcRange(SrcRange), AvailabilityInfo(Info), + AvailabilityInfoExplicit(InfoExplicit) { if (Parent) { assert(SrcRange.isValid()); Parent->addChild(this); - assert(Info.isContainedIn(Parent->getAvailabilityInfo())); + assert(InfoExplicit.isContainedIn(Parent->getAvailabilityInfoExplicit())); } Ctx.addDestructorCleanup(Children); } @@ -46,18 +48,20 @@ TypeRefinementContext::createRoot(SourceFile *SF, ASTContext &Ctx = SF->getASTContext(); return new (Ctx) TypeRefinementContext(Ctx, SF, - /*Parent=*/nullptr, SourceRange(), Info); + /*Parent=*/nullptr, SourceRange(), Info, + AvailabilityContext::alwaysAvailable()); } TypeRefinementContext * TypeRefinementContext::createForDecl(ASTContext &Ctx, Decl *D, TypeRefinementContext *Parent, const AvailabilityContext &Info, + const AvailabilityContext &InfoExplicit, SourceRange SrcRange) { assert(D); assert(Parent); return new (Ctx) - TypeRefinementContext(Ctx, D, Parent, SrcRange, Info); + TypeRefinementContext(Ctx, D, Parent, SrcRange, Info, InfoExplicit); } TypeRefinementContext * @@ -68,7 +72,7 @@ TypeRefinementContext::createForIfStmtThen(ASTContext &Ctx, IfStmt *S, assert(Parent); return new (Ctx) TypeRefinementContext(Ctx, IntroNode(S, /*IsThen=*/true), Parent, - S->getThenStmt()->getSourceRange(), Info); + S->getThenStmt()->getSourceRange(), Info, Info); } TypeRefinementContext * @@ -79,7 +83,7 @@ TypeRefinementContext::createForIfStmtElse(ASTContext &Ctx, IfStmt *S, assert(Parent); return new (Ctx) TypeRefinementContext(Ctx, IntroNode(S, /*IsThen=*/false), Parent, - S->getElseStmt()->getSourceRange(), Info); + S->getElseStmt()->getSourceRange(), Info, Info); } TypeRefinementContext * @@ -92,7 +96,7 @@ TypeRefinementContext::createForConditionFollowingQuery(ASTContext &Ctx, assert(Parent); SourceRange Range(PAI->getEndLoc(), LastElement.getEndLoc()); return new (Ctx) TypeRefinementContext(Ctx, PAI, Parent, Range, - Info); + Info, Info); } TypeRefinementContext * @@ -107,7 +111,7 @@ TypeRefinementContext::createForGuardStmtFallthrough(ASTContext &Ctx, SourceRange Range(RS->getEndLoc(), ContainingBraceStmt->getEndLoc()); return new (Ctx) TypeRefinementContext(Ctx, IntroNode(RS, /*IsFallthrough=*/true), - Parent, Range, Info); + Parent, Range, Info, Info); } TypeRefinementContext * @@ -118,7 +122,7 @@ TypeRefinementContext::createForGuardStmtElse(ASTContext &Ctx, GuardStmt *RS, assert(Parent); return new (Ctx) TypeRefinementContext(Ctx, IntroNode(RS, /*IsFallthrough=*/false), Parent, - RS->getBody()->getSourceRange(), Info); + RS->getBody()->getSourceRange(), Info, Info); } TypeRefinementContext * @@ -128,7 +132,7 @@ TypeRefinementContext::createForWhileStmtBody(ASTContext &Ctx, WhileStmt *S, assert(S); assert(Parent); return new (Ctx) TypeRefinementContext( - Ctx, S, Parent, S->getBody()->getSourceRange(), Info); + Ctx, S, Parent, S->getBody()->getSourceRange(), Info, Info); } // Only allow allocation of TypeRefinementContext using the allocator in diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index f99a9415f551c..1f102fa89bb52 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -170,13 +170,26 @@ class TypeRefinementContextBuilder : private ASTWalker { // The potential versions in the declaration are constrained by both // the declared availability of the declaration and the potential versions // of its lexical context. - AvailabilityContext DeclInfo = + AvailabilityContext ExplicitDeclInfo = swift::AvailabilityInference::availableRange(D, Context); - DeclInfo.intersectWith(getCurrentTRC()->getAvailabilityInfo()); + ExplicitDeclInfo.intersectWith( + getCurrentTRC()->getAvailabilityInfoExplicit()); + AvailabilityContext DeclInfo = ExplicitDeclInfo; + + // When the body is inlinable consider only the explicitly declared range + // for checking availability. Otherwise, use the parent range which may + // begin at the minimum deployment target. + bool isInlinable = D->getAttrs().hasAttribute() || + D->getAttrs().hasAttribute(); + if (!isInlinable)) { + DeclInfo.intersectWith( + getCurrentTRC()->getAvailabilityInfo()); + } TypeRefinementContext *NewTRC = TypeRefinementContext::createForDecl(Context, D, getCurrentTRC(), DeclInfo, + ExplicitDeclInfo, refinementSourceRangeForDecl(D)); // Record the TRC for this storage declaration so that @@ -198,8 +211,10 @@ class TypeRefinementContextBuilder : private ASTWalker { } // No need to introduce a context if the declaration does not have an - // availability attribute. - if (!hasActiveAvailableAttribute(D, Context)) { + // availability or inlinable attribute. + if (!hasActiveAvailableAttribute(D, Context) && + !D->getAttrs().hasAttribute() && + !D->getAttrs().hasAttribute()) { return false; } @@ -674,9 +689,7 @@ TypeChecker::overApproximateAvailabilityAtLocation(SourceLoc loc, // refined. For now, this is fine -- but if we ever synthesize #available(), // this will be a real problem. - // We can assume we are running on at least the minimum deployment target. - auto OverApproximateContext = - AvailabilityContext::forDeploymentTarget(Context); + auto OverApproximateContext = AvailabilityContext::alwaysAvailable(); auto isInvalidLoc = [SF](SourceLoc loc) { return SF ? loc.isInvalid() : true; }; diff --git a/test/Sema/availability_inlinable.swift b/test/Sema/availability_inlinable.swift new file mode 100644 index 0000000000000..34750bce1134e --- /dev/null +++ b/test/Sema/availability_inlinable.swift @@ -0,0 +1,48 @@ +/// Inlinable functions should check availability by ignoring the current +/// deployment target as clients could inline the function in a lower target. + +// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx11.0 +// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx10.10 + +// REQUIRES: OS=macosx + +@available(macOS 10.10, *) +@inlinable +public func availMacOS10() { + availMacOS11() // expected-error {{'availMacOS11()' is only available in macOS 11.0 or newer}} + // expected-note @-1 {{add 'if #available' version check}} + + if #available(macOS 11.0, *) { + availMacOS11() + } else { + availMacOS11() // expected-error {{'availMacOS11()' is only available in macOS 11.0 or newer}} + // expected-note @-1 {{add 'if #available' version check}} + } + + if #available(macOS 10.15, *) { + availMacOS11() // expected-error {{'availMacOS11()' is only available in macOS 11.0 or newer}} + // expected-note @-1 {{add 'if #available' version check}} + } + + func nestedFunc() { + availMacOS11() // expected-error {{'availMacOS11()' is only available in macOS 11.0 or newer}} + // expected-note @-1 {{add 'if #available' version check}} + } +} + +@available(macOS 11.0, *) +public func availMacOS11() { } + +@available(macOS 10.10, *) +public struct StructAvailMacOS10 { + @inlinable + public func availabilityFromTheContextInlinable() { + // expected-note @-1 {{add @available attribute to enclosing instance method}} + availMacOS11() // expected-error {{'availMacOS11()' is only available in macOS 11.0 or newer}} + // expected-note @-1 {{add 'if #available' version check}} + + availabilityFromContext() + } + + public func availabilityFromContext() {} +} From 53e04cb39db86d890ff674b775a5dbda47e9d599 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Tue, 1 Sep 2020 14:09:02 -0700 Subject: [PATCH 320/745] [Sema] Test explicit versions in TypeRefinementContext --- lib/AST/TypeRefinementContext.cpp | 1 + .../availability_refinement_contexts.swift | 129 ++++++++++++------ 2 files changed, 86 insertions(+), 44 deletions(-) diff --git a/lib/AST/TypeRefinementContext.cpp b/lib/AST/TypeRefinementContext.cpp index 758c6825f3d86..3fe283bebd896 100644 --- a/lib/AST/TypeRefinementContext.cpp +++ b/lib/AST/TypeRefinementContext.cpp @@ -300,6 +300,7 @@ void TypeRefinementContext::print(raw_ostream &OS, SourceManager &SrcMgr, OS << "(" << getReasonName(getReason()); OS << " versions=" << AvailabilityInfo.getOSVersion().getAsString(); + OS << " explicit=" << AvailabilityInfoExplicit.getOSVersion().getAsString(); if (getReason() == Reason::Decl) { Decl *D = Node.getAsDecl(); diff --git a/test/Sema/availability_refinement_contexts.swift b/test/Sema/availability_refinement_contexts.swift index 088a644384ff6..818b9b969bd74 100644 --- a/test/Sema/availability_refinement_contexts.swift +++ b/test/Sema/availability_refinement_contexts.swift @@ -1,18 +1,18 @@ -// RUN: %target-swift-frontend -typecheck -dump-type-refinement-contexts %s > %t.dump 2>&1 +// RUN: %target-swift-frontend -typecheck -dump-type-refinement-contexts -target %target-cpu-apple-macosx10.50 %s > %t.dump 2>&1 // RUN: %FileCheck --strict-whitespace %s < %t.dump // REQUIRES: OS=macosx -// CHECK: {{^}}(root versions=[10.{{[0-9]+}}.0,+Inf) +// CHECK: {{^}}(root versions=[10.50.0,+Inf) explicit=all -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=SomeClass -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=someMethod() -// CHECK-NEXT: {{^}} (decl versions=[10.53,+Inf) decl=someInnerFunc() -// CHECK-NEXT: {{^}} (decl versions=[10.53,+Inf) decl=InnerClass -// CHECK-NEXT: {{^}} (decl versions=[10.54,+Inf) decl=innerClassMethod -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=someStaticProperty -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=someComputedProperty -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=someOtherMethod() +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=SomeClass +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=someMethod() +// CHECK-NEXT: {{^}} (decl versions=[10.53,+Inf) explicit=[10.53,+Inf) decl=someInnerFunc() +// CHECK-NEXT: {{^}} (decl versions=[10.53,+Inf) explicit=[10.53,+Inf) decl=InnerClass +// CHECK-NEXT: {{^}} (decl versions=[10.54,+Inf) explicit=[10.54,+Inf) decl=innerClassMethod +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=someStaticProperty +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=someComputedProperty +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=someOtherMethod() @available(OSX 10.51, *) class SomeClass { @available(OSX 10.52, *) @@ -43,13 +43,13 @@ class SomeClass { func someOtherMethod() { } } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=someFunction() +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=someFunction() @available(OSX 10.51, *) func someFunction() { } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=SomeProtocol -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=protoMethod() -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=protoProperty +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=SomeProtocol +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=protoMethod() +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=protoProperty @available(OSX 10.51, *) protocol SomeProtocol { @available(OSX 10.52, *) @@ -59,26 +59,26 @@ protocol SomeProtocol { var protoProperty: Int { get } } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=extension.SomeClass -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=someExtensionFunction() +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=extension.SomeClass +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=someExtensionFunction() @available(OSX 10.51, *) extension SomeClass { @available(OSX 10.52, *) func someExtensionFunction() { } } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=functionWithStmtCondition -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.52,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.53,+Inf) -// CHECK-NEXT: {{^}} (if_then versions=[10.53,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.54,+Inf) -// CHECK-NEXT: {{^}} (if_then versions=[10.54,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.55,+Inf) -// CHECK-NEXT: {{^}} (decl versions=[10.55,+Inf) decl=funcInGuardElse() -// CHECK-NEXT: {{^}} (guard_fallthrough versions=[10.55,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.56,+Inf) -// CHECK-NEXT: {{^}} (guard_fallthrough versions=[10.56,+Inf) -// CHECK-NEXT: {{^}} (decl versions=[10.57,+Inf) decl=funcInInnerIfElse() +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=functionWithStmtCondition +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.52,+Inf) explicit=[10.52,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.53,+Inf) explicit=[10.53,+Inf) +// CHECK-NEXT: {{^}} (if_then versions=[10.53,+Inf) explicit=[10.53,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.54,+Inf) explicit=[10.54,+Inf) +// CHECK-NEXT: {{^}} (if_then versions=[10.54,+Inf) explicit=[10.54,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.55,+Inf) explicit=[10.55,+Inf) +// CHECK-NEXT: {{^}} (decl versions=[10.55,+Inf) explicit=[10.55,+Inf) decl=funcInGuardElse() +// CHECK-NEXT: {{^}} (guard_fallthrough versions=[10.55,+Inf) explicit=[10.55,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.56,+Inf) explicit=[10.56,+Inf) +// CHECK-NEXT: {{^}} (guard_fallthrough versions=[10.56,+Inf) explicit=[10.56,+Inf) +// CHECK-NEXT: {{^}} (decl versions=[10.57,+Inf) explicit=[10.57,+Inf) decl=funcInInnerIfElse() @available(OSX 10.51, *) func functionWithStmtCondition() { if #available(OSX 10.52, *), @@ -97,11 +97,11 @@ func functionWithStmtCondition() { } } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=functionWithUnnecessaryStmtCondition -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.53,+Inf) -// CHECK-NEXT: {{^}} (if_then versions=[10.53,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.54,+Inf) -// CHECK-NEXT: {{^}} (if_then versions=[10.54,+Inf) +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=functionWithUnnecessaryStmtCondition +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.53,+Inf) explicit=[10.53,+Inf) +// CHECK-NEXT: {{^}} (if_then versions=[10.53,+Inf) explicit=[10.53,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.54,+Inf) explicit=[10.54,+Inf) +// CHECK-NEXT: {{^}} (if_then versions=[10.54,+Inf) explicit=[10.54,+Inf) @available(OSX 10.51, *) func functionWithUnnecessaryStmtCondition() { @@ -127,13 +127,13 @@ func functionWithUnnecessaryStmtCondition() { } } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=functionWithUnnecessaryStmtConditionsHavingElseBranch -// CHECK-NEXT: {{^}} (if_else versions=empty -// CHECK-NEXT: {{^}} (decl versions=empty decl=funcInInnerIfElse() -// CHECK-NEXT: {{^}} (if_else versions=empty -// CHECK-NEXT: {{^}} (guard_else versions=empty -// CHECK-NEXT: {{^}} (guard_else versions=empty -// CHECK-NEXT: {{^}} (if_else versions=empty +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=functionWithUnnecessaryStmtConditionsHavingElseBranch +// CHECK-NEXT: {{^}} (if_else versions=empty explicit=empty +// CHECK-NEXT: {{^}} (decl versions=empty explicit=empty decl=funcInInnerIfElse() +// CHECK-NEXT: {{^}} (if_else versions=empty explicit=empty +// CHECK-NEXT: {{^}} (guard_else versions=empty explicit=empty +// CHECK-NEXT: {{^}} (guard_else versions=empty explicit=empty +// CHECK-NEXT: {{^}} (if_else versions=empty explicit=empty @available(OSX 10.51, *) func functionWithUnnecessaryStmtConditionsHavingElseBranch(p: Int?) { @@ -180,10 +180,10 @@ func functionWithUnnecessaryStmtConditionsHavingElseBranch(p: Int?) { } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=functionWithWhile() -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.52,+Inf) -// CHECK-NEXT: {{^}} (while_body versions=[10.52,+Inf) -// CHECK-NEXT: {{^}} (decl versions=[10.54,+Inf) decl=funcInWhileBody() +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=functionWithWhile() +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.52,+Inf) explicit=[10.52,+Inf) +// CHECK-NEXT: {{^}} (while_body versions=[10.52,+Inf) explicit=[10.52,+Inf) +// CHECK-NEXT: {{^}} (decl versions=[10.54,+Inf) explicit=[10.54,+Inf) decl=funcInWhileBody() @available(OSX 10.51, *) func functionWithWhile() { while #available(OSX 10.52, *), @@ -192,3 +192,44 @@ func functionWithWhile() { func funcInWhileBody() { } } } + +// CHECK-NEXT: {{^}} (decl versions=[10.50.0,+Inf) explicit=[10.10,+Inf) decl=olderFunction() +@available(macOS 10.10, *) +public func olderFunction() { +} + +// CHECK-NEXT: {{^}} (decl versions=[10.10,+Inf) explicit=[10.10,+Inf) decl=inlinableFunction() +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.55,+Inf) explicit=[10.55,+Inf) +// CHECK-NEXT: {{^}} (if_then versions=[10.55,+Inf) explicit=[10.55,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.15,+Inf) +// CHECK-NEXT: {{^}} (if_then versions=[10.15,+Inf) explicit=[10.15,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.15,+Inf) explicit=[10.15,+Inf) +// CHECK-NEXT: {{^}} (guard_fallthrough versions=[10.15,+Inf) explicit=[10.15,+Inf) +@available(macOS 10.10, *) +@inlinable +public func inlinableFunction() { + if #available(macOS 10.55, *) { } else { } + + if #available(macOS 10.15, *) { } + + guard #available(macOS 10.15, *) else { } + + func nestedFunc() { } +} + +// CHECK-NEXT: {{^}} (decl versions=[10.50.0,+Inf) explicit=[10.10,+Inf) decl=SomeOlderClass +// CHECK-NEXT: {{^}} (decl versions=[10.10,+Inf) explicit=[10.10,+Inf) decl=someInlinableMethod() +// CHECK-NEXT: {{^}} (decl versions=[10.15,+Inf) explicit=[10.15,+Inf) decl=someOtherInlinableMethod() +// CHECK-NEXT: {{^}} (decl versions=[10.50.0,+Inf) explicit=[10.15,+Inf) decl=someMethod() +@available(OSX 10.10, *) +class SomeOlderClass { + @inlinable + func someInlinableMethod() { } + + @available(OSX 10.15, *) + @inlinable + func someOtherInlinableMethod() { } + + @available(OSX 10.15, *) + func someMethod() { } +} From 2f182c2b785f5f7b68ee756d1474f21f3050ba67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Wed, 2 Sep 2020 13:21:20 -0700 Subject: [PATCH 321/745] [Sema] Consider unavailable functions as being unreachable This has the effect of rejecting unavailable overrides to available methods in a similar way as overrides that are less available than the introduction are rejected. --- lib/AST/Availability.cpp | 16 +++++++-- test/SILGen/vtables.swift | 5 +-- .../availability_refinement_contexts.swift | 5 +++ test/Sema/availability_versions.swift | 33 ++++++++++++++++--- test/attr/attr_availability_maccatalyst.swift | 21 +++++++++++- 5 files changed, 68 insertions(+), 12 deletions(-) diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index e042a3dda668f..20e9fb6132ec1 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -144,8 +144,13 @@ static bool isBetterThan(const AvailableAttr *newAttr, return true; // If they belong to the same platform, the one that introduces later wins. - if (prevAttr->Platform == newAttr->Platform) + if (prevAttr->Platform == newAttr->Platform) { + if (newAttr->isUnconditionallyUnavailable()) + return true; + if (prevAttr->isUnconditionallyUnavailable()) + return false; return prevAttr->Introduced.getValue() < newAttr->Introduced.getValue(); + } // If the new attribute's platform inherits from the old one, it wins. return inheritsAvailabilityFromPlatform(newAttr->Platform, @@ -158,10 +163,12 @@ AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) { for (auto Attr : D->getAttrs()) { auto *AvailAttr = dyn_cast(Attr); - if (AvailAttr == nullptr || !AvailAttr->Introduced.hasValue() || + if (AvailAttr == nullptr || !AvailAttr->isActivePlatform(Ctx) || AvailAttr->isLanguageVersionSpecific() || - AvailAttr->isPackageDescriptionVersionSpecific()) { + AvailAttr->isPackageDescriptionVersionSpecific() || + (!AvailAttr->Introduced.hasValue() && + !AvailAttr->isUnconditionallyUnavailable())) { continue; } @@ -172,6 +179,9 @@ AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) { if (!bestAvailAttr) return None; + if (bestAvailAttr->isUnconditionallyUnavailable()) + return AvailabilityContext(VersionRange::empty()); + return AvailabilityContext{ VersionRange::allGTE(bestAvailAttr->Introduced.getValue())}; } diff --git a/test/SILGen/vtables.swift b/test/SILGen/vtables.swift index 387fd0df1e69d..9c0f2fafc7369 100644 --- a/test/SILGen/vtables.swift +++ b/test/SILGen/vtables.swift @@ -21,7 +21,6 @@ class C : B { // CHECK: #A.bar: {{.*}} : @$s7vtables1CC3bar{{[_0-9a-zA-Z]*}}F // CHECK: #A.bas: {{.*}} : @$s7vtables1AC3bas{{[_0-9a-zA-Z]*}}F // CHECK: #A.qux: {{.*}} : @$s7vtables1CC3qux{{[_0-9a-zA-Z]*}}F -// CHECK: #A.flux: {{.*}} : @$s7vtables1BC4flux{{[_0-9a-zA-Z]*}}F // CHECK: #B.init!allocator: {{.*}} : @$s7vtables1CC{{[_0-9a-zA-Z]*}}fC // CHECK: #B.zim: {{.*}} : @$s7vtables1BC3zim{{[_0-9a-zA-Z]*}}F // CHECK: #B.zang: {{.*}} : @$s7vtables1CC4zang{{[_0-9a-zA-Z]*}}F @@ -35,7 +34,7 @@ class A { func bar() {} func bas() {} func qux() {} - func flux() {} + @available(*, unavailable) func flux() {} } // CHECK: sil_vtable A { @@ -54,7 +53,6 @@ class B : A { // bar inherited from A // bas inherited from A override func qux() {} - @available(*, unavailable) override func flux() {} func zim() {} func zang() {} @@ -65,7 +63,6 @@ class B : A { // CHECK: #A.bar: {{.*}} : @$s7vtables1AC3bar{{[_0-9a-zA-Z]*}}F // CHECK: #A.bas: {{.*}} : @$s7vtables1AC3bas{{[_0-9a-zA-Z]*}}F // CHECK: #A.qux: {{.*}} : @$s7vtables1BC3qux{{[_0-9a-zA-Z]*}}F -// CHECK: #A.flux: {{.*}} : @$s7vtables1BC4flux{{[_0-9a-zA-Z]*}}F // CHECK: #B.init!allocator: {{.*}} : @$s7vtables1BC{{[_0-9a-zA-Z]*}}fC // CHECK: #B.zim: {{.*}} : @$s7vtables1BC3zim{{[_0-9a-zA-Z]*}}F // CHECK: #B.zang: {{.*}} : @$s7vtables1BC4zang{{[_0-9a-zA-Z]*}}F diff --git a/test/Sema/availability_refinement_contexts.swift b/test/Sema/availability_refinement_contexts.swift index 818b9b969bd74..44f6b8fd05f6e 100644 --- a/test/Sema/availability_refinement_contexts.swift +++ b/test/Sema/availability_refinement_contexts.swift @@ -198,6 +198,11 @@ func functionWithWhile() { public func olderFunction() { } +// CHECK-NEXT: {{^}} (decl versions=empty explicit=empty decl=unavailableFunction() +@available(macOS, unavailable) +public func unavailableFunction() { +} + // CHECK-NEXT: {{^}} (decl versions=[10.10,+Inf) explicit=[10.10,+Inf) decl=inlinableFunction() // CHECK-NEXT: {{^}} (condition_following_availability versions=[10.55,+Inf) explicit=[10.55,+Inf) // CHECK-NEXT: {{^}} (if_then versions=[10.55,+Inf) explicit=[10.55,+Inf) diff --git a/test/Sema/availability_versions.swift b/test/Sema/availability_versions.swift index ad4159215becf..ff86f938446ba 100644 --- a/test/Sema/availability_versions.swift +++ b/test/Sema/availability_versions.swift @@ -788,21 +788,21 @@ class UnavailableClassExtendingUnavailableClass : ClassAvailableOn10_51 { // Method availability is contravariant class SuperWithAlwaysAvailableMembers { - func shouldAlwaysBeAvailableMethod() { // expected-note {{overridden declaration is here}} + func shouldAlwaysBeAvailableMethod() { // expected-note 2 {{overridden declaration is here}} } - var shouldAlwaysBeAvailableProperty: Int { // expected-note {{overridden declaration is here}} + var shouldAlwaysBeAvailableProperty: Int { // expected-note 2 {{overridden declaration is here}} get { return 9 } set(newVal) {} } var setterShouldAlwaysBeAvailableProperty: Int { get { return 9 } - set(newVal) {} // expected-note {{overridden declaration is here}} + set(newVal) {} // expected-note 2 {{overridden declaration is here}} } var getterShouldAlwaysBeAvailableProperty: Int { - get { return 9 } // expected-note {{overridden declaration is here}} + get { return 9 } // expected-note 2 {{overridden declaration is here}} set(newVal) {} } } @@ -832,6 +832,31 @@ class SubWithLimitedMemberAvailability : SuperWithAlwaysAvailableMembers { } } +class SubWithUnavailableMembers : SuperWithAlwaysAvailableMembers { + @available(OSX, unavailable) + override func shouldAlwaysBeAvailableMethod() { // expected-error {{overriding 'shouldAlwaysBeAvailableMethod' must be as available as declaration it overrides}} + } + + @available(OSX, unavailable) + override var shouldAlwaysBeAvailableProperty: Int { // expected-error {{overriding 'shouldAlwaysBeAvailableProperty' must be as available as declaration it overrides}} + get { return 10 } + set(newVal) {} + } + + override var setterShouldAlwaysBeAvailableProperty: Int { + get { return 9 } + @available(OSX, unavailable) + set(newVal) {} // expected-error {{overriding setter for 'setterShouldAlwaysBeAvailableProperty' must be as available as declaration it overrides}} + // This is a terrible diagnostic. rdar://problem/20427938 + } + + override var getterShouldAlwaysBeAvailableProperty: Int { + @available(OSX, unavailable) + get { return 9 } // expected-error {{overriding getter for 'getterShouldAlwaysBeAvailableProperty' must be as available as declaration it overrides}} + set(newVal) {} + } +} + class SuperWithLimitedMemberAvailability { @available(OSX, introduced: 10.51) func someMethod() { diff --git a/test/attr/attr_availability_maccatalyst.swift b/test/attr/attr_availability_maccatalyst.swift index f69cc8aea1b3d..2346cb9ec0dfc 100644 --- a/test/attr/attr_availability_maccatalyst.swift +++ b/test/attr/attr_availability_maccatalyst.swift @@ -1,6 +1,6 @@ // RUN: %swift -typecheck -verify -parse-stdlib -target x86_64-apple-ios51.0-macabi %s -// REQUIRES: OS=maccatalyst +// REQUIRES: VENDOR=apple @available(macCatalyst, introduced: 1.0, deprecated: 2.0, obsoleted: 9.0, message: "you don't want to do that anyway") @@ -52,6 +52,16 @@ func introducedLaterOnMacCatalyst() { func introducedLaterOnIOS() { } +@available(iOS, introduced: 1.0) +@available(macCatalyst, unavailable) +func unavailableOnMacCatalyst() { // expected-note 3 {{'unavailableOnMacCatalyst()' has been explicitly marked unavailable here}} +} + +@available(iOS, unavailable) +@available(macCatalyst, introduced: 1.0) +func unavailableOnIOS() { +} + // expected-note@+1 *{{add @available attribute to enclosing global function}} func testPoundAvailable() { @@ -60,6 +70,9 @@ func testPoundAvailable() { // expected-note@-1 {{add 'if #available' version check}} introducedLaterOnIOS() // expected-error {{'introducedLaterOnIOS()' is only available in Mac Catalyst 56.0 or newer}} // expected-note@-1 {{add 'if #available' version check}} + + unavailableOnMacCatalyst() // expected-error {{'unavailableOnMacCatalyst()' is unavailable in Mac Catalyst}} + unavailableOnIOS() } // macCatalyst should win over iOS when present @@ -69,6 +82,9 @@ func testPoundAvailable() { // expected-note@-1 {{add 'if #available' version check}} introducedLaterOnIOS() // expected-error {{'introducedLaterOnIOS()' is only available in Mac Catalyst 56.0 or newer}} // expected-note@-1 {{add 'if #available' version check}} + + unavailableOnMacCatalyst() // expected-error {{'unavailableOnMacCatalyst()' is unavailable in Mac Catalyst}} + unavailableOnIOS() } if #available(iOS 55.0, macCatalyst 56.0, *) { @@ -88,6 +104,9 @@ func testPoundAvailable() { // expected-note@-1 {{add 'if #available' version check}} introducedLaterOnIOS() // expected-error {{'introducedLaterOnIOS()' is only available in Mac Catalyst 56.0 or newer}} // expected-note@-1 {{add 'if #available' version check}} + + unavailableOnMacCatalyst() // expected-error {{'unavailableOnMacCatalyst()' is unavailable in Mac Catalyst}} + unavailableOnIOS() } if #available(iOS 56.0, *) { From 39ee5916bd656cf56c75ddf45a47e628d0ff1db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Wed, 2 Sep 2020 19:10:45 -0700 Subject: [PATCH 322/745] [SIL] Don't print availability for unreachable/unavailable functions --- lib/SIL/IR/SILPrinter.cpp | 3 ++- lib/Serialization/SerializeSIL.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index f08d0cfc025f3..2ba2f076e9a1b 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -2638,7 +2638,8 @@ void SILFunction::print(SILPrintContext &PrintCtx) const { if (isAlwaysWeakImported()) OS << "[weak_imported] "; auto availability = getAvailabilityForLinkage(); - if (!availability.isAlwaysAvailable()) { + if (!availability.isAlwaysAvailable() && + !availability.isKnownUnreachable()) { auto version = availability.getOSVersion().getLowerEndpoint(); OS << "[available " << version.getAsString() << "] "; } diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index b651fcae6698d..eb7e086679a00 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -430,7 +430,8 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) { Optional available; auto availability = F.getAvailabilityForLinkage(); - if (!availability.isAlwaysAvailable()) { + if (!availability.isAlwaysAvailable() && + !availability.isKnownUnreachable()) { available = availability.getOSVersion().getLowerEndpoint(); } ENCODE_VER_TUPLE(available, available) From a5e953b69009b59541420d06496206a26961a976 Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Wed, 26 Feb 2020 15:49:33 +0100 Subject: [PATCH 323/745] Add support for calling C++ constructors. Because C++ constructors always take a `this` pointer to the object to be initialized, we mark the SIL function return type with the `@out` attribute. On the IRGen side, we retrofit support for formal indirect return values as well as thin metatypes. --- lib/ClangImporter/ClangImporter.cpp | 6 +- lib/ClangImporter/ImportDecl.cpp | 88 +- lib/ClangImporter/ImportName.cpp | 14 + lib/IRGen/GenCall.cpp | 27 +- lib/IRGen/GenClangType.cpp | 14 +- lib/SIL/IR/SILFunctionType.cpp | 21 + lib/SIL/SILFunctionType.cpp | 4284 +++++++++++++++++ test/CXXInterop/Inputs/cxx_constructors.h | 22 + test/CXXInterop/Inputs/module.modulemap | 3 + .../cxx-constructors-executable.swift | 37 + test/CXXInterop/cxx-constructors-ir.swift | 14 + .../cxx-constructors-module-interface.swift | 20 + test/CXXInterop/cxx-constructors-sil.swift | 13 + .../cxx-constructors-typecheck.swift | 13 + 14 files changed, 4547 insertions(+), 29 deletions(-) create mode 100644 lib/SIL/SILFunctionType.cpp create mode 100644 test/CXXInterop/Inputs/cxx_constructors.h create mode 100644 test/CXXInterop/Inputs/module.modulemap create mode 100644 test/CXXInterop/cxx-constructors-executable.swift create mode 100644 test/CXXInterop/cxx-constructors-ir.swift create mode 100644 test/CXXInterop/cxx-constructors-module-interface.swift create mode 100644 test/CXXInterop/cxx-constructors-sil.swift create mode 100644 test/CXXInterop/cxx-constructors-typecheck.swift diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 0720569da9558..2aad8328ac8cd 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3589,7 +3589,11 @@ void ClangImporter::getMangledName(raw_ostream &os, if (!Impl.Mangler) Impl.Mangler.reset(Impl.getClangASTContext().createMangleContext()); - Impl.Mangler->mangleName(clangDecl, os); + if (auto ctor = dyn_cast(clangDecl)) { + Impl.Mangler->mangleCXXCtor(ctor, clang::Ctor_Complete, os); + } else { + Impl.Mangler->mangleName(clangDecl, os); + } } // --------------------------------------------------------------------------- diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 079ded2e02094..9bf0a0e91f309 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3378,6 +3378,11 @@ namespace { continue; } + if (auto CD = dyn_cast(member)) { + ctors.push_back(CD); + continue; + } + if (auto MD = dyn_cast(member)) { methods.push_back(MD); continue; @@ -3434,12 +3439,14 @@ namespace { bool hasReferenceableFields = !members.empty(); - if (hasZeroInitializableStorage) { + if (hasZeroInitializableStorage && + !Impl.SwiftContext.LangOpts.EnableCXXInterop) { // Add constructors for the struct. ctors.push_back(createDefaultConstructor(Impl, result)); } - if (hasReferenceableFields && hasMemberwiseInitializer) { + if (hasReferenceableFields && hasMemberwiseInitializer && + !Impl.SwiftContext.LangOpts.EnableCXXInterop) { // The default zero initializer suppresses the implicit value // constructor that would normally be formed, so we have to add that // explicitly as well. @@ -3491,6 +3498,28 @@ namespace { return result; } + Decl *VisitCXXRecordDecl(const clang::CXXRecordDecl *decl) { + auto &clangSema = Impl.getClangSema(); + // Make Clang define the implicit default constructor if the class needs + // it. Make sure we only do this if the class has been fully defined and + // we're not in a dependent context (this is equivalent to the logic in + // CanDeclareSpecialMemberFunction in Clang's SemaLookup.cpp). + if (decl->getDefinition() && !decl->isBeingDefined() && + !decl->isDependentContext() && + decl->needsImplicitDefaultConstructor()) { + // Casting away const here should be OK because + // SwiftDeclConverter::Visit() is in practice called with a non-const + // argument. + clang::CXXConstructorDecl *ctor = + clangSema.DeclareImplicitDefaultConstructor( + const_cast(decl)); + clangSema.DefineImplicitDefaultConstructor(clang::SourceLocation(), + ctor); + } + + return VisitRecordDecl(decl); + } + Decl *VisitClassTemplateSpecializationDecl( const clang::ClassTemplateSpecializationDecl *decl) { // `Sema::isCompleteType` will try to instantiate the class template as a @@ -3745,6 +3774,9 @@ namespace { ImportedName importedName, Optional correctSwiftName, Optional accessorInfo) { + if (decl->isDeleted()) + return nullptr; + auto dc = Impl.importDeclContextOf(decl, importedName.getEffectiveContext()); if (!dc) @@ -3753,7 +3785,6 @@ namespace { DeclName name = accessorInfo ? DeclName() : importedName.getDeclName(); auto selfIdx = importedName.getSelfIndex(); - FuncDecl *result = nullptr; ImportedType importedType; bool selfIsInOut = false; ParameterList *bodyParams = nullptr; @@ -3855,27 +3886,44 @@ namespace { if (!importedType) return nullptr; - auto resultTy = importedType.getType(); auto loc = Impl.importSourceLoc(decl->getLocation()); // FIXME: Poor location info. auto nameLoc = Impl.importSourceLoc(decl->getLocation()); - result = createFuncOrAccessor( - Impl.SwiftContext, loc, accessorInfo, name, - nameLoc, bodyParams, resultTy, - /*async*/ false, /*throws*/ false, dc, decl); - - if (!dc->isModuleScopeContext()) { - if (selfIsInOut) - result->setSelfAccessKind(SelfAccessKind::Mutating); - else - result->setSelfAccessKind(SelfAccessKind::NonMutating); - if (selfIdx) { - result->setSelfIndex(selfIdx.getValue()); - } else { - result->setStatic(); - result->setImportAsStaticMember(); + + AbstractFunctionDecl *result = nullptr; + if (auto *ctordecl = dyn_cast(decl)) { + // TODO: Is failable, throws etc. correct? + DeclName ctorName(Impl.SwiftContext, DeclBaseName::createConstructor(), + bodyParams); + result = Impl.createDeclWithClangNode( + decl, AccessLevel::Public, ctorName, loc, /*failable=*/false, + /*FailabilityLoc=*/SourceLoc(), /*Throws=*/false, + /*ThrowsLoc=*/SourceLoc(), bodyParams, /*GenericParams=*/nullptr, + dc); + } else { + auto resultTy = importedType.getType(); + + FuncDecl *func = createFuncOrAccessor( + Impl.SwiftContext, loc, accessorInfo, name, + nameLoc, bodyParams, resultTy, + /*async*/ false, /*throws*/ false, dc, decl); + result = func; + + if (!dc->isModuleScopeContext()) { + if (selfIsInOut) + func->setSelfAccessKind(SelfAccessKind::Mutating); + else + func->setSelfAccessKind(SelfAccessKind::NonMutating); + if (selfIdx) { + func->setSelfIndex(selfIdx.getValue()); + } else { + func->setStatic(); + func->setImportAsStaticMember(); + } } + // Someday, maybe this will need to be 'open' for C++ virtual methods. + func->setAccess(AccessLevel::Public); } result->setIsObjC(false); @@ -3889,8 +3937,6 @@ namespace { result->getAttrs().add(new (Impl.SwiftContext) FinalAttr(/*IsImplicit=*/true)); - // Someday, maybe this will need to be 'open' for C++ virtual methods. - result->setAccess(AccessLevel::Public); finishFuncDecl(decl, result); // If this is a compatibility stub, mark it as such. diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index cae15627ba7ef..65df344f664a0 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -1600,6 +1600,20 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, ArrayRef params; switch (D->getDeclName().getNameKind()) { case clang::DeclarationName::CXXConstructorName: + isInitializer = true; + isFunction = true; + result.info.initKind = CtorInitializerKind::Designated; + baseName = "init"; + // Add empty argument names. + if (auto ctor = dyn_cast(D)) { + for (size_t i = 0; i < ctor->param_size(); ++i) { + argumentNames.push_back(StringRef()); + } + if (ctor->isVariadic()) + argumentNames.push_back(StringRef()); + } + break; + case clang::DeclarationName::CXXConversionFunctionName: case clang::DeclarationName::CXXDestructorName: case clang::DeclarationName::CXXLiteralOperatorName: diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index a544328dc0193..3fa9365d3c04f 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -338,10 +338,12 @@ llvm::CallingConv::ID irgen::expandCallingConv(IRGenModule &IGM, static void addIndirectResultAttributes(IRGenModule &IGM, llvm::AttributeList &attrs, - unsigned paramIndex, bool allowSRet) { + unsigned paramIndex, bool allowSRet, + bool noCapture = true) { llvm::AttrBuilder b; b.addAttribute(llvm::Attribute::NoAlias); - b.addAttribute(llvm::Attribute::NoCapture); + if (noCapture) + b.addAttribute(llvm::Attribute::NoCapture); if (allowSRet) b.addAttribute(llvm::Attribute::StructRet); attrs = attrs.addAttributes(IGM.getLLVMContext(), @@ -428,7 +430,7 @@ namespace { private: void expand(SILParameterInfo param); - llvm::Type *addIndirectResult(); + llvm::Type *addIndirectResult(bool noCapture = true); SILFunctionConventions getSILFuncConventions() const { return SILFunctionConventions(FnType, IGM.getSILModule()); @@ -482,7 +484,8 @@ llvm::Type *SignatureExpansion::addIndirectResult() { auto resultType = getSILFuncConventions().getSILResultType( IGM.getMaximalTypeExpansionContext()); const TypeInfo &resultTI = IGM.getTypeInfo(resultType); - addIndirectResultAttributes(IGM, Attrs, ParamIRTypes.size(), claimSRet()); + addIndirectResultAttributes(IGM, Attrs, ParamIRTypes.size(), claimSRet(), + noCapture); addPointerParameter(resultTI.getStorageType()); return IGM.VoidTy; } @@ -1306,6 +1309,9 @@ void SignatureExpansion::expandExternalSignatureTypes() { // Convert each parameter to a Clang type. for (auto param : params) { auto clangTy = IGM.getClangType(param, FnType); + if (clangTy->isVoidType()) { + continue; + } paramTys.push_back(clangTy); } @@ -1330,8 +1336,17 @@ void SignatureExpansion::expandExternalSignatureTypes() { } // If we return indirectly, that is the first parameter type. - if (returnInfo.isIndirect()) { - addIndirectResult(); + bool formalIndirect = FnType->getNumResults() > 0 && + FnType->getSingleResult().isFormalIndirect(); + if (formalIndirect || returnInfo.isIndirect()) { + // Specify "nocapture" if we're returning the result indirectly for + // low-level ABI reasons, as the called function never sees the implicit + // output parameter. + // On the other hand, if the result is a formal indirect result in SIL, that + // means that the Clang function has an explicit output parameter (e.g. it's + // a C++ constructor), which it might capture -- so don't specify + // "nocapture" in that case. + addIndirectResult(/* noCapture = */ returnInfo.isIndirect()); } size_t firstParamToLowerNormally = 0; diff --git a/lib/IRGen/GenClangType.cpp b/lib/IRGen/GenClangType.cpp index 8bc35dd99c134..c8f38d6038a65 100644 --- a/lib/IRGen/GenClangType.cpp +++ b/lib/IRGen/GenClangType.cpp @@ -403,7 +403,19 @@ clang::CanQualType GenClangType::visitProtocolType(CanProtocolType type) { } clang::CanQualType GenClangType::visitMetatypeType(CanMetatypeType type) { - return getClangMetatypeType(getClangASTContext()); + assert(type->hasRepresentation() && + "metatype should have been assigned a representation by SIL"); + switch (type->getRepresentation()) { + case MetatypeRepresentation::Thin: + return getClangASTContext().VoidTy; + + case MetatypeRepresentation::Thick: + llvm_unreachable("thick metatypes don't have a corresponding Clang type"); + + case MetatypeRepresentation::ObjC: + return getClangMetatypeType(getClangASTContext()); + } + llvm_unreachable("bad representation"); } clang::CanQualType diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index fc175f4460017..ff07a080d7c4f 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2788,6 +2788,27 @@ class CXXMethodConventions : public CFunctionTypeConventions { return ParameterConvention::Indirect_In_Guaranteed; return ParameterConvention::Indirect_Inout; } + ResultConvention getResult(const TypeLowering &resultTL) const override { + if (isa(TheDecl)) { + // Represent the `this` pointer as an indirectly returned result. + // This gets us most of the way towards representing the ABI of a + // constructor correctly, but it's not guaranteed to be entirely correct. + // C++ constructor ABIs are complicated and can require passing additional + // "implicit" arguments that depend not only on the signature of the + // constructor but on the class on which it's defined (e.g. whether that + // class has a virtual base class). + // Effectively, we're making an assumption here that there are no implicit + // arguments and that the return type of the constructor ABI is void (and + // indeed we have no way to represent anything else here). If this assumed + // ABI doesn't match the actual ABI, we insert a thunk in IRGen. On some + // ABIs (e.g. Itanium x64), we get lucky and the ABI for a complete + // constructor call always matches the ABI we assume here. Even if the + // actual ABI doesn't match the assumed ABI, we try to get as close as + // possible to make it easy for LLVM to optimize away the thunk. + return ResultConvention::Indirect; + } + return CFunctionTypeConventions::getResult(resultTL); + } static bool classof(const Conventions *C) { return C->getKind() == ConventionsKind::CXXMethod; } diff --git a/lib/SIL/SILFunctionType.cpp b/lib/SIL/SILFunctionType.cpp new file mode 100644 index 0000000000000..e50568874f4e0 --- /dev/null +++ b/lib/SIL/SILFunctionType.cpp @@ -0,0 +1,4284 @@ +//===--- SILFunctionType.cpp - Giving SIL types to AST functions ----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines the native Swift ownership transfer conventions +// and works in concert with the importer to give the correct +// conventions to imported functions and types. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "libsil" +#include "swift/AST/AnyFunctionRef.h" +#include "swift/AST/Decl.h" +#include "swift/AST/DiagnosticsSIL.h" +#include "swift/AST/ForeignInfo.h" +#include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericSignatureBuilder.h" +#include "swift/AST/Module.h" +#include "swift/AST/ModuleLoader.h" +#include "swift/AST/ProtocolConformance.h" +#include "swift/SIL/SILModule.h" +#include "swift/SIL/SILType.h" +#include "clang/AST/Attr.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/SaveAndRestore.h" + +using namespace swift; +using namespace swift::Lowering; + +SILType SILFunctionType::substInterfaceType(SILModule &M, + SILType interfaceType) const { + // Apply pattern substitutions first, then invocation substitutions. + if (auto subs = getPatternSubstitutions()) + interfaceType = interfaceType.subst(M, subs); + if (auto subs = getInvocationSubstitutions()) + interfaceType = interfaceType.subst(M, subs); + return interfaceType; +} + +CanSILFunctionType SILFunctionType::getUnsubstitutedType(SILModule &M) const { + auto mutableThis = const_cast(this); + + // If we have no substitutions, there's nothing to do. + if (!hasPatternSubstitutions() && !hasInvocationSubstitutions()) + return CanSILFunctionType(mutableThis); + + // Otherwise, substitute the component types. + + SmallVector params; + SmallVector yields; + SmallVector results; + Optional errorResult; + + auto subs = getCombinedSubstitutions(); + auto substComponentType = [&](CanType type) { + if (!type->hasTypeParameter()) return type; + return SILType::getPrimitiveObjectType(type) + .subst(M, subs).getASTType(); + }; + + for (auto param : getParameters()) { + params.push_back(param.map(substComponentType)); + } + + for (auto yield : getYields()) { + yields.push_back(yield.map(substComponentType)); + } + + for (auto result : getResults()) { + results.push_back(result.map(substComponentType)); + } + + if (auto error = getOptionalErrorResult()) { + errorResult = error->map(substComponentType); + } + + auto signature = isPolymorphic() ? getInvocationGenericSignature() + : CanGenericSignature(); + return SILFunctionType::get(signature, + getExtInfo(), + getCoroutineKind(), + getCalleeConvention(), + params, yields, results, errorResult, + SubstitutionMap(), + SubstitutionMap(), + mutableThis->getASTContext(), + getWitnessMethodConformanceOrInvalid()); +} + +CanType SILParameterInfo::getArgumentType(SILModule &M, + const SILFunctionType *t) const { + // TODO: We should always require a function type. + if (t) + return t->substInterfaceType(M, + SILType::getPrimitiveAddressType(getInterfaceType())) + .getASTType(); + + return getInterfaceType(); +} + +CanType SILResultInfo::getReturnValueType(SILModule &M, + const SILFunctionType *t) const { + // TODO: We should always require a function type. + if (t) + return t->substInterfaceType(M, + SILType::getPrimitiveAddressType(getInterfaceType())) + .getASTType(); + + return getInterfaceType(); +} + +SILType SILFunctionType::getDirectFormalResultsType(SILModule &M) { + CanType type; + if (getNumDirectFormalResults() == 0) { + type = getASTContext().TheEmptyTupleType; + } else if (getNumDirectFormalResults() == 1) { + type = getSingleDirectFormalResult().getReturnValueType(M, this); + } else { + auto &cache = getMutableFormalResultsCache(); + if (cache) { + type = cache; + } else { + SmallVector elts; + for (auto result : getResults()) + if (!result.isFormalIndirect()) + elts.push_back(result.getReturnValueType(M, this)); + type = CanType(TupleType::get(elts, getASTContext())); + cache = type; + } + } + return SILType::getPrimitiveObjectType(type); +} + +SILType SILFunctionType::getAllResultsInterfaceType() { + CanType type; + if (getNumResults() == 0) { + type = getASTContext().TheEmptyTupleType; + } else if (getNumResults() == 1) { + type = getResults()[0].getInterfaceType(); + } else { + auto &cache = getMutableAllResultsCache(); + if (cache) { + type = cache; + } else { + SmallVector elts; + for (auto result : getResults()) + elts.push_back(result.getInterfaceType()); + type = CanType(TupleType::get(elts, getASTContext())); + cache = type; + } + } + return SILType::getPrimitiveObjectType(type); +} + +SILType SILFunctionType::getAllResultsSubstType(SILModule &M) { + return substInterfaceType(M, getAllResultsInterfaceType()); +} + +SILType SILFunctionType::getFormalCSemanticResult(SILModule &M) { + assert(getLanguage() == SILFunctionLanguage::C); + assert(getNumResults() <= 1); + return getDirectFormalResultsType(M); +} + +CanType SILFunctionType::getSelfInstanceType(SILModule &M) const { + auto selfTy = getSelfParameter().getArgumentType(M, this); + + // If this is a static method, get the instance type. + if (auto metaTy = dyn_cast(selfTy)) + return metaTy.getInstanceType(); + + return selfTy; +} + +ClassDecl * +SILFunctionType::getWitnessMethodClass(SILModule &M) const { + // TODO: When witnesses use substituted types, we'd get this from the + // substitution map. + auto selfTy = getSelfInstanceType(M); + auto genericSig = getSubstGenericSignature(); + if (auto paramTy = dyn_cast(selfTy)) { + assert(paramTy->getDepth() == 0 && paramTy->getIndex() == 0); + auto superclass = genericSig->getSuperclassBound(paramTy); + if (superclass) + return superclass->getClassOrBoundGenericClass(); + } + + return nullptr; +} + +IndexSubset * +SILFunctionType::getDifferentiabilityParameterIndices() { + assert(isDifferentiable() && "Must be a differentiable function"); + SmallVector result; + for (auto valueAndIndex : enumerate(getParameters())) + if (valueAndIndex.value().getDifferentiability() != + SILParameterDifferentiability::NotDifferentiable) + result.push_back(valueAndIndex.index()); + return IndexSubset::get(getASTContext(), getNumParameters(), result); +} + +CanSILFunctionType +SILFunctionType::getWithDifferentiability(DifferentiabilityKind kind, + IndexSubset *parameterIndices) { + assert(kind != DifferentiabilityKind::NonDifferentiable && + "Differentiability kind must be normal or linear"); + SmallVector newParameters; + for (auto paramAndIndex : enumerate(getParameters())) { + auto ¶m = paramAndIndex.value(); + unsigned index = paramAndIndex.index(); + newParameters.push_back(param.getWithDifferentiability( + index < parameterIndices->getCapacity() && + parameterIndices->contains(index) + ? SILParameterDifferentiability::DifferentiableOrNotApplicable + : SILParameterDifferentiability::NotDifferentiable)); + } + auto newExtInfo = getExtInfo().withDifferentiabilityKind(kind); + return get(getInvocationGenericSignature(), newExtInfo, getCoroutineKind(), + getCalleeConvention(), newParameters, getYields(), getResults(), + getOptionalErrorResult(), getPatternSubstitutions(), + getInvocationSubstitutions(), getASTContext(), + getWitnessMethodConformanceOrInvalid()); +} + +CanSILFunctionType SILFunctionType::getWithoutDifferentiability() { + if (!isDifferentiable()) + return CanSILFunctionType(this); + auto nondiffExtInfo = getExtInfo().withDifferentiabilityKind( + DifferentiabilityKind::NonDifferentiable); + SmallVector newParams; + for (auto ¶m : getParameters()) + newParams.push_back(param.getWithDifferentiability( + SILParameterDifferentiability::DifferentiableOrNotApplicable)); + return SILFunctionType::get(getInvocationGenericSignature(), nondiffExtInfo, + getCoroutineKind(), getCalleeConvention(), + newParams, getYields(), getResults(), + getOptionalErrorResult(), + getPatternSubstitutions(), + getInvocationSubstitutions(), + getASTContext()); +} + +/// Collects the differentiability parameters of the given original function +/// type in `diffParams`. +static void +getDifferentiabilityParameters(SILFunctionType *originalFnTy, + IndexSubset *parameterIndices, + SmallVectorImpl &diffParams) { + // Returns true if `index` is a differentiability parameter index. + auto isDiffParamIndex = [&](unsigned index) -> bool { + return index < parameterIndices->getCapacity() && + parameterIndices->contains(index); + }; + // Calculate differentiability parameter infos. + for (auto valueAndIndex : enumerate(originalFnTy->getParameters())) + if (isDiffParamIndex(valueAndIndex.index())) + diffParams.push_back(valueAndIndex.value()); +} + +/// Collects the semantic results of the given function type in +/// `originalResults`. The semantic results are formal results followed by +/// `inout` parameters, in type order. +// TODO(TF-983): Generalize to support multiple `inout` parameters. The current +// singular `inoutParam` and `isWrtInoutParameter` are hacky. +static void +getSemanticResults(SILFunctionType *functionType, IndexSubset *parameterIndices, + Optional &inoutParam, + bool &isWrtInoutParameter, + SmallVectorImpl &originalResults) { + inoutParam = None; + isWrtInoutParameter = false; + // Collect original formal results. + originalResults.append(functionType->getResults().begin(), + functionType->getResults().end()); + // Collect original `inout` parameters. + for (auto i : range(functionType->getNumParameters())) { + auto param = functionType->getParameters()[i]; + if (!param.isIndirectInOut()) + continue; + inoutParam = param; + isWrtInoutParameter = parameterIndices->contains(i); + originalResults.push_back( + SILResultInfo(param.getInterfaceType(), ResultConvention::Indirect)); + } +} + +/// Returns the differential type for the given original function type, +/// parameter indices, and result index. +static CanSILFunctionType +getAutoDiffDifferentialType(SILFunctionType *originalFnTy, + IndexSubset *parameterIndices, unsigned resultIndex, + LookupConformanceFn lookupConformance) { + auto &ctx = originalFnTy->getASTContext(); + SmallVector substGenericParams; + SmallVector substRequirements; + SmallVector substReplacements; + SmallVector substConformances; + + Optional inoutParam = None; + bool isWrtInoutParameter = false; + SmallVector originalResults; + getSemanticResults(originalFnTy, parameterIndices, inoutParam, + isWrtInoutParameter, originalResults); + + SmallVector diffParams; + getDifferentiabilityParameters(originalFnTy, parameterIndices, diffParams); + SmallVector differentialParams; + for (auto ¶m : diffParams) { + auto paramTan = + param.getInterfaceType()->getAutoDiffTangentSpace(lookupConformance); + assert(paramTan && "Parameter type does not have a tangent space?"); + auto paramTanType = paramTan->getCanonicalType(); + if (!paramTanType->hasArchetype() && !paramTanType->hasTypeParameter()) { + differentialParams.push_back( + {paramTan->getCanonicalType(), param.getConvention()}); + } else { + auto gpIndex = substGenericParams.size(); + auto gpType = CanGenericTypeParamType::get(0, gpIndex, ctx); + substGenericParams.push_back(gpType); + substReplacements.push_back(paramTanType); + differentialParams.push_back({gpType, param.getConvention()}); + } + } + SmallVector differentialResults; + if (!inoutParam || !isWrtInoutParameter) { + auto &result = originalResults[resultIndex]; + auto resultTan = + result.getInterfaceType()->getAutoDiffTangentSpace(lookupConformance); + assert(resultTan && "Result type does not have a tangent space?"); + auto resultTanType = resultTan->getCanonicalType(); + if (!resultTanType->hasArchetype() && !resultTanType->hasTypeParameter()) { + differentialResults.push_back( + {resultTan->getCanonicalType(), result.getConvention()}); + } else { + auto gpIndex = substGenericParams.size(); + auto gpType = CanGenericTypeParamType::get(0, gpIndex, ctx); + substGenericParams.push_back(gpType); + substReplacements.push_back(resultTanType); + differentialResults.push_back({gpType, result.getConvention()}); + } + } + SubstitutionMap substitutions; + if (!substGenericParams.empty()) { + auto genericSig = + GenericSignature::get(substGenericParams, substRequirements) + .getCanonicalSignature(); + substitutions = + SubstitutionMap::get(genericSig, llvm::makeArrayRef(substReplacements), + llvm::makeArrayRef(substConformances)); + } + return SILFunctionType::get( + GenericSignature(), SILFunctionType::ExtInfo(), SILCoroutineKind::None, + ParameterConvention::Direct_Guaranteed, differentialParams, {}, + differentialResults, None, substitutions, + /*invocationSubstitutions*/ SubstitutionMap(), ctx); +} + +/// Returns the pullback type for the given original function type, parameter +/// indices, and result index. +static CanSILFunctionType +getAutoDiffPullbackType(SILFunctionType *originalFnTy, + IndexSubset *parameterIndices, unsigned resultIndex, + LookupConformanceFn lookupConformance, + TypeConverter &TC) { + auto &ctx = originalFnTy->getASTContext(); + SmallVector substGenericParams; + SmallVector substRequirements; + SmallVector substReplacements; + SmallVector substConformances; + + Optional inoutParam = None; + bool isWrtInoutParameter = false; + SmallVector originalResults; + getSemanticResults(originalFnTy, parameterIndices, inoutParam, + isWrtInoutParameter, originalResults); + + // Given a type, returns its formal SIL parameter info. + auto getTangentParameterConventionForOriginalResult = + [&](CanType tanType, + ResultConvention origResConv) -> ParameterConvention { + tanType = + tanType->getCanonicalType(originalFnTy->getSubstGenericSignature()); + AbstractionPattern pattern(originalFnTy->getSubstGenericSignature(), + tanType); + auto &tl = + TC.getTypeLowering(pattern, tanType, TypeExpansionContext::minimal()); + ParameterConvention conv; + switch (origResConv) { + case ResultConvention::Owned: + case ResultConvention::Autoreleased: + if (tl.isAddressOnly()) { + conv = ParameterConvention::Indirect_In_Guaranteed; + } else { + conv = tl.isTrivial() ? ParameterConvention::Direct_Unowned + : ParameterConvention::Direct_Guaranteed; + } + break; + case ResultConvention::Unowned: + case ResultConvention::UnownedInnerPointer: + conv = ParameterConvention::Direct_Unowned; + break; + case ResultConvention::Indirect: + conv = ParameterConvention::Indirect_In_Guaranteed; + break; + } + return conv; + }; + + // Given a type, returns its formal SIL result info. + auto getTangentResultConventionForOriginalParameter = + [&](CanType tanType, + ParameterConvention origParamConv) -> ResultConvention { + tanType = + tanType->getCanonicalType(originalFnTy->getSubstGenericSignature()); + AbstractionPattern pattern(originalFnTy->getSubstGenericSignature(), + tanType); + auto &tl = + TC.getTypeLowering(pattern, tanType, TypeExpansionContext::minimal()); + ResultConvention conv; + switch (origParamConv) { + case ParameterConvention::Direct_Owned: + case ParameterConvention::Direct_Guaranteed: + case ParameterConvention::Direct_Unowned: + if (tl.isAddressOnly()) { + conv = ResultConvention::Indirect; + } else { + conv = tl.isTrivial() ? ResultConvention::Unowned + : ResultConvention::Owned; + } + break; + case ParameterConvention::Indirect_In: + case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_In_Constant: + case ParameterConvention::Indirect_In_Guaranteed: + case ParameterConvention::Indirect_InoutAliasable: + conv = ResultConvention::Indirect; + break; + } + return conv; + }; + + SmallVector pullbackParams; + if (inoutParam) { + auto paramTan = inoutParam->getInterfaceType()->getAutoDiffTangentSpace( + lookupConformance); + assert(paramTan && "Parameter type does not have a tangent space?"); + auto paramTanConvention = isWrtInoutParameter + ? inoutParam->getConvention() + : ParameterConvention::Indirect_In_Guaranteed; + auto paramTanType = paramTan->getCanonicalType(); + if (!paramTanType->hasArchetype() && !paramTanType->hasTypeParameter()) { + pullbackParams.push_back( + SILParameterInfo(paramTanType, paramTanConvention)); + } else { + auto gpIndex = substGenericParams.size(); + auto gpType = CanGenericTypeParamType::get(0, gpIndex, ctx); + substGenericParams.push_back(gpType); + substReplacements.push_back(paramTanType); + pullbackParams.push_back({gpType, paramTanConvention}); + } + } else { + auto &origRes = originalResults[resultIndex]; + auto resultTan = + origRes.getInterfaceType()->getAutoDiffTangentSpace(lookupConformance); + assert(resultTan && "Result type does not have a tangent space?"); + auto resultTanType = resultTan->getCanonicalType(); + auto paramTanConvention = getTangentParameterConventionForOriginalResult( + resultTanType, origRes.getConvention()); + if (!resultTanType->hasArchetype() && !resultTanType->hasTypeParameter()) { + auto resultTanType = resultTan->getCanonicalType(); + pullbackParams.push_back({resultTanType, paramTanConvention}); + } else { + auto gpIndex = substGenericParams.size(); + auto gpType = CanGenericTypeParamType::get(0, gpIndex, ctx); + substGenericParams.push_back(gpType); + substReplacements.push_back(resultTanType); + pullbackParams.push_back({gpType, paramTanConvention}); + } + } + SmallVector diffParams; + getDifferentiabilityParameters(originalFnTy, parameterIndices, diffParams); + SmallVector pullbackResults; + for (auto ¶m : diffParams) { + if (param.isIndirectInOut()) + continue; + auto paramTan = + param.getInterfaceType()->getAutoDiffTangentSpace(lookupConformance); + assert(paramTan && "Parameter type does not have a tangent space?"); + auto paramTanType = paramTan->getCanonicalType(); + auto resultTanConvention = getTangentResultConventionForOriginalParameter( + paramTanType, param.getConvention()); + if (!paramTanType->hasArchetype() && !paramTanType->hasTypeParameter()) { + pullbackResults.push_back({paramTanType, resultTanConvention}); + } else { + auto gpIndex = substGenericParams.size(); + auto gpType = CanGenericTypeParamType::get(0, gpIndex, ctx); + substGenericParams.push_back(gpType); + substReplacements.push_back(paramTanType); + pullbackResults.push_back({gpType, resultTanConvention}); + } + } + SubstitutionMap substitutions; + if (!substGenericParams.empty()) { + auto genericSig = + GenericSignature::get(substGenericParams, substRequirements) + .getCanonicalSignature(); + substitutions = + SubstitutionMap::get(genericSig, llvm::makeArrayRef(substReplacements), + llvm::makeArrayRef(substConformances)); + } + return SILFunctionType::get( + GenericSignature(), SILFunctionType::ExtInfo(), SILCoroutineKind::None, + ParameterConvention::Direct_Guaranteed, pullbackParams, {}, + pullbackResults, None, substitutions, + /*invocationSubstitutions*/ SubstitutionMap(), ctx); +} + +/// Constrains the `original` function type according to differentiability +/// requirements: +/// - All differentiability parameters are constrained to conform to +/// `Differentiable`. +/// - The invocation generic signature is replaced by the +/// `constrainedInvocationGenSig` argument. +static SILFunctionType *getConstrainedAutoDiffOriginalFunctionType( + SILFunctionType *original, IndexSubset *parameterIndices, + LookupConformanceFn lookupConformance, + CanGenericSignature constrainedInvocationGenSig) { + auto originalInvocationGenSig = original->getInvocationGenericSignature(); + if (!originalInvocationGenSig) { + assert(!constrainedInvocationGenSig || + constrainedInvocationGenSig->areAllParamsConcrete() && + "derivative function cannot have invocation generic signature " + "when original function doesn't"); + return original; + } + + assert(!original->getPatternSubstitutions() && + "cannot constrain substituted function type"); + if (!constrainedInvocationGenSig) + constrainedInvocationGenSig = originalInvocationGenSig; + if (!constrainedInvocationGenSig) + return original; + constrainedInvocationGenSig = + autodiff::getConstrainedDerivativeGenericSignature( + original, parameterIndices, constrainedInvocationGenSig, + lookupConformance) + .getCanonicalSignature(); + + SmallVector newParameters; + newParameters.reserve(original->getNumParameters()); + for (auto ¶m : original->getParameters()) { + newParameters.push_back( + param.getWithInterfaceType(param.getInterfaceType()->getCanonicalType( + constrainedInvocationGenSig))); + } + + SmallVector newResults; + newResults.reserve(original->getNumResults()); + for (auto &result : original->getResults()) { + newResults.push_back( + result.getWithInterfaceType(result.getInterfaceType()->getCanonicalType( + constrainedInvocationGenSig))); + } + return SILFunctionType::get( + constrainedInvocationGenSig->areAllParamsConcrete() + ? GenericSignature() + : constrainedInvocationGenSig, + original->getExtInfo(), original->getCoroutineKind(), + original->getCalleeConvention(), newParameters, original->getYields(), + newResults, original->getOptionalErrorResult(), + /*patternSubstitutions*/ SubstitutionMap(), + /*invocationSubstitutions*/ SubstitutionMap(), original->getASTContext(), + original->getWitnessMethodConformanceOrInvalid()); +} + +CanSILFunctionType SILFunctionType::getAutoDiffDerivativeFunctionType( + IndexSubset *parameterIndices, unsigned resultIndex, + AutoDiffDerivativeFunctionKind kind, TypeConverter &TC, + LookupConformanceFn lookupConformance, + CanGenericSignature derivativeFnInvocationGenSig, + bool isReabstractionThunk) { + auto &ctx = getASTContext(); + + // Look up result in cache. + auto *resultIndices = IndexSubset::get( + ctx, getNumResults() + getNumIndirectMutatingParameters(), {resultIndex}); + SILAutoDiffDerivativeFunctionKey key{this, + parameterIndices, + resultIndices, + kind, + derivativeFnInvocationGenSig, + isReabstractionThunk}; + auto insertion = + ctx.SILAutoDiffDerivativeFunctions.try_emplace(key, CanSILFunctionType()); + auto &cachedResult = insertion.first->getSecond(); + if (!insertion.second) + return cachedResult; + + SILFunctionType *constrainedOriginalFnTy = + getConstrainedAutoDiffOriginalFunctionType(this, parameterIndices, + lookupConformance, + derivativeFnInvocationGenSig); + // Compute closure type. + CanSILFunctionType closureType; + switch (kind) { + case AutoDiffDerivativeFunctionKind::JVP: + closureType = + getAutoDiffDifferentialType(constrainedOriginalFnTy, parameterIndices, + resultIndex, lookupConformance); + break; + case AutoDiffDerivativeFunctionKind::VJP: + closureType = + getAutoDiffPullbackType(constrainedOriginalFnTy, parameterIndices, + resultIndex, lookupConformance, TC); + break; + } + // Compute the derivative function parameters. + SmallVector newParameters; + newParameters.reserve(constrainedOriginalFnTy->getNumParameters()); + for (auto ¶m : constrainedOriginalFnTy->getParameters()) { + newParameters.push_back(param); + } + // Reabstraction thunks have a function-typed parameter (the function to + // reabstract) as their last parameter. Reabstraction thunk JVPs/VJPs have a + // `@differentiable` function-typed last parameter instead. + if (isReabstractionThunk) { + assert(!parameterIndices->contains(getNumParameters() - 1) && + "Function-typed parameter should not be wrt"); + auto fnParam = newParameters.back(); + auto fnParamType = dyn_cast(fnParam.getInterfaceType()); + assert(fnParamType); + auto diffFnType = fnParamType->getWithDifferentiability( + DifferentiabilityKind::Normal, parameterIndices); + newParameters.back() = fnParam.getWithInterfaceType(diffFnType); + } + + // Compute the derivative function results. + SmallVector newResults; + newResults.reserve(getNumResults() + 1); + for (auto &result : constrainedOriginalFnTy->getResults()) { + newResults.push_back(result); + } + newResults.push_back({closureType, ResultConvention::Owned}); + + // Compute the derivative function ExtInfo. + // If original function is `@convention(c)`, the derivative function should + // have `@convention(thin)`. IRGen does not support `@convention(c)` functions + // with multiple results. + auto extInfo = constrainedOriginalFnTy->getExtInfo(); + if (getRepresentation() == SILFunctionTypeRepresentation::CFunctionPointer) + extInfo = extInfo.withRepresentation(SILFunctionTypeRepresentation::Thin); + + // Put everything together to get the derivative function type. Then, store in + // cache and return. + cachedResult = SILFunctionType::get( + constrainedOriginalFnTy->getSubstGenericSignature(), extInfo, + constrainedOriginalFnTy->getCoroutineKind(), + constrainedOriginalFnTy->getCalleeConvention(), newParameters, + constrainedOriginalFnTy->getYields(), newResults, + constrainedOriginalFnTy->getOptionalErrorResult(), + /*patternSubstitutions*/ SubstitutionMap(), + /*invocationSubstitutions*/ SubstitutionMap(), + constrainedOriginalFnTy->getASTContext(), + constrainedOriginalFnTy->getWitnessMethodConformanceOrInvalid()); + return cachedResult; +} + +CanSILFunctionType SILFunctionType::getAutoDiffTransposeFunctionType( + IndexSubset *parameterIndices, Lowering::TypeConverter &TC, + LookupConformanceFn lookupConformance, + CanGenericSignature transposeFnGenSig) { + // Get the "constrained" transpose function generic signature. + if (!transposeFnGenSig) + transposeFnGenSig = getSubstGenericSignature(); + transposeFnGenSig = autodiff::getConstrainedDerivativeGenericSignature( + this, parameterIndices, transposeFnGenSig, + lookupConformance, /*isLinear*/ true) + .getCanonicalSignature(); + + // Given a type, returns its formal SIL parameter info. + auto getParameterInfoForOriginalResult = + [&](const SILResultInfo &result) -> SILParameterInfo { + AbstractionPattern pattern(transposeFnGenSig, result.getInterfaceType()); + auto &tl = TC.getTypeLowering(pattern, result.getInterfaceType(), + TypeExpansionContext::minimal()); + ParameterConvention newConv; + switch (result.getConvention()) { + case ResultConvention::Owned: + case ResultConvention::Autoreleased: + newConv = tl.isTrivial() ? ParameterConvention::Direct_Unowned + : ParameterConvention::Direct_Guaranteed; + break; + case ResultConvention::Unowned: + case ResultConvention::UnownedInnerPointer: + newConv = ParameterConvention::Direct_Unowned; + break; + case ResultConvention::Indirect: + newConv = ParameterConvention::Indirect_In_Guaranteed; + break; + } + return {result.getInterfaceType(), newConv}; + }; + + // Given a type, returns its formal SIL result info. + auto getResultInfoForOriginalParameter = + [&](const SILParameterInfo ¶m) -> SILResultInfo { + AbstractionPattern pattern(transposeFnGenSig, param.getInterfaceType()); + auto &tl = TC.getTypeLowering(pattern, param.getInterfaceType(), + TypeExpansionContext::minimal()); + ResultConvention newConv; + switch (param.getConvention()) { + case ParameterConvention::Direct_Owned: + case ParameterConvention::Direct_Guaranteed: + case ParameterConvention::Direct_Unowned: + newConv = + tl.isTrivial() ? ResultConvention::Unowned : ResultConvention::Owned; + break; + case ParameterConvention::Indirect_In: + case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_In_Constant: + case ParameterConvention::Indirect_In_Guaranteed: + case ParameterConvention::Indirect_InoutAliasable: + newConv = ResultConvention::Indirect; + break; + } + return {param.getInterfaceType(), newConv}; + }; + + SmallVector newParameters; + SmallVector newResults; + for (auto pair : llvm::enumerate(getParameters())) { + auto index = pair.index(); + auto param = pair.value(); + if (parameterIndices->contains(index)) + newResults.push_back(getResultInfoForOriginalParameter(param)); + else + newParameters.push_back(param); + } + for (auto &res : getResults()) + newParameters.push_back(getParameterInfoForOriginalResult(res)); + return SILFunctionType::get( + getInvocationGenericSignature(), getExtInfo(), getCoroutineKind(), + getCalleeConvention(), newParameters, getYields(), newResults, + getOptionalErrorResult(), getPatternSubstitutions(), + /*invocationSubstitutions*/ {}, getASTContext()); +} + +static CanType getKnownType(Optional &cacheSlot, ASTContext &C, + StringRef moduleName, StringRef typeName) { + if (!cacheSlot) { + cacheSlot = ([&] { + ModuleDecl *mod = C.getLoadedModule(C.getIdentifier(moduleName)); + if (!mod) + return CanType(); + + // Do a general qualified lookup instead of a direct lookupValue because + // some of the types we want are reexported through overlays and + // lookupValue would only give us types actually declared in the overlays + // themselves. + SmallVector decls; + mod->lookupQualified(mod, DeclNameRef(C.getIdentifier(typeName)), + NL_QualifiedDefault | NL_KnownNonCascadingDependency, + decls); + if (decls.size() != 1) + return CanType(); + + const auto *typeDecl = dyn_cast(decls.front()); + if (!typeDecl) + return CanType(); + + return typeDecl->getDeclaredInterfaceType()->getCanonicalType(); + })(); + } + CanType t = *cacheSlot; + + // It is possible that we won't find a bridging type (e.g. String) when we're + // parsing the stdlib itself. + if (t) { + LLVM_DEBUG(llvm::dbgs() << "Bridging type " << moduleName << '.' << typeName + << " mapped to "; + if (t) + t->print(llvm::dbgs()); + else + llvm::dbgs() << ""; + llvm::dbgs() << '\n'); + } + return t; +} + +#define BRIDGING_KNOWN_TYPE(BridgedModule,BridgedType) \ + CanType TypeConverter::get##BridgedType##Type() { \ + return getKnownType(BridgedType##Ty, Context, \ + #BridgedModule, #BridgedType); \ + } +#include "swift/SIL/BridgedTypes.def" + +/// Adjust a function type to have a slightly different type. +CanAnyFunctionType +Lowering::adjustFunctionType(CanAnyFunctionType t, + AnyFunctionType::ExtInfo extInfo) { + if (t->getExtInfo() == extInfo) + return t; + return CanAnyFunctionType(t->withExtInfo(extInfo)); +} + +/// Adjust a function type to have a slightly different type. +CanSILFunctionType +Lowering::adjustFunctionType(CanSILFunctionType type, + SILFunctionType::ExtInfo extInfo, + ParameterConvention callee, + ProtocolConformanceRef witnessMethodConformance) { + if (type->getExtInfo() == extInfo && type->getCalleeConvention() == callee && + type->getWitnessMethodConformanceOrInvalid() == witnessMethodConformance) + return type; + + return SILFunctionType::get(type->getInvocationGenericSignature(), + extInfo, type->getCoroutineKind(), callee, + type->getParameters(), type->getYields(), + type->getResults(), + type->getOptionalErrorResult(), + type->getPatternSubstitutions(), + type->getInvocationSubstitutions(), + type->getASTContext(), + witnessMethodConformance); +} + +CanSILFunctionType +SILFunctionType::getWithRepresentation(Representation repr) { + return getWithExtInfo(getExtInfo().withRepresentation(repr)); +} + +CanSILFunctionType SILFunctionType::getWithExtInfo(ExtInfo newExt) { + auto oldExt = getExtInfo(); + if (newExt == oldExt) + return CanSILFunctionType(this); + + auto calleeConvention = + (newExt.hasContext() + ? (oldExt.hasContext() + ? getCalleeConvention() + : Lowering::DefaultThickCalleeConvention) + : ParameterConvention::Direct_Unowned); + + return get(getInvocationGenericSignature(), newExt, getCoroutineKind(), + calleeConvention, getParameters(), getYields(), getResults(), + getOptionalErrorResult(), getPatternSubstitutions(), + getInvocationSubstitutions(), getASTContext(), + getWitnessMethodConformanceOrInvalid()); +} + +namespace { + +enum class ConventionsKind : uint8_t { + Default = 0, + DefaultBlock = 1, + ObjCMethod = 2, + CFunctionType = 3, + CFunction = 4, + ObjCSelectorFamily = 5, + Deallocator = 6, + Capture = 7, + CXXMethod = 8, +}; + +class Conventions { + ConventionsKind kind; + +protected: + virtual ~Conventions() = default; + +public: + Conventions(ConventionsKind k) : kind(k) {} + + ConventionsKind getKind() const { return kind; } + + virtual ParameterConvention + getIndirectParameter(unsigned index, + const AbstractionPattern &type, + const TypeLowering &substTL) const = 0; + virtual ParameterConvention + getDirectParameter(unsigned index, + const AbstractionPattern &type, + const TypeLowering &substTL) const = 0; + virtual ParameterConvention getCallee() const = 0; + virtual ResultConvention getResult(const TypeLowering &resultTL) const = 0; + virtual ParameterConvention + getIndirectSelfParameter(const AbstractionPattern &type) const = 0; + virtual ParameterConvention + getDirectSelfParameter(const AbstractionPattern &type) const = 0; + + // Helpers that branch based on a value ownership. + ParameterConvention getIndirect(ValueOwnership ownership, bool forSelf, + unsigned index, + const AbstractionPattern &type, + const TypeLowering &substTL) const { + switch (ownership) { + case ValueOwnership::Default: + if (forSelf) + return getIndirectSelfParameter(type); + return getIndirectParameter(index, type, substTL); + case ValueOwnership::InOut: + return ParameterConvention::Indirect_Inout; + case ValueOwnership::Shared: + return ParameterConvention::Indirect_In_Guaranteed; + case ValueOwnership::Owned: + return ParameterConvention::Indirect_In; + } + llvm_unreachable("unhandled ownership"); + } + + ParameterConvention getDirect(ValueOwnership ownership, bool forSelf, + unsigned index, const AbstractionPattern &type, + const TypeLowering &substTL) const { + switch (ownership) { + case ValueOwnership::Default: + if (forSelf) + return getDirectSelfParameter(type); + return getDirectParameter(index, type, substTL); + case ValueOwnership::InOut: + return ParameterConvention::Indirect_Inout; + case ValueOwnership::Shared: + return ParameterConvention::Direct_Guaranteed; + case ValueOwnership::Owned: + return ParameterConvention::Direct_Owned; + } + llvm_unreachable("unhandled ownership"); + } +}; + +/// A structure for building the substituted generic signature of a lowered type. +/// +/// Where the abstraction pattern for a lowered type involves substitutable types, we extract those positions +/// out into generic arguments. This signature only needs to consider the general calling convention, +/// so it can reduce away protocol and base class constraints aside from +/// `AnyObject`. We want similar-shaped generic function types to remain +/// canonically equivalent, like `(T, U) -> ()`, `(T, T) -> ()`, +/// `(U, T) -> ()` or `(T, T.A) -> ()` when given substitutions that produce +/// the same function types, so we also introduce a new generic argument for +/// each position where we see a dependent type, and canonicalize the order in +/// which we see independent generic arguments. +class SubstFunctionTypeCollector { +public: + TypeConverter &TC; + TypeExpansionContext Expansion; + CanGenericSignature GenericSig; + bool Enabled; + + SmallVector substGenericParams; + SmallVector substRequirements; + SmallVector substReplacements; + SmallVector substConformances; + + SubstFunctionTypeCollector(TypeConverter &TC, TypeExpansionContext context, + CanGenericSignature genericSig, bool enabled) + : TC(TC), Expansion(context), GenericSig(genericSig), Enabled(enabled) { + } + SubstFunctionTypeCollector(const SubstFunctionTypeCollector &) = delete; + + // Add a substitution for a fresh type variable, with the given replacement + // type and layout constraint. + CanType addSubstitution(LayoutConstraint layout, + CanType substType, + ArchetypeType *upperBound, + ArrayRef substTypeConformances) { + auto paramIndex = substGenericParams.size(); + auto param = CanGenericTypeParamType::get(0, paramIndex, TC.Context); + + // Expand the bound type according to the expansion context. + if (Expansion.shouldLookThroughOpaqueTypeArchetypes() + && substType->hasOpaqueArchetype()) { + substType = substOpaqueTypesWithUnderlyingTypes(substType, Expansion); + } + + substGenericParams.push_back(param); + substReplacements.push_back(substType); + + LayoutConstraint upperBoundLayout; + Type upperBoundSuperclass; + ArrayRef upperBoundConformances; + + // If the parameter is in a position with upper bound constraints, such + // as a generic nominal type with type constraints on its arguments, then + // preserve the constraints from that upper bound. + if (upperBound) { + upperBoundSuperclass = upperBound->getSuperclass(); + upperBoundConformances = upperBound->getConformsTo(); + upperBoundLayout = upperBound->getLayoutConstraint(); + } + + if (upperBoundSuperclass) { + upperBoundSuperclass = upperBoundSuperclass->mapTypeOutOfContext(); + substRequirements.push_back( + Requirement(RequirementKind::Superclass, param, upperBoundSuperclass)); + } + + // Preserve the layout constraint, if any, on the archetype in the + // generic signature, generalizing away some constraints that + // shouldn't affect ABI substitutability. + if (layout) { + switch (layout->getKind()) { + // Keep these layout constraints as is. + case LayoutConstraintKind::RefCountedObject: + case LayoutConstraintKind::TrivialOfAtMostSize: + break; + + case LayoutConstraintKind::UnknownLayout: + case LayoutConstraintKind::Trivial: + // These constraints don't really constrain the ABI, so we can + // eliminate them. + layout = LayoutConstraint(); + break; + + // Replace these specific constraints with one of the more general + // constraints above. + case LayoutConstraintKind::NativeClass: + case LayoutConstraintKind::Class: + case LayoutConstraintKind::NativeRefCountedObject: + // These can all be generalized to RefCountedObject. + layout = LayoutConstraint::getLayoutConstraint( + LayoutConstraintKind::RefCountedObject); + break; + + case LayoutConstraintKind::TrivialOfExactSize: + // Generalize to TrivialOfAtMostSize. + layout = LayoutConstraint::getLayoutConstraint( + LayoutConstraintKind::TrivialOfAtMostSize, + layout->getTrivialSizeInBits(), + layout->getAlignmentInBits(), + TC.Context); + break; + } + + if (layout) { + // Pick the more specific of the upper bound layout and the layout + // we chose above. + if (upperBoundLayout) { + layout = layout.merge(upperBoundLayout); + } + + substRequirements.push_back( + Requirement(RequirementKind::Layout, param, layout)); + } + } else { + (void)0; + } + + for (unsigned i : indices(upperBoundConformances)) { + auto proto = upperBoundConformances[i]; + auto conformance = substTypeConformances[i]; + substRequirements.push_back(Requirement(RequirementKind::Conformance, + param, proto->getDeclaredType())); + substConformances.push_back(conformance); + } + + return param; + } + + /// Given the destructured original abstraction pattern and substituted type for a destructured + /// parameter or result, introduce substituted generic parameters and requirements as needed for + /// the lowered type, and return the substituted type in terms of the substituted generic signature. + CanType getSubstitutedInterfaceType(AbstractionPattern origType, + CanType substType) { + if (!Enabled) + return substType; + + // Replace every dependent type we see with a fresh type variable in + // the substituted signature, substituted by the corresponding concrete + // type. + + // The entire original context could be a generic parameter. + if (origType.isTypeParameter()) { + return addSubstitution(origType.getLayoutConstraint(), substType, + nullptr, {}); + } + + auto origContextType = origType.getType(); + + // If the substituted type is a subclass of the abstraction pattern + // type, build substitutions for any type parameters in it. This only + // comes up when lowering override types for vtable entries. + auto areDifferentClasses = [](Type a, Type b) -> bool { + if (auto dynA = a->getAs()) { + a = dynA->getSelfType(); + } + if (auto dynB = b->getAs()) { + b = dynB->getSelfType(); + } + if (auto aClass = a->getClassOrBoundGenericClass()) { + if (auto bClass = b->getClassOrBoundGenericClass()) { + return aClass != bClass; + } + } + + return false; + }; + + bool substituteBindingsInSubstType = false; + if (areDifferentClasses(substType, origContextType)) { + substituteBindingsInSubstType = true; + } + if (auto substMeta = dyn_cast(substType)) { + if (auto origMeta = dyn_cast(origContextType)) { + if (areDifferentClasses(substMeta->getInstanceType(), + origMeta->getInstanceType())) { + substituteBindingsInSubstType = true; + } + } + } + + CanGenericSignature origSig = origType.getGenericSignature(); + if (substituteBindingsInSubstType) { + origContextType = substType; + origSig = TC.getCurGenericSignature(); + assert((!substType->hasTypeParameter() || origSig) && + "lowering mismatched interface types in a context without " + "a generic signature"); + } + + if (!origContextType->hasTypeParameter() + && !origContextType->hasArchetype()) { + // If the abstraction pattern doesn't have substitutable positions, nor + // should the concrete type. + assert(!substType->hasTypeParameter() + && !substType->hasArchetype()); + return substType; + } + + // Extract structural substitutions. + if (origContextType->hasTypeParameter()) { + origContextType = origSig->getGenericEnvironment() + ->mapTypeIntoContext(origContextType) + ->getCanonicalType(origSig); + } + + auto result = origContextType + ->substituteBindingsTo(substType, + [&](ArchetypeType *archetype, + CanType binding, + ArchetypeType *upperBound, + ArrayRef bindingConformances) -> CanType { + // TODO: ArchetypeType::getLayoutConstraint sometimes misses out on + // implied layout constraints. For now AnyObject is the only one we + // care about. + return addSubstitution(archetype->requiresClass() + ? LayoutConstraint::getLayoutConstraint(LayoutConstraintKind::Class) + : LayoutConstraint(), + binding, + upperBound, + bindingConformances); + }); + + assert(result && "substType was not bindable to abstraction pattern type?"); + return result; + } +}; + +/// A visitor for breaking down formal result types into a SILResultInfo +/// and possibly some number of indirect-out SILParameterInfos, +/// matching the abstraction patterns of the original type. +class DestructureResults { + TypeConverter &TC; + const Conventions &Convs; + SmallVectorImpl &Results; + TypeExpansionContext context; + SubstFunctionTypeCollector &Subst; + +public: + DestructureResults(TypeExpansionContext context, TypeConverter &TC, + const Conventions &conventions, + SmallVectorImpl &results, + SubstFunctionTypeCollector &subst) + : TC(TC), Convs(conventions), Results(results), context(context), + Subst(subst) {} + + void destructure(AbstractionPattern origType, CanType substType) { + // Recur into tuples. + if (origType.isTuple()) { + auto substTupleType = cast(substType); + for (auto eltIndex : indices(substTupleType.getElementTypes())) { + AbstractionPattern origEltType = + origType.getTupleElementType(eltIndex); + CanType substEltType = substTupleType.getElementType(eltIndex); + destructure(origEltType, substEltType); + } + return; + } + + auto substInterfaceType = Subst.getSubstitutedInterfaceType(origType, + substType); + + auto &substResultTLForConvention = TC.getTypeLowering( + origType, substInterfaceType, TypeExpansionContext::minimal()); + auto &substResultTL = TC.getTypeLowering(origType, substInterfaceType, + context); + + + // Determine the result convention. + ResultConvention convention; + if (isFormallyReturnedIndirectly(origType, substType, + substResultTLForConvention)) { + convention = ResultConvention::Indirect; + } else { + convention = Convs.getResult(substResultTLForConvention); + + // Reduce conventions for trivial types to an unowned convention. + if (substResultTL.isTrivial()) { + switch (convention) { + case ResultConvention::Indirect: + case ResultConvention::Unowned: + case ResultConvention::UnownedInnerPointer: + // Leave these as-is. + break; + + case ResultConvention::Autoreleased: + case ResultConvention::Owned: + // These aren't distinguishable from unowned for trivial types. + convention = ResultConvention::Unowned; + break; + } + } + } + + SILResultInfo result(substResultTL.getLoweredType().getASTType(), + convention); + Results.push_back(result); + } + + /// Query whether the original type is returned indirectly for the purpose + /// of reabstraction given complete lowering information about its + /// substitution. + bool isFormallyReturnedIndirectly(AbstractionPattern origType, + CanType substType, + const TypeLowering &substTL) { + // If the substituted type is returned indirectly, so must the + // unsubstituted type. + if ((origType.isTypeParameter() + && !origType.isConcreteType() + && !origType.requiresClass()) + || substTL.isAddressOnly()) { + return true; + + // If the substitution didn't change the type, then a negative + // response to the above is determinative as well. + } else if (origType.getType() == substType && + !origType.getType()->hasTypeParameter()) { + return false; + + // Otherwise, query specifically for the original type. + } else { + return SILType::isFormallyReturnedIndirectly( + origType.getType(), TC, origType.getGenericSignature()); + } + } +}; + +static bool isClangTypeMoreIndirectThanSubstType(TypeConverter &TC, + const clang::Type *clangTy, + CanType substTy) { + // A const pointer argument might have been imported as + // UnsafePointer, COpaquePointer, or a CF foreign class. + // (An ObjC class type wouldn't be const-qualified.) + if (clangTy->isPointerType() + && clangTy->getPointeeType().isConstQualified()) { + // Peek through optionals. + if (auto substObjTy = substTy.getOptionalObjectType()) + substTy = substObjTy; + + // Void pointers aren't usefully indirectable. + if (clangTy->isVoidPointerType()) + return false; + + if (auto eltTy = substTy->getAnyPointerElementType()) + return isClangTypeMoreIndirectThanSubstType(TC, + clangTy->getPointeeType().getTypePtr(), CanType(eltTy)); + + if (substTy->getAnyNominal() == + TC.Context.getOpaquePointerDecl()) + // TODO: We could conceivably have an indirect opaque ** imported + // as COpaquePointer. That shouldn't ever happen today, though, + // since we only ever indirect the 'self' parameter of functions + // imported as methods. + return false; + + if (clangTy->getPointeeType()->getAs()) { + // CF type as foreign class + if (substTy->getClassOrBoundGenericClass() && + substTy->getClassOrBoundGenericClass()->getForeignClassKind() == + ClassDecl::ForeignKind::CFType) { + return false; + } + } + + // swift_newtypes are always passed directly + if (auto typedefTy = clangTy->getAs()) { + if (typedefTy->getDecl()->getAttr()) + return false; + } + + return true; + } + return false; +} + +static bool isFormallyPassedIndirectly(TypeConverter &TC, + AbstractionPattern origType, + CanType substType, + const TypeLowering &substTL) { + // If the C type of the argument is a const pointer, but the Swift type + // isn't, treat it as indirect. + if (origType.isClangType() + && isClangTypeMoreIndirectThanSubstType(TC, origType.getClangType(), + substType)) { + return true; + } + + // If the substituted type is passed indirectly, so must the + // unsubstituted type. + if ((origType.isTypeParameter() && !origType.isConcreteType() + && !origType.requiresClass()) + || substTL.isAddressOnly()) { + return true; + + // If the substitution didn't change the type, then a negative + // response to the above is determinative as well. + } else if (origType.getType() == substType && + !origType.getType()->hasTypeParameter()) { + return false; + + // Otherwise, query specifically for the original type. + } else { + return SILType::isFormallyPassedIndirectly( + origType.getType(), TC, origType.getGenericSignature()); + } +} + +/// A visitor for turning formal input types into SILParameterInfos, matching +/// the abstraction patterns of the original type. +/// +/// If the original abstraction pattern is fully opaque, we must pass the +/// function's parameters and results indirectly, as if the original type were +/// the most general function signature (expressed entirely in generic +/// parameters) which can be substituted to equal the given signature. +/// +/// See the comment in AbstractionPattern.h for details. +class DestructureInputs { + TypeExpansionContext expansion; + TypeConverter &TC; + const Conventions &Convs; + const ForeignInfo &Foreign; + Optional> HandleForeignSelf; + SmallVectorImpl &Inputs; + SubstFunctionTypeCollector &Subst; + unsigned NextOrigParamIndex = 0; +public: + DestructureInputs(TypeExpansionContext expansion, TypeConverter &TC, + const Conventions &conventions, const ForeignInfo &foreign, + SmallVectorImpl &inputs, + SubstFunctionTypeCollector &subst) + : expansion(expansion), TC(TC), Convs(conventions), Foreign(foreign), + Inputs(inputs), Subst(subst) {} + + void destructure(AbstractionPattern origType, + CanAnyFunctionType::CanParamArrayRef params, + AnyFunctionType::ExtInfo extInfo) { + visitTopLevelParams(origType, params, extInfo); + } + +private: + /// Query whether the original type is address-only given complete + /// lowering information about its substitution. + bool isFormallyPassedIndirectly(AbstractionPattern origType, + CanType substType, + const TypeLowering &substTL) { + return ::isFormallyPassedIndirectly(TC, origType, substType, substTL); + } + + /// This is a special entry point that allows destructure inputs to handle + /// self correctly. + void visitTopLevelParams(AbstractionPattern origType, + CanAnyFunctionType::CanParamArrayRef params, + AnyFunctionType::ExtInfo extInfo) { + unsigned numEltTypes = params.size(); + + bool hasSelf = (extInfo.hasSelfParam() || Foreign.Self.isImportAsMember()); + unsigned numNonSelfParams = (hasSelf ? numEltTypes - 1 : numEltTypes); + + auto silRepresentation = extInfo.getSILRepresentation(); + + // We have to declare this out here so that the lambda scope lasts for + // the duration of the loop below. + auto handleForeignSelf = [&] { + // This is a "self", but it's not a Swift self, we handle it differently. + auto selfParam = params[numNonSelfParams]; + visit(selfParam.getValueOwnership(), + /*forSelf=*/false, + origType.getFunctionParamType(numNonSelfParams), + selfParam.getParameterType(), silRepresentation); + }; + + // If we have a foreign-self, install handleSelf as the handler. + if (Foreign.Self.isInstance()) { + assert(hasSelf && numEltTypes > 0); + // This is safe because function_ref just stores a pointer to the + // existing lambda object. + HandleForeignSelf = handleForeignSelf; + } + + // Add any leading foreign parameters. + maybeAddForeignParameters(); + + // Process all the non-self parameters. + for (unsigned i = 0; i != numNonSelfParams; ++i) { + auto ty = params[i].getParameterType(); + auto eltPattern = origType.getFunctionParamType(i); + auto flags = params[i].getParameterFlags(); + + visit(flags.getValueOwnership(), /*forSelf=*/false, eltPattern, ty, + silRepresentation, flags.isNoDerivative()); + } + + // Process the self parameter. Note that we implicitly drop self + // if this is a static foreign-self import. + if (hasSelf && !Foreign.Self.isImportAsMember()) { + auto selfParam = params[numNonSelfParams]; + auto ty = selfParam.getParameterType(); + auto eltPattern = origType.getFunctionParamType(numNonSelfParams); + auto flags = selfParam.getParameterFlags(); + + visit(flags.getValueOwnership(), /*forSelf=*/true, + eltPattern, ty, silRepresentation); + } + + // Clear the foreign-self handler for safety. + HandleForeignSelf.reset(); + } + + void visit(ValueOwnership ownership, bool forSelf, + AbstractionPattern origType, CanType substType, + SILFunctionTypeRepresentation rep, + bool isNonDifferentiable = false) { + assert(!isa(substType)); + + // Tuples get handled specially, in some cases: + CanTupleType substTupleTy = dyn_cast(substType); + if (substTupleTy && !origType.isTypeParameter()) { + assert(origType.getNumTupleElements() == substTupleTy->getNumElements()); + switch (ownership) { + case ValueOwnership::Default: + case ValueOwnership::Owned: + case ValueOwnership::Shared: + // Expand the tuple. + for (auto i : indices(substTupleTy.getElementTypes())) { + auto &elt = substTupleTy->getElement(i); + auto ownership = elt.getParameterFlags().getValueOwnership(); + assert(ownership == ValueOwnership::Default); + assert(!elt.isVararg()); + visit(ownership, forSelf, + origType.getTupleElementType(i), + CanType(elt.getRawType()), rep); + } + return; + case ValueOwnership::InOut: + // handled below + break; + } + } + + unsigned origParamIndex = NextOrigParamIndex++; + + auto substInterfaceType = + Subst.getSubstitutedInterfaceType(origType, substType); + + auto &substTLConv = TC.getTypeLowering(origType, substInterfaceType, + TypeExpansionContext::minimal()); + auto &substTL = TC.getTypeLowering(origType, substInterfaceType, expansion); + + ParameterConvention convention; + if (ownership == ValueOwnership::InOut) { + convention = ParameterConvention::Indirect_Inout; + } else if (isFormallyPassedIndirectly(origType, substType, substTLConv)) { + convention = Convs.getIndirect(ownership, forSelf, origParamIndex, + origType, substTLConv); + assert(isIndirectFormalParameter(convention)); + } else if (substTL.isTrivial()) { + convention = ParameterConvention::Direct_Unowned; + } else { + convention = Convs.getDirect(ownership, forSelf, origParamIndex, origType, + substTLConv); + assert(!isIndirectFormalParameter(convention)); + } + + SILParameterInfo param(substTL.getLoweredType().getASTType(), convention); + if (isNonDifferentiable) + param = param.getWithDifferentiability( + SILParameterDifferentiability::NotDifferentiable); + Inputs.push_back(param); + + maybeAddForeignParameters(); + } + + /// Given that we've just reached an argument index for the + /// first time, add any foreign parameters. + void maybeAddForeignParameters() { + while (maybeAddForeignErrorParameter() || + maybeAddForeignSelfParameter()) { + // Continue to see, just in case there are more parameters to add. + } + } + + bool maybeAddForeignErrorParameter() { + if (!Foreign.Error || + NextOrigParamIndex != Foreign.Error->getErrorParameterIndex()) + return false; + + auto foreignErrorTy = TC.getLoweredRValueType( + expansion, Foreign.Error->getErrorParameterType()); + + // Assume the error parameter doesn't have interesting lowering. + Inputs.push_back(SILParameterInfo(foreignErrorTy, + ParameterConvention::Direct_Unowned)); + NextOrigParamIndex++; + return true; + } + + bool maybeAddForeignSelfParameter() { + if (!Foreign.Self.isInstance() || + NextOrigParamIndex != Foreign.Self.getSelfIndex()) + return false; + + (*HandleForeignSelf)(); + return true; + } +}; + +} // end anonymous namespace + +static bool isPseudogeneric(SILDeclRef c) { + // FIXME: should this be integrated in with the Sema check that prevents + // illegal use of type arguments in pseudo-generic method bodies? + + // The implicitly-generated native initializer thunks for imported + // initializers are never pseudo-generic, because they may need + // to use their type arguments to bridge their value arguments. + if (!c.isForeign && + (c.kind == SILDeclRef::Kind::Allocator || + c.kind == SILDeclRef::Kind::Initializer) && + c.getDecl()->hasClangNode()) + return false; + + // Otherwise, we have to look at the entity's context. + DeclContext *dc; + if (c.hasDecl()) { + dc = c.getDecl()->getDeclContext(); + } else if (auto closure = c.getAbstractClosureExpr()) { + dc = closure->getParent(); + } else { + return false; + } + dc = dc->getInnermostTypeContext(); + if (!dc) return false; + + auto classDecl = dc->getSelfClassDecl(); + return (classDecl && classDecl->usesObjCGenericsModel()); +} + +/// Update the result type given the foreign error convention that we will be +/// using. +static std::pair updateResultTypeForForeignError( + ForeignErrorConvention convention, CanGenericSignature genericSig, + AbstractionPattern origResultType, CanType substFormalResultType) { + switch (convention.getKind()) { + // These conventions replace the result type. + case ForeignErrorConvention::ZeroResult: + case ForeignErrorConvention::NonZeroResult: + assert(substFormalResultType->isVoid()); + substFormalResultType = convention.getResultType(); + origResultType = AbstractionPattern(genericSig, substFormalResultType); + return {origResultType, substFormalResultType}; + + // These conventions wrap the result type in a level of optionality. + case ForeignErrorConvention::NilResult: + assert(!substFormalResultType->getOptionalObjectType()); + substFormalResultType = + OptionalType::get(substFormalResultType)->getCanonicalType(); + origResultType = + AbstractionPattern::getOptional(origResultType); + return {origResultType, substFormalResultType}; + + // These conventions don't require changes to the formal error type. + case ForeignErrorConvention::ZeroPreservedResult: + case ForeignErrorConvention::NonNilError: + return {origResultType, substFormalResultType}; + } + llvm_unreachable("unhandled kind"); +} + +/// Lower any/all capture context parameters. +/// +/// *NOTE* Currently default arg generators can not capture anything. +/// If we ever add that ability, it will be a different capture list +/// from the function to which the argument is attached. +static void +lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function, + CanGenericSignature genericSig, + TypeExpansionContext expansion, + SmallVectorImpl &inputs) { + + // NB: The generic signature may be elided from the lowered function type + // if the function is in a fully-specialized context, but we still need to + // canonicalize references to the generic parameters that may appear in + // non-canonical types in that context. We need the original generic + // signature from the AST for that. + auto origGenericSig = function.getAnyFunctionRef()->getGenericSignature(); + auto loweredCaptures = TC.getLoweredLocalCaptures(function); + + for (auto capture : loweredCaptures.getCaptures()) { + if (capture.isDynamicSelfMetadata()) { + ParameterConvention convention = ParameterConvention::Direct_Unowned; + auto dynamicSelfInterfaceType = + loweredCaptures.getDynamicSelfType()->mapTypeOutOfContext(); + + auto selfMetatype = MetatypeType::get(dynamicSelfInterfaceType, + MetatypeRepresentation::Thick); + + auto canSelfMetatype = selfMetatype->getCanonicalType(origGenericSig); + SILParameterInfo param(canSelfMetatype, convention); + inputs.push_back(param); + + continue; + } + + if (capture.isOpaqueValue()) { + OpaqueValueExpr *opaqueValue = capture.getOpaqueValue(); + auto canType = opaqueValue->getType()->mapTypeOutOfContext() + ->getCanonicalType(origGenericSig); + auto &loweredTL = + TC.getTypeLowering(AbstractionPattern(genericSig, canType), + canType, expansion); + auto loweredTy = loweredTL.getLoweredType(); + + ParameterConvention convention; + if (loweredTL.isAddressOnly()) { + convention = ParameterConvention::Indirect_In; + } else { + convention = ParameterConvention::Direct_Owned; + } + SILParameterInfo param(loweredTy.getASTType(), convention); + inputs.push_back(param); + + continue; + } + + auto *VD = capture.getDecl(); + auto type = VD->getInterfaceType(); + auto canType = type->getCanonicalType(origGenericSig); + + auto &loweredTL = + TC.getTypeLowering(AbstractionPattern(genericSig, canType), canType, + expansion); + auto loweredTy = loweredTL.getLoweredType(); + switch (TC.getDeclCaptureKind(capture, expansion)) { + case CaptureKind::Constant: { + // Constants are captured by value. + ParameterConvention convention; + assert (!loweredTL.isAddressOnly()); + if (loweredTL.isTrivial()) { + convention = ParameterConvention::Direct_Unowned; + } else { + convention = ParameterConvention::Direct_Guaranteed; + } + SILParameterInfo param(loweredTy.getASTType(), convention); + inputs.push_back(param); + break; + } + case CaptureKind::Box: { + // The type in the box is lowered in the minimal context. + auto minimalLoweredTy = + TC.getTypeLowering(AbstractionPattern(genericSig, canType), canType, + TypeExpansionContext::minimal()) + .getLoweredType(); + // Lvalues are captured as a box that owns the captured value. + auto boxTy = TC.getInterfaceBoxTypeForCapture( + VD, minimalLoweredTy.getASTType(), + /*mutable*/ true); + auto convention = ParameterConvention::Direct_Guaranteed; + auto param = SILParameterInfo(boxTy, convention); + inputs.push_back(param); + break; + } + case CaptureKind::StorageAddress: { + // Non-escaping lvalues are captured as the address of the value. + SILType ty = loweredTy.getAddressType(); + auto param = + SILParameterInfo(ty.getASTType(), + ParameterConvention::Indirect_InoutAliasable); + inputs.push_back(param); + break; + } + case CaptureKind::Immutable: { + // 'let' constants that are address-only are captured as the address of + // the value and will be consumed by the closure. + SILType ty = loweredTy.getAddressType(); + auto param = + SILParameterInfo(ty.getASTType(), + ParameterConvention::Indirect_In_Guaranteed); + inputs.push_back(param); + break; + } + } + } +} + +static AccessorDecl *getAsCoroutineAccessor(Optional constant) { + if (!constant || !constant->hasDecl()) + return nullptr;; + + auto accessor = dyn_cast(constant->getDecl()); + if (!accessor || !accessor->isCoroutine()) + return nullptr; + + return accessor; +} + +static void destructureYieldsForReadAccessor(TypeConverter &TC, + TypeExpansionContext expansion, + AbstractionPattern origType, + CanType valueType, + SmallVectorImpl &yields, + SubstFunctionTypeCollector &subst) { + // Recursively destructure tuples. + if (origType.isTuple()) { + auto valueTupleType = cast(valueType); + for (auto i : indices(valueTupleType.getElementTypes())) { + auto origEltType = origType.getTupleElementType(i); + auto valueEltType = valueTupleType.getElementType(i); + destructureYieldsForReadAccessor(TC, expansion, origEltType, valueEltType, + yields, subst); + } + return; + } + + auto valueInterfaceType = + subst.getSubstitutedInterfaceType(origType, valueType); + + auto &tlConv = + TC.getTypeLowering(origType, valueInterfaceType, + TypeExpansionContext::minimal()); + auto &tl = + TC.getTypeLowering(origType, valueInterfaceType, expansion); + auto convention = [&] { + if (isFormallyPassedIndirectly(TC, origType, valueInterfaceType, tlConv)) + return ParameterConvention::Indirect_In_Guaranteed; + if (tlConv.isTrivial()) + return ParameterConvention::Direct_Unowned; + return ParameterConvention::Direct_Guaranteed; + }(); + + yields.push_back(SILYieldInfo(tl.getLoweredType().getASTType(), + convention)); +} + +static void destructureYieldsForCoroutine(TypeConverter &TC, + TypeExpansionContext expansion, + Optional origConstant, + Optional constant, + Optional reqtSubs, + SmallVectorImpl &yields, + SILCoroutineKind &coroutineKind, + SubstFunctionTypeCollector &subst) { + assert(coroutineKind == SILCoroutineKind::None); + assert(yields.empty()); + + auto accessor = getAsCoroutineAccessor(constant); + if (!accessor) + return; + + auto origAccessor = cast(origConstant->getDecl()); + + // Coroutine accessors are implicitly yield-once coroutines, despite + // their function type. + coroutineKind = SILCoroutineKind::YieldOnce; + + // Coroutine accessors are always native, so fetch the native + // abstraction pattern. + auto origStorage = origAccessor->getStorage(); + auto origType = TC.getAbstractionPattern(origStorage, /*nonobjc*/ true) + .getReferenceStorageReferentType(); + + auto storage = accessor->getStorage(); + auto valueType = storage->getValueInterfaceType(); + if (reqtSubs) { + valueType = valueType.subst(*reqtSubs); + } + + auto canValueType = valueType->getCanonicalType( + accessor->getGenericSignature()); + + // 'modify' yields an inout of the target type. + if (accessor->getAccessorKind() == AccessorKind::Modify) { + auto valueInterfaceType = subst.getSubstitutedInterfaceType(origType, + canValueType); + auto loweredValueTy = + TC.getLoweredType(origType, valueInterfaceType, expansion); + yields.push_back(SILYieldInfo(loweredValueTy.getASTType(), + ParameterConvention::Indirect_Inout)); + return; + } + + // 'read' yields a borrowed value of the target type, destructuring + // tuples as necessary. + assert(accessor->getAccessorKind() == AccessorKind::Read); + destructureYieldsForReadAccessor(TC, expansion, origType, canValueType, + yields, subst); +} + +/// Create the appropriate SIL function type for the given formal type +/// and conventions. +/// +/// The lowering of function types is generally sensitive to the +/// declared abstraction pattern. We want to be able to take +/// advantage of declared type information in order to, say, pass +/// arguments separately and directly; but we also want to be able to +/// call functions from generic code without completely embarrassing +/// performance. Therefore, different abstraction patterns induce +/// different argument-passing conventions, and we must introduce +/// implicit reabstracting conversions where necessary to map one +/// convention to another. +/// +/// However, we actually can't reabstract arbitrary thin function +/// values while still leaving them thin, at least without costly +/// page-mapping tricks. Therefore, the representation must remain +/// consistent across all abstraction patterns. +/// +/// We could reabstract block functions in theory, but (1) we don't +/// really need to and (2) doing so would be problematic because +/// stuffing something in an Optional currently forces it to be +/// reabstracted to the most general type, which means that we'd +/// expect the wrong abstraction conventions on bridged block function +/// types. +/// +/// Therefore, we only honor abstraction patterns on thick or +/// polymorphic functions. +/// +/// FIXME: we shouldn't just drop the original abstraction pattern +/// when we can't reabstract. Instead, we should introduce +/// dynamic-indirect argument-passing conventions and map opaque +/// archetypes to that, then respect those conventions in IRGen by +/// using runtime call construction. +/// +/// \param conventions - conventions as expressed for the original type +static CanSILFunctionType getSILFunctionType( + TypeConverter &TC, TypeExpansionContext expansionContext, AbstractionPattern origType, + CanAnyFunctionType substFnInterfaceType, AnyFunctionType::ExtInfo extInfo, + const Conventions &conventions, const ForeignInfo &foreignInfo, + Optional origConstant, Optional constant, + Optional reqtSubs, + ProtocolConformanceRef witnessMethodConformance) { + // Find the generic parameters. + CanGenericSignature genericSig = + substFnInterfaceType.getOptGenericSignature(); + + Optional contextRAII; + if (genericSig) contextRAII.emplace(TC, genericSig); + + // Per above, only fully honor opaqueness in the abstraction pattern + // for thick or polymorphic functions. We don't need to worry about + // non-opaque patterns because the type-checker forbids non-thick + // function types from having generic parameters or results. + if (origType.isTypeParameter() && + substFnInterfaceType->getExtInfo().getSILRepresentation() + != SILFunctionType::Representation::Thick && + isa(substFnInterfaceType)) { + origType = AbstractionPattern(genericSig, + substFnInterfaceType); + } + + // Map 'throws' to the appropriate error convention. + Optional errorResult; + assert((!foreignInfo.Error || substFnInterfaceType->getExtInfo().throws()) && + "foreignError was set but function type does not throw?"); + if (substFnInterfaceType->getExtInfo().throws() && !foreignInfo.Error) { + assert(!origType.isForeign() && + "using native Swift error convention for foreign type!"); + SILType exnType = SILType::getExceptionType(TC.Context); + assert(exnType.isObject()); + errorResult = SILResultInfo(exnType.getASTType(), + ResultConvention::Owned); + } + + // Lower the result type. + AbstractionPattern origResultType = origType.getFunctionResultType(); + CanType substFormalResultType = substFnInterfaceType.getResult(); + + // If we have a foreign error convention, restore the original result type. + if (auto convention = foreignInfo.Error) { + std::tie(origResultType, substFormalResultType) = + updateResultTypeForForeignError(*convention, genericSig, origResultType, + substFormalResultType); + } + + bool shouldBuildSubstFunctionType = [&]{ + if (!TC.Context.LangOpts.EnableSubstSILFunctionTypesForFunctionValues) + return false; + + // We always use substituted function types for coroutines that are + // being lowered in the context of another coroutine, which is to say, + // for class override thunks. This is required to make the yields + // match in abstraction to the base method's yields, which is necessary + // to make the extracted continuation-function signatures match. + if (constant != origConstant && getAsCoroutineAccessor(constant)) + return true; + + // We don't currently use substituted function types for generic function + // type lowering, though we should for generic methods on classes and + // protocols. + if (genericSig) + return false; + + // We only currently use substituted function types for function values, + // which will have standard thin or thick representation. (Per the previous + // comment, it would be useful to do so for generic methods on classes and + // protocols too.) + auto rep = extInfo.getSILRepresentation(); + return (rep == SILFunctionTypeRepresentation::Thick || + rep == SILFunctionTypeRepresentation::Thin); + }(); + + SubstFunctionTypeCollector subst(TC, expansionContext, genericSig, + shouldBuildSubstFunctionType); + + // Destructure the input tuple type. + SmallVector inputs; + { + DestructureInputs destructurer(expansionContext, TC, conventions, + foreignInfo, inputs, subst); + destructurer.destructure(origType, + substFnInterfaceType.getParams(), + extInfo); + } + + // Destructure the coroutine yields. + SILCoroutineKind coroutineKind = SILCoroutineKind::None; + SmallVector yields; + destructureYieldsForCoroutine(TC, expansionContext, origConstant, constant, + reqtSubs, yields, coroutineKind, subst); + + // Destructure the result tuple type. + SmallVector results; + { + DestructureResults destructurer(expansionContext, TC, conventions, + results, subst); + destructurer.destructure(origResultType, substFormalResultType); + } + + // Lower the capture context parameters, if any. + if (constant && constant->getAnyFunctionRef()) { + auto expansion = TypeExpansionContext::maximal( + expansionContext.getContext(), expansionContext.isWholeModuleContext()); + if (constant->isSerialized()) + expansion = TypeExpansionContext::minimal(); + lowerCaptureContextParameters(TC, *constant, genericSig, expansion, inputs); + } + + auto calleeConvention = ParameterConvention::Direct_Unowned; + if (extInfo.hasContext()) + calleeConvention = conventions.getCallee(); + + bool pseudogeneric = genericSig && constant + ? isPseudogeneric(*constant) + : false; + + // NOTE: SILFunctionType::ExtInfo doesn't track everything that + // AnyFunctionType::ExtInfo tracks. For example: 'throws' or 'auto-closure' + auto silExtInfo = SILFunctionType::ExtInfo() + .withRepresentation(extInfo.getSILRepresentation()) + .withIsPseudogeneric(pseudogeneric) + .withNoEscape(extInfo.isNoEscape()) + .withDifferentiabilityKind(extInfo.getDifferentiabilityKind()); + + // Build the substituted generic signature we extracted. + SubstitutionMap substitutions; + if (subst.Enabled) { + if (!subst.substGenericParams.empty()) { + auto subSig = GenericSignature::get(subst.substGenericParams, + subst.substRequirements) + .getCanonicalSignature(); + substitutions = SubstitutionMap::get(subSig, + llvm::makeArrayRef(subst.substReplacements), + llvm::makeArrayRef(subst.substConformances)); + } + } + + return SILFunctionType::get(genericSig, silExtInfo, coroutineKind, + calleeConvention, inputs, yields, + results, errorResult, + substitutions, SubstitutionMap(), + TC.Context, witnessMethodConformance); +} + +//===----------------------------------------------------------------------===// +// Deallocator SILFunctionTypes +//===----------------------------------------------------------------------===// + +namespace { + +// The convention for general deallocators. +struct DeallocatorConventions : Conventions { + DeallocatorConventions() : Conventions(ConventionsKind::Deallocator) {} + + ParameterConvention getIndirectParameter(unsigned index, + const AbstractionPattern &type, + const TypeLowering &substTL) const override { + llvm_unreachable("Deallocators do not have indirect parameters"); + } + + ParameterConvention getDirectParameter(unsigned index, + const AbstractionPattern &type, + const TypeLowering &substTL) const override { + llvm_unreachable("Deallocators do not have non-self direct parameters"); + } + + ParameterConvention getCallee() const override { + llvm_unreachable("Deallocators do not have callees"); + } + + ResultConvention getResult(const TypeLowering &tl) const override { + // TODO: Put an unreachable here? + return ResultConvention::Owned; + } + + ParameterConvention + getDirectSelfParameter(const AbstractionPattern &type) const override { + // TODO: Investigate whether or not it is + return ParameterConvention::Direct_Owned; + } + + ParameterConvention + getIndirectSelfParameter(const AbstractionPattern &type) const override { + llvm_unreachable("Deallocators do not have indirect self parameters"); + } + + static bool classof(const Conventions *C) { + return C->getKind() == ConventionsKind::Deallocator; + } +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Default Convention FunctionTypes +//===----------------------------------------------------------------------===// + +namespace { + +enum class NormalParameterConvention { Owned, Guaranteed }; + +/// The default Swift conventions. +class DefaultConventions : public Conventions { + NormalParameterConvention normalParameterConvention; + +public: + DefaultConventions(NormalParameterConvention normalParameterConvention) + : Conventions(ConventionsKind::Default), + normalParameterConvention(normalParameterConvention) {} + + bool isNormalParameterConventionGuaranteed() const { + return normalParameterConvention == NormalParameterConvention::Guaranteed; + } + + ParameterConvention getIndirectParameter(unsigned index, + const AbstractionPattern &type, + const TypeLowering &substTL) const override { + if (isNormalParameterConventionGuaranteed()) { + return ParameterConvention::Indirect_In_Guaranteed; + } + return ParameterConvention::Indirect_In; + } + + ParameterConvention getDirectParameter(unsigned index, + const AbstractionPattern &type, + const TypeLowering &substTL) const override { + if (isNormalParameterConventionGuaranteed()) + return ParameterConvention::Direct_Guaranteed; + return ParameterConvention::Direct_Owned; + } + + ParameterConvention getCallee() const override { + return DefaultThickCalleeConvention; + } + + ResultConvention getResult(const TypeLowering &tl) const override { + return ResultConvention::Owned; + } + + ParameterConvention + getDirectSelfParameter(const AbstractionPattern &type) const override { + return ParameterConvention::Direct_Guaranteed; + } + + ParameterConvention + getIndirectSelfParameter(const AbstractionPattern &type) const override { + return ParameterConvention::Indirect_In_Guaranteed; + } + + static bool classof(const Conventions *C) { + return C->getKind() == ConventionsKind::Default; + } +}; + +/// The default conventions for Swift initializing constructors. +/// +/// Initializing constructors take all parameters (including) self at +1. This +/// is because: +/// +/// 1. We are likely to be initializing fields of self implying that the +/// parameters are likely to be forwarded into memory without further +/// copies. +/// 2. Initializers must take 'self' at +1, since they will return it back +/// at +1, and may chain onto Objective-C initializers that replace the +/// instance. +struct DefaultInitializerConventions : DefaultConventions { + DefaultInitializerConventions() + : DefaultConventions(NormalParameterConvention::Owned) {} + + /// Initializers must take 'self' at +1, since they will return it back at +1, + /// and may chain onto Objective-C initializers that replace the instance. + ParameterConvention + getDirectSelfParameter(const AbstractionPattern &type) const override { + return ParameterConvention::Direct_Owned; + } + + ParameterConvention + getIndirectSelfParameter(const AbstractionPattern &type) const override { + return ParameterConvention::Indirect_In; + } +}; + +/// The convention used for allocating inits. Allocating inits take their normal +/// parameters at +1 and do not have a self parameter. +struct DefaultAllocatorConventions : DefaultConventions { + DefaultAllocatorConventions() + : DefaultConventions(NormalParameterConvention::Owned) {} + + ParameterConvention + getDirectSelfParameter(const AbstractionPattern &type) const override { + llvm_unreachable("Allocating inits do not have self parameters"); + } + + ParameterConvention + getIndirectSelfParameter(const AbstractionPattern &type) const override { + llvm_unreachable("Allocating inits do not have self parameters"); + } +}; + +/// The default conventions for Swift setter acccessors. +/// +/// These take self at +0, but all other parameters at +1. This is because we +/// assume that setter parameters are likely to be values to be forwarded into +/// memory. Thus by passing in the +1 value, we avoid a potential copy in that +/// case. +struct DefaultSetterConventions : DefaultConventions { + DefaultSetterConventions() + : DefaultConventions(NormalParameterConvention::Owned) {} +}; + +/// The default conventions for ObjC blocks. +struct DefaultBlockConventions : Conventions { + DefaultBlockConventions() : Conventions(ConventionsKind::DefaultBlock) {} + + ParameterConvention getIndirectParameter(unsigned index, + const AbstractionPattern &type, + const TypeLowering &substTL) const override { + llvm_unreachable("indirect block parameters unsupported"); + } + + ParameterConvention getDirectParameter(unsigned index, + const AbstractionPattern &type, + const TypeLowering &substTL) const override { + return ParameterConvention::Direct_Unowned; + } + + ParameterConvention getCallee() const override { + return ParameterConvention::Direct_Unowned; + } + + ResultConvention getResult(const TypeLowering &substTL) const override { + return ResultConvention::Autoreleased; + } + + ParameterConvention + getDirectSelfParameter(const AbstractionPattern &type) const override { + llvm_unreachable("objc blocks do not have a self parameter"); + } + + ParameterConvention + getIndirectSelfParameter(const AbstractionPattern &type) const override { + llvm_unreachable("objc blocks do not have a self parameter"); + } + + static bool classof(const Conventions *C) { + return C->getKind() == ConventionsKind::DefaultBlock; + } +}; + +} // end anonymous namespace + +static CanSILFunctionType +getSILFunctionTypeForAbstractCFunction(TypeConverter &TC, + AbstractionPattern origType, + CanAnyFunctionType substType, + AnyFunctionType::ExtInfo extInfo, + Optional constant); + +static CanSILFunctionType getNativeSILFunctionType( + TypeConverter &TC, TypeExpansionContext context, AbstractionPattern origType, + CanAnyFunctionType substInterfaceType, AnyFunctionType::ExtInfo extInfo, + Optional origConstant, Optional constant, + Optional reqtSubs, + ProtocolConformanceRef witnessMethodConformance) { + assert(bool(origConstant) == bool(constant)); + switch (extInfo.getSILRepresentation()) { + case SILFunctionType::Representation::Block: + case SILFunctionType::Representation::CFunctionPointer: + return getSILFunctionTypeForAbstractCFunction(TC, origType, + substInterfaceType, + extInfo, constant); + + case SILFunctionType::Representation::Thin: + case SILFunctionType::Representation::ObjCMethod: + case SILFunctionType::Representation::Thick: + case SILFunctionType::Representation::Method: + case SILFunctionType::Representation::Closure: + case SILFunctionType::Representation::WitnessMethod: { + switch (constant ? constant->kind : SILDeclRef::Kind::Func) { + case SILDeclRef::Kind::Initializer: + case SILDeclRef::Kind::EnumElement: + return getSILFunctionType(TC, context, origType, substInterfaceType, + extInfo, DefaultInitializerConventions(), + ForeignInfo(), origConstant, constant, reqtSubs, + witnessMethodConformance); + case SILDeclRef::Kind::Allocator: + return getSILFunctionType(TC, context, origType, substInterfaceType, + extInfo, DefaultAllocatorConventions(), + ForeignInfo(), origConstant, constant, reqtSubs, + witnessMethodConformance); + case SILDeclRef::Kind::Func: + // If we have a setter, use the special setter convention. This ensures + // that we take normal parameters at +1. + if (constant && constant->isSetter()) { + return getSILFunctionType(TC, context, origType, substInterfaceType, + extInfo, DefaultSetterConventions(), + ForeignInfo(), origConstant, constant, + reqtSubs, witnessMethodConformance); + } + LLVM_FALLTHROUGH; + case SILDeclRef::Kind::Destroyer: + case SILDeclRef::Kind::GlobalAccessor: + case SILDeclRef::Kind::DefaultArgGenerator: + case SILDeclRef::Kind::StoredPropertyInitializer: + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + case SILDeclRef::Kind::IVarInitializer: + case SILDeclRef::Kind::IVarDestroyer: { + auto conv = DefaultConventions(NormalParameterConvention::Guaranteed); + return getSILFunctionType(TC, context, origType, substInterfaceType, + extInfo, conv, ForeignInfo(), origConstant, + constant, reqtSubs, witnessMethodConformance); + } + case SILDeclRef::Kind::Deallocator: + return getSILFunctionType(TC, context, origType, substInterfaceType, + extInfo, DeallocatorConventions(), + ForeignInfo(), origConstant, constant, reqtSubs, + witnessMethodConformance); + } + } + } + + llvm_unreachable("Unhandled SILDeclRefKind in switch."); +} + +CanSILFunctionType swift::getNativeSILFunctionType( + TypeConverter &TC, TypeExpansionContext context, + AbstractionPattern origType, CanAnyFunctionType substType, + Optional origConstant, Optional substConstant, + Optional reqtSubs, + ProtocolConformanceRef witnessMethodConformance) { + AnyFunctionType::ExtInfo extInfo; + + // Preserve type information from the original type if possible. + if (auto origFnType = origType.getAs()) { + extInfo = origFnType->getExtInfo(); + + // Otherwise, preserve function type attributes from the substituted type. + } else { + extInfo = substType->getExtInfo(); + } + + return ::getNativeSILFunctionType(TC, context, origType, substType, extInfo, + origConstant, substConstant, reqtSubs, + witnessMethodConformance); +} + +//===----------------------------------------------------------------------===// +// Foreign SILFunctionTypes +//===----------------------------------------------------------------------===// + +static bool isCFTypedef(const TypeLowering &tl, clang::QualType type) { + // If we imported a C pointer type as a non-trivial type, it was + // a foreign class type. + return !tl.isTrivial() && type->isPointerType(); +} + +/// Given nothing but a formal C parameter type that's passed +/// indirectly, deduce the convention for it. +/// +/// Generally, whether the parameter is +1 is handled before this. +static ParameterConvention getIndirectCParameterConvention(clang::QualType type) { + // Non-trivial C++ types would be Indirect_Inout (at least in Itanium). + // A trivial const * parameter in C should be considered @in. + return ParameterConvention::Indirect_In; +} + +/// Given a C parameter declaration whose type is passed indirectly, +/// deduce the convention for it. +/// +/// Generally, whether the parameter is +1 is handled before this. +static ParameterConvention +getIndirectCParameterConvention(const clang::ParmVarDecl *param) { + return getIndirectCParameterConvention(param->getType()); +} + +/// Given nothing but a formal C parameter type that's passed +/// directly, deduce the convention for it. +/// +/// Generally, whether the parameter is +1 is handled before this. +static ParameterConvention getDirectCParameterConvention(clang::QualType type) { + return ParameterConvention::Direct_Unowned; +} + +/// Given a C parameter declaration whose type is passed directly, +/// deduce the convention for it. +static ParameterConvention +getDirectCParameterConvention(const clang::ParmVarDecl *param) { + if (param->hasAttr() || + param->hasAttr()) + return ParameterConvention::Direct_Owned; + return getDirectCParameterConvention(param->getType()); +} + +// FIXME: that should be Direct_Guaranteed +const auto ObjCSelfConvention = ParameterConvention::Direct_Unowned; + +namespace { + +class ObjCMethodConventions : public Conventions { + const clang::ObjCMethodDecl *Method; + +public: + const clang::ObjCMethodDecl *getMethod() const { return Method; } + + ObjCMethodConventions(const clang::ObjCMethodDecl *method) + : Conventions(ConventionsKind::ObjCMethod), Method(method) {} + + ParameterConvention getIndirectParameter(unsigned index, + const AbstractionPattern &type, + const TypeLowering &substTL) const override { + return getIndirectCParameterConvention(Method->param_begin()[index]); + } + + ParameterConvention getDirectParameter(unsigned index, + const AbstractionPattern &type, + const TypeLowering &substTL) const override { + return getDirectCParameterConvention(Method->param_begin()[index]); + } + + ParameterConvention getCallee() const override { + // Always thin. + return ParameterConvention::Direct_Unowned; + } + + /// Given that a method returns a CF type, infer its method + /// family. Unfortunately, Clang's getMethodFamily() never + /// considers a method to be in a special family if its result + /// doesn't satisfy isObjCRetainable(). + clang::ObjCMethodFamily getMethodFamilyForCFResult() const { + // Trust an explicit attribute. + if (auto attr = Method->getAttr()) { + switch (attr->getFamily()) { + case clang::ObjCMethodFamilyAttr::OMF_None: + return clang::OMF_None; + case clang::ObjCMethodFamilyAttr::OMF_alloc: + return clang::OMF_alloc; + case clang::ObjCMethodFamilyAttr::OMF_copy: + return clang::OMF_copy; + case clang::ObjCMethodFamilyAttr::OMF_init: + return clang::OMF_init; + case clang::ObjCMethodFamilyAttr::OMF_mutableCopy: + return clang::OMF_mutableCopy; + case clang::ObjCMethodFamilyAttr::OMF_new: + return clang::OMF_new; + } + llvm_unreachable("bad attribute value"); + } + + return Method->getSelector().getMethodFamily(); + } + + bool isImplicitPlusOneCFResult() const { + switch (getMethodFamilyForCFResult()) { + case clang::OMF_None: + case clang::OMF_dealloc: + case clang::OMF_finalize: + case clang::OMF_retain: + case clang::OMF_release: + case clang::OMF_autorelease: + case clang::OMF_retainCount: + case clang::OMF_self: + case clang::OMF_initialize: + case clang::OMF_performSelector: + return false; + + case clang::OMF_alloc: + case clang::OMF_new: + case clang::OMF_mutableCopy: + case clang::OMF_copy: + return true; + + case clang::OMF_init: + return Method->isInstanceMethod(); + } + llvm_unreachable("bad method family"); + } + + ResultConvention getResult(const TypeLowering &tl) const override { + // If we imported the result as something trivial, we need to + // use one of the unowned conventions. + if (tl.isTrivial()) { + if (Method->hasAttr()) + return ResultConvention::UnownedInnerPointer; + + auto type = tl.getLoweredType(); + if (type.unwrapOptionalType().getStructOrBoundGenericStruct() + == type.getASTContext().getUnmanagedDecl()) + return ResultConvention::UnownedInnerPointer; + return ResultConvention::Unowned; + } + + // Otherwise, the return type had better be a retainable object pointer. + auto resultType = Method->getReturnType(); + assert(resultType->isObjCRetainableType() || isCFTypedef(tl, resultType)); + + // If it's retainable for the purposes of ObjC ARC, we can trust + // the presence of ns_returns_retained, because Clang will add + // that implicitly based on the method family. + if (resultType->isObjCRetainableType()) { + if (Method->hasAttr()) + return ResultConvention::Owned; + return ResultConvention::Autoreleased; + } + + // Otherwise, it's a CF return type, which unfortunately means + // we can't just trust getMethodFamily(). We should really just + // change that, but that's an annoying change to make to Clang + // right now. + assert(isCFTypedef(tl, resultType)); + + // Trust the explicit attributes. + if (Method->hasAttr()) + return ResultConvention::Owned; + if (Method->hasAttr()) + return ResultConvention::Autoreleased; + + // Otherwise, infer based on the method family. + if (isImplicitPlusOneCFResult()) + return ResultConvention::Owned; + return ResultConvention::Autoreleased; + } + + ParameterConvention + getDirectSelfParameter(const AbstractionPattern &type) const override { + if (Method->hasAttr()) + return ParameterConvention::Direct_Owned; + + // The caller is supposed to take responsibility for ensuring + // that 'self' survives a method call. + return ObjCSelfConvention; + } + + ParameterConvention + getIndirectSelfParameter(const AbstractionPattern &type) const override { + llvm_unreachable("objc methods do not support indirect self parameters"); + } + + static bool classof(const Conventions *C) { + return C->getKind() == ConventionsKind::ObjCMethod; + } +}; + +/// Conventions based on a C function type. +class CFunctionTypeConventions : public Conventions { + const clang::FunctionType *FnType; + + clang::QualType getParamType(unsigned i) const { + return FnType->castAs()->getParamType(i); + } + +protected: + /// Protected constructor for subclasses to override the kind passed to the + /// super class. + CFunctionTypeConventions(ConventionsKind kind, + const clang::FunctionType *type) + : Conventions(kind), FnType(type) {} + +public: + CFunctionTypeConventions(const clang::FunctionType *type) + : Conventions(ConventionsKind::CFunctionType), FnType(type) {} + + ParameterConvention getIndirectParameter(unsigned index, + const AbstractionPattern &type, + const TypeLowering &substTL) const override { + return getIndirectCParameterConvention(getParamType(index)); + } + + ParameterConvention getDirectParameter(unsigned index, + const AbstractionPattern &type, + const TypeLowering &substTL) const override { + if (cast(FnType)->isParamConsumed(index)) + return ParameterConvention::Direct_Owned; + return getDirectCParameterConvention(getParamType(index)); + } + + ParameterConvention getCallee() const override { + // FIXME: blocks should be Direct_Guaranteed. + return ParameterConvention::Direct_Unowned; + } + + ResultConvention getResult(const TypeLowering &tl) const override { + if (tl.isTrivial()) + return ResultConvention::Unowned; + if (FnType->getExtInfo().getProducesResult()) + return ResultConvention::Owned; + return ResultConvention::Autoreleased; + } + + ParameterConvention + getDirectSelfParameter(const AbstractionPattern &type) const override { + llvm_unreachable("c function types do not have a self parameter"); + } + + ParameterConvention + getIndirectSelfParameter(const AbstractionPattern &type) const override { + llvm_unreachable("c function types do not have a self parameter"); + } + + static bool classof(const Conventions *C) { + return C->getKind() == ConventionsKind::CFunctionType; + } +}; + +/// Conventions based on C function declarations. +class CFunctionConventions : public CFunctionTypeConventions { + using super = CFunctionTypeConventions; + const clang::FunctionDecl *TheDecl; +public: + CFunctionConventions(const clang::FunctionDecl *decl) + : CFunctionTypeConventions(ConventionsKind::CFunction, + decl->getType()->castAs()), + TheDecl(decl) {} + + ParameterConvention getDirectParameter(unsigned index, + const AbstractionPattern &type, + const TypeLowering &substTL) const override { + if (auto param = TheDecl->getParamDecl(index)) + if (param->hasAttr()) + return ParameterConvention::Direct_Owned; + return super::getDirectParameter(index, type, substTL); + } + + ResultConvention getResult(const TypeLowering &tl) const override { + if (isCFTypedef(tl, TheDecl->getReturnType())) { + // The CF attributes aren't represented in the type, so we need + // to check them here. + if (TheDecl->hasAttr()) { + return ResultConvention::Owned; + } else if (TheDecl->hasAttr()) { + // Probably not actually autoreleased. + return ResultConvention::Autoreleased; + + // The CF Create/Copy rule only applies to functions that return + // a CF-runtime type; it does not apply to methods, and it does + // not apply to functions returning ObjC types. + } else if (clang::ento::coreFoundation::followsCreateRule(TheDecl)) { + return ResultConvention::Owned; + } else { + return ResultConvention::Autoreleased; + } + } + + // Otherwise, fall back on the ARC annotations, which are part + // of the type. + return super::getResult(tl); + } + + static bool classof(const Conventions *C) { + return C->getKind() == ConventionsKind::CFunction; + } +}; + +/// Conventions based on C++ method declarations. +class CXXMethodConventions : public CFunctionTypeConventions { + using super = CFunctionTypeConventions; + const clang::CXXMethodDecl *TheDecl; + +public: + CXXMethodConventions(const clang::CXXMethodDecl *decl) + : CFunctionTypeConventions( + ConventionsKind::CXXMethod, + decl->getType()->castAs()), + TheDecl(decl) {} + ParameterConvention + getIndirectSelfParameter(const AbstractionPattern &type) const override { + if (TheDecl->isConst()) + return ParameterConvention::Indirect_In_Guaranteed; + return ParameterConvention::Indirect_Inout; + } + ResultConvention getResult(const TypeLowering &resultTL) const override { + if (dyn_cast(TheDecl)) { + return ResultConvention::Indirect; + } + return CFunctionTypeConventions::getResult(resultTL); + } + static bool classof(const Conventions *C) { + return C->getKind() == ConventionsKind::CXXMethod; + } +}; + +} // end anonymous namespace + +/// Given that we have an imported Clang declaration, deduce the +/// ownership conventions for calling it and build the SILFunctionType. +static CanSILFunctionType +getSILFunctionTypeForClangDecl(TypeConverter &TC, const clang::Decl *clangDecl, + CanAnyFunctionType origType, + CanAnyFunctionType substInterfaceType, + AnyFunctionType::ExtInfo extInfo, + const ForeignInfo &foreignInfo, + Optional constant) { + if (auto method = dyn_cast(clangDecl)) { + auto origPattern = + AbstractionPattern::getObjCMethod(origType, method, foreignInfo.Error); + return getSILFunctionType(TC, TypeExpansionContext::minimal(), origPattern, + substInterfaceType, extInfo, + ObjCMethodConventions(method), foreignInfo, + constant, constant, None, + ProtocolConformanceRef()); + } + + if (auto method = dyn_cast(clangDecl)) { + AbstractionPattern origPattern = + AbstractionPattern::getCXXMethod(origType, method); + auto conventions = CXXMethodConventions(method); + return getSILFunctionType(TC, TypeExpansionContext::minimal(), origPattern, + substInterfaceType, extInfo, conventions, + foreignInfo, constant, constant, None, + ProtocolConformanceRef()); + } + + if (auto func = dyn_cast(clangDecl)) { + auto clangType = func->getType().getTypePtr(); + AbstractionPattern origPattern = + foreignInfo.Self.isImportAsMember() + ? AbstractionPattern::getCFunctionAsMethod(origType, clangType, + foreignInfo.Self) + : AbstractionPattern(origType, clangType); + return getSILFunctionType(TC, TypeExpansionContext::minimal(), origPattern, + substInterfaceType, extInfo, + CFunctionConventions(func), foreignInfo, constant, + constant, None, ProtocolConformanceRef()); + } + + llvm_unreachable("call to unknown kind of C function"); +} + +static CanSILFunctionType +getSILFunctionTypeForAbstractCFunction(TypeConverter &TC, + AbstractionPattern origType, + CanAnyFunctionType substType, + AnyFunctionType::ExtInfo extInfo, + Optional constant) { + if (origType.isClangType()) { + auto clangType = origType.getClangType(); + const clang::FunctionType *fnType; + if (auto blockPtr = clangType->getAs()) { + fnType = blockPtr->getPointeeType()->castAs(); + } else if (auto ptr = clangType->getAs()) { + fnType = ptr->getPointeeType()->getAs(); + } else if (auto ref = clangType->getAs()) { + fnType = ref->getPointeeType()->getAs(); + } else if (auto fn = clangType->getAs()) { + fnType = fn; + } else { + llvm_unreachable("unexpected type imported as a function type"); + } + if (fnType) { + return getSILFunctionType( + TC, TypeExpansionContext::minimal(), origType, substType, extInfo, + CFunctionTypeConventions(fnType), ForeignInfo(), constant, constant, + None, ProtocolConformanceRef()); + } + } + + // TODO: Ought to support captures in block funcs. + return getSILFunctionType(TC, TypeExpansionContext::minimal(), origType, + substType, extInfo, DefaultBlockConventions(), + ForeignInfo(), constant, constant, None, + ProtocolConformanceRef()); +} + +/// Try to find a clang method declaration for the given function. +static const clang::Decl *findClangMethod(ValueDecl *method) { + if (auto *methodFn = dyn_cast(method)) { + if (auto *decl = methodFn->getClangDecl()) + return decl; + + if (auto overridden = methodFn->getOverriddenDecl()) + return findClangMethod(overridden); + } + + if (auto *constructor = dyn_cast(method)) { + if (auto *decl = constructor->getClangDecl()) + return decl; + } + + return nullptr; +} + +//===----------------------------------------------------------------------===// +// Selector Family SILFunctionTypes +//===----------------------------------------------------------------------===// + +/// Derive the ObjC selector family from an identifier. +/// +/// Note that this will never derive the Init family, which is too dangerous +/// to leave to chance. Swift functions starting with "init" are always +/// emitted as if they are part of the "none" family. +static ObjCSelectorFamily getObjCSelectorFamily(ObjCSelector name) { + auto result = name.getSelectorFamily(); + + if (result == ObjCSelectorFamily::Init) + return ObjCSelectorFamily::None; + + return result; +} + +/// Get the ObjC selector family a foreign SILDeclRef belongs to. +static ObjCSelectorFamily getObjCSelectorFamily(SILDeclRef c) { + assert(c.isForeign); + switch (c.kind) { + case SILDeclRef::Kind::Func: { + if (!c.hasDecl()) + return ObjCSelectorFamily::None; + + auto *FD = cast(c.getDecl()); + if (auto accessor = dyn_cast(FD)) { + switch (accessor->getAccessorKind()) { + case AccessorKind::Get: + case AccessorKind::Set: + break; +#define OBJC_ACCESSOR(ID, KEYWORD) +#define ACCESSOR(ID) \ + case AccessorKind::ID: +#include "swift/AST/AccessorKinds.def" + llvm_unreachable("Unexpected AccessorKind of foreign FuncDecl"); + } + } + + return getObjCSelectorFamily(FD->getObjCSelector()); + } + case SILDeclRef::Kind::Initializer: + case SILDeclRef::Kind::IVarInitializer: + return ObjCSelectorFamily::Init; + + /// Currently IRGen wraps alloc/init methods into Swift constructors + /// with Swift conventions. + case SILDeclRef::Kind::Allocator: + /// These constants don't correspond to method families we care about yet. + case SILDeclRef::Kind::Destroyer: + case SILDeclRef::Kind::Deallocator: + case SILDeclRef::Kind::IVarDestroyer: + return ObjCSelectorFamily::None; + + case SILDeclRef::Kind::EnumElement: + case SILDeclRef::Kind::GlobalAccessor: + case SILDeclRef::Kind::DefaultArgGenerator: + case SILDeclRef::Kind::StoredPropertyInitializer: + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + llvm_unreachable("Unexpected Kind of foreign SILDeclRef"); + } + + llvm_unreachable("Unhandled SILDeclRefKind in switch."); +} + +namespace { + +class ObjCSelectorFamilyConventions : public Conventions { + ObjCSelectorFamily Family; + +public: + ObjCSelectorFamilyConventions(ObjCSelectorFamily family) + : Conventions(ConventionsKind::ObjCSelectorFamily), Family(family) {} + + ParameterConvention getIndirectParameter(unsigned index, + const AbstractionPattern &type, + const TypeLowering &substTL) const override { + return ParameterConvention::Indirect_In; + } + + ParameterConvention getDirectParameter(unsigned index, + const AbstractionPattern &type, + const TypeLowering &substTL) const override { + return ParameterConvention::Direct_Unowned; + } + + ParameterConvention getCallee() const override { + // Always thin. + return ParameterConvention::Direct_Unowned; + } + + ResultConvention getResult(const TypeLowering &tl) const override { + switch (Family) { + case ObjCSelectorFamily::Alloc: + case ObjCSelectorFamily::Copy: + case ObjCSelectorFamily::Init: + case ObjCSelectorFamily::MutableCopy: + case ObjCSelectorFamily::New: + return ResultConvention::Owned; + + case ObjCSelectorFamily::None: + // Defaults below. + break; + } + + // Get the underlying AST type, potentially stripping off one level of + // optionality while we do it. + CanType type = tl.getLoweredType().unwrapOptionalType().getASTType(); + if (type->hasRetainablePointerRepresentation() + || (type->getSwiftNewtypeUnderlyingType() && !tl.isTrivial())) + return ResultConvention::Autoreleased; + + return ResultConvention::Unowned; + } + + ParameterConvention + getDirectSelfParameter(const AbstractionPattern &type) const override { + if (Family == ObjCSelectorFamily::Init) + return ParameterConvention::Direct_Owned; + return ObjCSelfConvention; + } + + ParameterConvention + getIndirectSelfParameter(const AbstractionPattern &type) const override { + llvm_unreachable("selector family objc function types do not support " + "indirect self parameters"); + } + + static bool classof(const Conventions *C) { + return C->getKind() == ConventionsKind::ObjCSelectorFamily; + } +}; + +} // end anonymous namespace + +static CanSILFunctionType +getSILFunctionTypeForObjCSelectorFamily(TypeConverter &TC, ObjCSelectorFamily family, + CanAnyFunctionType origType, + CanAnyFunctionType substInterfaceType, + AnyFunctionType::ExtInfo extInfo, + const ForeignInfo &foreignInfo, + Optional constant) { + return getSILFunctionType( + TC, TypeExpansionContext::minimal(), AbstractionPattern(origType), + substInterfaceType, extInfo, ObjCSelectorFamilyConventions(family), + foreignInfo, constant, constant, + /*requirement subs*/ None, ProtocolConformanceRef()); +} + +static bool isImporterGeneratedAccessor(const clang::Decl *clangDecl, + SILDeclRef constant) { + // Must be an accessor. + auto accessor = dyn_cast(constant.getDecl()); + if (!accessor) + return false; + + // Must be a type member. + if (constant.getParameterListCount() != 2) + return false; + + // Must be imported from a function. + if (!isa(clangDecl)) + return false; + + return true; +} + +static CanSILFunctionType getUncachedSILFunctionTypeForConstant( + TypeConverter &TC, TypeExpansionContext context, SILDeclRef constant, + CanAnyFunctionType origLoweredInterfaceType) { + assert(origLoweredInterfaceType->getExtInfo().getSILRepresentation() + != SILFunctionTypeRepresentation::Thick + && origLoweredInterfaceType->getExtInfo().getSILRepresentation() + != SILFunctionTypeRepresentation::Block); + + auto extInfo = origLoweredInterfaceType->getExtInfo(); + + if (!constant.isForeign) { + ProtocolConformanceRef witnessMethodConformance; + + if (extInfo.getSILRepresentation() == + SILFunctionTypeRepresentation::WitnessMethod) { + auto proto = constant.getDecl()->getDeclContext()->getSelfProtocolDecl(); + witnessMethodConformance = ProtocolConformanceRef(proto); + } + + return ::getNativeSILFunctionType( + TC, context, AbstractionPattern(origLoweredInterfaceType), + origLoweredInterfaceType, extInfo, constant, constant, None, + witnessMethodConformance); + } + + ForeignInfo foreignInfo; + + // If we have a clang decl associated with the Swift decl, derive its + // ownership conventions. + if (constant.hasDecl()) { + auto decl = constant.getDecl(); + if (auto funcDecl = dyn_cast(decl)) { + foreignInfo.Error = funcDecl->getForeignErrorConvention(); + foreignInfo.Self = funcDecl->getImportAsMemberStatus(); + } + + if (auto clangDecl = findClangMethod(decl)) { + // The importer generates accessors that are not actually + // import-as-member but do involve the same gymnastics with the + // formal type. That's all that SILFunctionType cares about, so + // pretend that it's import-as-member. + if (!foreignInfo.Self.isImportAsMember() && + isImporterGeneratedAccessor(clangDecl, constant)) { + unsigned selfIndex = cast(decl)->isSetter() ? 1 : 0; + foreignInfo.Self.setSelfIndex(selfIndex); + } + + return getSILFunctionTypeForClangDecl(TC, clangDecl, + origLoweredInterfaceType, + origLoweredInterfaceType, + extInfo, foreignInfo, constant); + } + } + + // If the decl belongs to an ObjC method family, use that family's + // ownership conventions. + return getSILFunctionTypeForObjCSelectorFamily( + TC, getObjCSelectorFamily(constant), + origLoweredInterfaceType, origLoweredInterfaceType, + extInfo, foreignInfo, constant); +} + +CanSILFunctionType TypeConverter::getUncachedSILFunctionTypeForConstant( + TypeExpansionContext context, SILDeclRef constant, + CanAnyFunctionType origInterfaceType) { + auto origLoweredInterfaceType = + getLoweredFormalTypes(constant, origInterfaceType).Uncurried; + return ::getUncachedSILFunctionTypeForConstant(*this, context, constant, + origLoweredInterfaceType); +} + +static bool isClassOrProtocolMethod(ValueDecl *vd) { + if (!vd->getDeclContext()) + return false; + Type contextType = vd->getDeclContext()->getDeclaredInterfaceType(); + if (!contextType) + return false; + return contextType->getClassOrBoundGenericClass() + || contextType->isClassExistentialType(); +} + +SILFunctionTypeRepresentation +TypeConverter::getDeclRefRepresentation(SILDeclRef c) { + // If this is a foreign thunk, it always has the foreign calling convention. + if (c.isForeign) { + if (!c.hasDecl() || + c.getDecl()->isImportAsMember()) + return SILFunctionTypeRepresentation::CFunctionPointer; + + if (isClassOrProtocolMethod(c.getDecl()) || + c.kind == SILDeclRef::Kind::IVarInitializer || + c.kind == SILDeclRef::Kind::IVarDestroyer) + return SILFunctionTypeRepresentation::ObjCMethod; + + return SILFunctionTypeRepresentation::CFunctionPointer; + } + + // Anonymous functions currently always have Freestanding CC. + if (!c.hasDecl()) + return SILFunctionTypeRepresentation::Thin; + + // FIXME: Assert that there is a native entry point + // available. There's no great way to do this. + + // Protocol witnesses are called using the witness calling convention. + if (auto proto = dyn_cast(c.getDecl()->getDeclContext())) { + // Use the regular method convention for foreign-to-native thunks. + if (c.isForeignToNativeThunk()) + return SILFunctionTypeRepresentation::Method; + assert(!c.isNativeToForeignThunk() && "shouldn't be possible"); + return getProtocolWitnessRepresentation(proto); + } + + switch (c.kind) { + case SILDeclRef::Kind::GlobalAccessor: + case SILDeclRef::Kind::DefaultArgGenerator: + case SILDeclRef::Kind::StoredPropertyInitializer: + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + return SILFunctionTypeRepresentation::Thin; + + case SILDeclRef::Kind::Func: + if (c.getDecl()->getDeclContext()->isTypeContext()) + return SILFunctionTypeRepresentation::Method; + return SILFunctionTypeRepresentation::Thin; + + case SILDeclRef::Kind::Destroyer: + case SILDeclRef::Kind::Deallocator: + case SILDeclRef::Kind::Allocator: + case SILDeclRef::Kind::Initializer: + case SILDeclRef::Kind::EnumElement: + case SILDeclRef::Kind::IVarInitializer: + case SILDeclRef::Kind::IVarDestroyer: + return SILFunctionTypeRepresentation::Method; + } + + llvm_unreachable("Unhandled SILDeclRefKind in switch."); +} + +// Provide the ability to turn off the type converter cache to ease debugging. +static llvm::cl::opt + DisableConstantInfoCache("sil-disable-typelowering-constantinfo-cache", + llvm::cl::init(false)); + +const SILConstantInfo & +TypeConverter::getConstantInfo(TypeExpansionContext expansion, + SILDeclRef constant) { + if (!DisableConstantInfoCache) { + auto found = ConstantTypes.find(std::make_pair(expansion, constant)); + if (found != ConstantTypes.end()) + return *found->second; + } + + // First, get a function type for the constant. This creates the + // right type for a getter or setter. + auto formalInterfaceType = makeConstantInterfaceType(constant); + + // The formal type is just that with the right representation. + auto rep = getDeclRefRepresentation(constant); + formalInterfaceType = adjustFunctionType(formalInterfaceType, rep); + + // The lowered type is the formal type, but uncurried and with + // parameters automatically turned into their bridged equivalents. + auto bridgedTypes = getLoweredFormalTypes(constant, formalInterfaceType); + + CanAnyFunctionType loweredInterfaceType = bridgedTypes.Uncurried; + + // The SIL type encodes conventions according to the original type. + CanSILFunctionType silFnType = + ::getUncachedSILFunctionTypeForConstant(*this, expansion, constant, + loweredInterfaceType); + + // If the constant refers to a derivative function, get the SIL type of the + // original function and use it to compute the derivative SIL type. + // + // This is necessary because the "lowered AST derivative function type" (BC) + // may differ from the "derivative type of the lowered original function type" + // (AD): + // + // +--------------------+ lowering +--------------------+ + // | AST orig. fn type | -------(A)------> | SIL orig. fn type | + // +--------------------+ +--------------------+ + // | | + // (B, Sema) getAutoDiffDerivativeFunctionType (D, here) + // V V + // +--------------------+ lowering +--------------------+ + // | AST deriv. fn type | -------(C)------> | SIL deriv. fn type | + // +--------------------+ +--------------------+ + // + // (AD) does not always commute with (BC): + // - (BC) is the result of computing the AST derivative type (Sema), then + // lowering it via SILGen. This is the default lowering behavior, but may + // break SIL typing invariants because expected lowered derivative types are + // computed from lowered original function types. + // - (AD) is the result of lowering the original function type, then computing + // its derivative type. This is the expected lowered derivative type, + // preserving SIL typing invariants. + // + // Always use (AD) to compute lowered derivative function types. + if (auto *derivativeId = constant.derivativeFunctionIdentifier) { + // Get lowered original function type. + auto origFnConstantInfo = getConstantInfo( + TypeExpansionContext::minimal(), constant.asAutoDiffOriginalFunction()); + // Use it to compute lowered derivative function type. + auto *loweredIndices = autodiff::getLoweredParameterIndices( + derivativeId->getParameterIndices(), formalInterfaceType); + silFnType = origFnConstantInfo.SILFnType->getAutoDiffDerivativeFunctionType( + loweredIndices, /*resultIndex*/ 0, derivativeId->getKind(), *this, + LookUpConformanceInModule(&M)); + } + + LLVM_DEBUG(llvm::dbgs() << "lowering type for constant "; + constant.print(llvm::dbgs()); + llvm::dbgs() << "\n formal type: "; + formalInterfaceType.print(llvm::dbgs()); + llvm::dbgs() << "\n lowered AST type: "; + loweredInterfaceType.print(llvm::dbgs()); + llvm::dbgs() << "\n SIL type: "; + silFnType.print(llvm::dbgs()); + llvm::dbgs() << "\n Expansion context: " + << expansion.shouldLookThroughOpaqueTypeArchetypes(); + llvm::dbgs() << "\n"); + + auto resultBuf = Context.Allocate(sizeof(SILConstantInfo), + alignof(SILConstantInfo)); + + auto result = ::new (resultBuf) SILConstantInfo{formalInterfaceType, + bridgedTypes.Pattern, + loweredInterfaceType, + silFnType}; + if (DisableConstantInfoCache) + return *result; + + auto inserted = + ConstantTypes.insert({std::make_pair(expansion, constant), result}); + assert(inserted.second); + (void)inserted; + return *result; +} + +/// Returns the SILParameterInfo for the given declaration's `self` parameter. +/// `constant` must refer to a method. +SILParameterInfo +TypeConverter::getConstantSelfParameter(TypeExpansionContext context, + SILDeclRef constant) { + auto ty = getConstantFunctionType(context, constant); + + // In most cases the "self" parameter is lowered as the back parameter. + // The exception is C functions imported as methods. + if (!constant.isForeign) + return ty->getParameters().back(); + if (!constant.hasDecl()) + return ty->getParameters().back(); + auto fn = dyn_cast(constant.getDecl()); + if (!fn) + return ty->getParameters().back(); + if (fn->isImportAsStaticMember()) + return SILParameterInfo(); + if (fn->isImportAsInstanceMember()) + return ty->getParameters()[fn->getSelfIndex()]; + return ty->getParameters().back(); +} + +// This check duplicates TypeConverter::checkForABIDifferences(), +// but on AST types. The issue is we only want to introduce a new +// vtable thunk if the AST type changes, but an abstraction change +// is OK; we don't want a new entry if an @in parameter became +// @guaranteed or whatever. +static bool checkASTTypeForABIDifferences(CanType type1, + CanType type2) { + return !type1->matches(type2, TypeMatchFlags::AllowABICompatible); +} + +// FIXME: This makes me very upset. Can we do without this? +static CanType copyOptionalityFromDerivedToBase(TypeConverter &tc, + CanType derived, + CanType base) { + // Unwrap optionals, but remember that we did. + bool derivedWasOptional = false; + if (auto object = derived.getOptionalObjectType()) { + derivedWasOptional = true; + derived = object; + } + if (auto object = base.getOptionalObjectType()) { + base = object; + } + + // T? +> S = (T +> S)? + // T? +> S? = (T +> S)? + if (derivedWasOptional) { + base = copyOptionalityFromDerivedToBase(tc, derived, base); + + auto optDecl = tc.Context.getOptionalDecl(); + return CanType(BoundGenericEnumType::get(optDecl, Type(), base)); + } + + // (T1, T2, ...) +> (S1, S2, ...) = (T1 +> S1, T2 +> S2, ...) + if (auto derivedTuple = dyn_cast(derived)) { + if (auto baseTuple = dyn_cast(base)) { + assert(derivedTuple->getNumElements() == baseTuple->getNumElements()); + SmallVector elements; + for (unsigned i = 0, e = derivedTuple->getNumElements(); i < e; i++) { + elements.push_back( + baseTuple->getElement(i).getWithType( + copyOptionalityFromDerivedToBase( + tc, + derivedTuple.getElementType(i), + baseTuple.getElementType(i)))); + } + return CanType(TupleType::get(elements, tc.Context)); + } + } + + // (T1 -> T2) +> (S1 -> S2) = (T1 +> S1) -> (T2 +> S2) + if (auto derivedFunc = dyn_cast(derived)) { + if (auto baseFunc = dyn_cast(base)) { + SmallVector params; + + auto derivedParams = derivedFunc.getParams(); + auto baseParams = baseFunc.getParams(); + assert(derivedParams.size() == baseParams.size()); + for (unsigned i = 0, e = derivedParams.size(); i < e; i++) { + assert(derivedParams[i].getParameterFlags() == + baseParams[i].getParameterFlags()); + + params.emplace_back( + copyOptionalityFromDerivedToBase( + tc, + derivedParams[i].getPlainType(), + baseParams[i].getPlainType()), + Identifier(), + baseParams[i].getParameterFlags()); + } + + auto result = copyOptionalityFromDerivedToBase(tc, + derivedFunc.getResult(), + baseFunc.getResult()); + return CanAnyFunctionType::get(baseFunc.getOptGenericSignature(), + llvm::makeArrayRef(params), result, + baseFunc->getExtInfo()); + } + } + + return base; +} + +/// Returns the ConstantInfo corresponding to the VTable thunk for overriding. +/// Will be the same as getConstantInfo if the declaration does not override. +const SILConstantInfo & +TypeConverter::getConstantOverrideInfo(TypeExpansionContext context, + SILDeclRef derived, SILDeclRef base) { + // Foreign overrides currently don't need reabstraction. + if (derived.isForeign) + return getConstantInfo(context, derived); + + auto found = ConstantOverrideTypes.find({derived, base}); + if (found != ConstantOverrideTypes.end()) + return *found->second; + + assert(base.requiresNewVTableEntry() && "base must not be an override"); + + // Figure out the generic signature for the class method call. This is the + // signature of the derived class, with requirements transplanted from + // the base method. The derived method is allowed to have fewer + // requirements, in which case the thunk will translate the calling + // convention appropriately before calling the derived method. + bool hasGenericRequirementDifference = false; + + auto derivedSig = derived.getDecl()->getAsGenericContext() + ->getGenericSignature(); + auto genericSig = Context.getOverrideGenericSignature(base.getDecl(), + derived.getDecl()); + if (genericSig) { + hasGenericRequirementDifference = + !genericSig->requirementsNotSatisfiedBy(derivedSig).empty(); + } + + auto baseInfo = getConstantInfo(context, base); + auto derivedInfo = getConstantInfo(context, derived); + + auto params = derivedInfo.FormalType.getParams(); + assert(params.size() == 1); + auto selfInterfaceTy = params[0].getPlainType()->getMetatypeInstanceType(); + + auto overrideInterfaceTy = + cast( + selfInterfaceTy->adjustSuperclassMemberDeclType( + base.getDecl(), derived.getDecl(), baseInfo.FormalType) + ->getCanonicalType()); + + // Build the formal AST function type for the class method call. + auto basePattern = AbstractionPattern(baseInfo.LoweredType); + + if (!hasGenericRequirementDifference && + !checkASTTypeForABIDifferences(derivedInfo.FormalType, + overrideInterfaceTy)) { + + // The derived method is ABI-compatible with the base method. Let's + // just use the derived method's formal type. + basePattern = AbstractionPattern( + copyOptionalityFromDerivedToBase( + *this, + derivedInfo.LoweredType, + baseInfo.LoweredType)); + overrideInterfaceTy = derivedInfo.FormalType; + } + + if (genericSig && !genericSig->areAllParamsConcrete()) { + overrideInterfaceTy = + cast( + GenericFunctionType::get(genericSig, + overrideInterfaceTy->getParams(), + overrideInterfaceTy->getResult(), + overrideInterfaceTy->getExtInfo()) + ->getCanonicalType()); + } + + // Build the lowered AST function type for the class method call. + auto bridgedTypes = getLoweredFormalTypes(derived, overrideInterfaceTy); + + // Build the SILFunctionType for the class method call. + CanSILFunctionType fnTy = getNativeSILFunctionType( + *this, context, basePattern, bridgedTypes.Uncurried, base, derived, + /*reqt subs*/ None, ProtocolConformanceRef()); + + // Build the SILConstantInfo and cache it. + auto resultBuf = Context.Allocate(sizeof(SILConstantInfo), + alignof(SILConstantInfo)); + auto result = ::new (resultBuf) SILConstantInfo{ + overrideInterfaceTy, + basePattern, + bridgedTypes.Uncurried, + fnTy}; + + auto inserted = ConstantOverrideTypes.insert({{derived, base}, result}); + assert(inserted.second); + (void)inserted; + return *result; +} + +namespace { + +/// Given a lowered SIL type, apply a substitution to it to produce another +/// lowered SIL type which uses the same abstraction conventions. +class SILTypeSubstituter : + public CanTypeVisitor { + TypeConverter &TC; + TypeSubstitutionFn Subst; + LookupConformanceFn Conformances; + // The signature for the original type. + // + // Replacement types are lowered with respect to the current + // context signature. + CanGenericSignature Sig; + + TypeExpansionContext typeExpansionContext; + + bool shouldSubstituteOpaqueArchetypes; + +public: + SILTypeSubstituter(TypeConverter &TC, + TypeExpansionContext context, + TypeSubstitutionFn Subst, + LookupConformanceFn Conformances, + CanGenericSignature Sig, + bool shouldSubstituteOpaqueArchetypes) + : TC(TC), + Subst(Subst), + Conformances(Conformances), + Sig(Sig), + typeExpansionContext(context), + shouldSubstituteOpaqueArchetypes(shouldSubstituteOpaqueArchetypes) + {} + + // SIL type lowering only does special things to tuples and functions. + + // When a function appears inside of another type, we only perform + // substitutions if it is not polymorphic. + CanSILFunctionType visitSILFunctionType(CanSILFunctionType origType) { + return substSILFunctionType(origType, false); + } + + SubstitutionMap substSubstitutions(SubstitutionMap subs) { + // Substitute the substitutions. + SubstOptions options = None; + if (shouldSubstituteOpaqueArchetypes) + options |= SubstFlags::SubstituteOpaqueArchetypes; + + // Expand substituted type according to the expansion context. + auto newSubs = subs.subst(Subst, Conformances, options); + + // If we need to look through opaque types in this context, re-substitute + // according to the expansion context. + newSubs = substOpaqueTypes(newSubs); + + return newSubs; + } + + SubstitutionMap substOpaqueTypes(SubstitutionMap subs) { + if (!typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) + return subs; + + return subs.subst([&](SubstitutableType *s) -> Type { + return substOpaqueTypesWithUnderlyingTypes(s->getCanonicalType(), + typeExpansionContext); + }, [&](CanType dependentType, + Type conformingReplacementType, + ProtocolDecl *conformedProtocol) -> ProtocolConformanceRef { + return substOpaqueTypesWithUnderlyingTypes( + ProtocolConformanceRef(conformedProtocol), + conformingReplacementType->getCanonicalType(), + typeExpansionContext); + }, SubstFlags::SubstituteOpaqueArchetypes); + } + + // Substitute a function type. + CanSILFunctionType substSILFunctionType(CanSILFunctionType origType, + bool isGenericApplication) { + assert((!isGenericApplication || origType->isPolymorphic()) && + "generic application without invocation signature or with " + "existing arguments"); + assert((!isGenericApplication || !shouldSubstituteOpaqueArchetypes) && + "generic application while substituting opaque archetypes"); + + // The general substitution rule is that we should only substitute + // into the free components of the type, i.e. the components that + // aren't inside a generic signature. That rule would say: + // + // - If there are invocation substitutions, just substitute those; + // the other components are necessarily inside the invocation + // generic signature. + // + // - Otherwise, if there's an invocation generic signature, + // substitute nothing. If we are applying generic arguments, + // add the appropriate invocation substitutions. + // + // - Otherwise, if there are pattern substitutions, just substitute + // those; the other components are inside the patttern generic + // signature. + // + // - Otherwise, substitute the basic components. + // + // There are two caveats here. The first is that we haven't yet + // written all the code that would be necessary in order to handle + // invocation substitutions everywhere, and so we never build those. + // Instead, we substitute into the pattern substitutions if present, + // or the components if not, and build a type with no invocation + // signature. As a special case, when substituting a coroutine type, + // we build pattern substitutions instead of substituting the + // component types in order to preserve the original yield structure, + // which factors into the continuation function ABI. + // + // The second is that this function is also used when substituting + // opaque archetypes. In this case, we may need to substitute + // into component types even within generic signatures. This is + // safe because the substitutions used in this case don't change + // generics, they just narrowly look through certain opaque archetypes. + // If substitutions are present, we still don't substitute into + // the basic components, in order to maintain the information about + // what was abstracted there. + + auto patternSubs = origType->getPatternSubstitutions(); + + // If we have an invocation signatture, we generally shouldn't + // substitute into the pattern substitutions and component types. + if (auto sig = origType->getInvocationGenericSignature()) { + // Substitute the invocation substitutions if present. + if (auto invocationSubs = origType->getInvocationSubstitutions()) { + assert(!isGenericApplication); + invocationSubs = substSubstitutions(invocationSubs); + auto substType = + origType->withInvocationSubstitutions(invocationSubs); + + // Also do opaque-type substitutions on the pattern substitutions + // if requested and applicable. + if (patternSubs) { + patternSubs = substOpaqueTypes(patternSubs); + substType = substType->withPatternSubstitutions(patternSubs); + } + + return substType; + } + + // Otherwise, we shouldn't substitute any components except + // when substituting opaque archetypes. + + // If we're doing a generic application, and there are pattern + // substitutions, substitute into the pattern substitutions; or if + // it's a coroutine, build pattern substitutions; or else, fall + // through to substitute the component types as discussed above. + if (isGenericApplication) { + if (patternSubs || origType->isCoroutine()) { + CanSILFunctionType substType = origType; + if (typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) { + substType = + origType->substituteOpaqueArchetypes(TC, typeExpansionContext); + } + + SubstitutionMap subs; + if (patternSubs) { + subs = substSubstitutions(patternSubs); + } else { + subs = SubstitutionMap::get(sig, Subst, Conformances); + } + auto witnessConformance = substWitnessConformance(origType); + substType = substType->withPatternSpecialization(nullptr, subs, + witnessConformance); + + return substType; + } + // else fall down to component substitution + + // If we're substituting opaque archetypes, and there are pattern + // substitutions present, just substitute those and preserve the + // basic structure in the component types. Otherwise, fall through + // to substitute the component types. + } else if (shouldSubstituteOpaqueArchetypes) { + if (patternSubs) { + patternSubs = substOpaqueTypes(patternSubs); + auto witnessConformance = substWitnessConformance(origType); + return origType->withPatternSpecialization(sig, patternSubs, + witnessConformance); + } + // else fall down to component substitution + + // Otherwise, don't try to substitute bound components. + } else { + auto substType = origType; + if (patternSubs) { + patternSubs = substOpaqueTypes(patternSubs); + auto witnessConformance = substWitnessConformance(origType); + substType = substType->withPatternSpecialization(sig, patternSubs, + witnessConformance); + } + return substType; + } + + // Otherwise, if there are pattern substitutions, just substitute + // into those and don't touch the component types. + } else if (patternSubs) { + patternSubs = substSubstitutions(patternSubs); + auto witnessConformance = substWitnessConformance(origType); + return origType->withPatternSpecialization(nullptr, patternSubs, + witnessConformance); + } + + // Otherwise, we need to substitute component types. + + SmallVector substResults; + substResults.reserve(origType->getNumResults()); + for (auto origResult : origType->getResults()) { + substResults.push_back(substInterface(origResult)); + } + + auto substErrorResult = origType->getOptionalErrorResult(); + assert(!substErrorResult || + (!substErrorResult->getInterfaceType()->hasTypeParameter() && + !substErrorResult->getInterfaceType()->hasArchetype())); + + SmallVector substParams; + substParams.reserve(origType->getParameters().size()); + for (auto &origParam : origType->getParameters()) { + substParams.push_back(substInterface(origParam)); + } + + SmallVector substYields; + substYields.reserve(origType->getYields().size()); + for (auto &origYield : origType->getYields()) { + substYields.push_back(substInterface(origYield)); + } + + auto witnessMethodConformance = substWitnessConformance(origType); + + // The substituted type is no longer generic, so it'd never be + // pseudogeneric. + auto extInfo = origType->getExtInfo(); + if (!shouldSubstituteOpaqueArchetypes) + extInfo = extInfo.withIsPseudogeneric(false); + + auto genericSig = shouldSubstituteOpaqueArchetypes + ? origType->getInvocationGenericSignature() + : nullptr; + + return SILFunctionType::get(genericSig, extInfo, + origType->getCoroutineKind(), + origType->getCalleeConvention(), substParams, + substYields, substResults, substErrorResult, + SubstitutionMap(), SubstitutionMap(), + TC.Context, witnessMethodConformance); + } + + ProtocolConformanceRef substWitnessConformance(CanSILFunctionType origType) { + auto conformance = origType->getWitnessMethodConformanceOrInvalid(); + if (!conformance) return conformance; + + assert(origType->getExtInfo().hasSelfParam()); + auto selfType = origType->getSelfParameter().getInterfaceType(); + + // The Self type can be nested in a few layers of metatypes (etc.). + while (auto metatypeType = dyn_cast(selfType)) { + auto next = metatypeType.getInstanceType(); + if (next == selfType) + break; + selfType = next; + } + + auto substConformance = + conformance.subst(selfType, Subst, Conformances); + + // Substitute the underlying conformance of opaque type archetypes if we + // should look through opaque archetypes. + if (typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) { + SubstOptions substOptions(None); + auto substType = selfType.subst(Subst, Conformances, substOptions) + ->getCanonicalType(); + if (substType->hasOpaqueArchetype()) { + substConformance = substOpaqueTypesWithUnderlyingTypes( + substConformance, substType, typeExpansionContext); + } + } + + return substConformance; + } + + SILType subst(SILType type) { + return SILType::getPrimitiveType(visit(type.getASTType()), + type.getCategory()); + } + + SILResultInfo substInterface(SILResultInfo orig) { + return SILResultInfo(visit(orig.getInterfaceType()), orig.getConvention()); + } + + SILYieldInfo substInterface(SILYieldInfo orig) { + return SILYieldInfo(visit(orig.getInterfaceType()), orig.getConvention()); + } + + SILParameterInfo substInterface(SILParameterInfo orig) { + return SILParameterInfo(visit(orig.getInterfaceType()), + orig.getConvention(), orig.getDifferentiability()); + } + + /// Tuples need to have their component types substituted by these + /// same rules. + CanType visitTupleType(CanTupleType origType) { + // Fast-path the empty tuple. + if (origType->getNumElements() == 0) return origType; + + SmallVector substElts; + substElts.reserve(origType->getNumElements()); + for (auto &origElt : origType->getElements()) { + auto substEltType = visit(CanType(origElt.getType())); + substElts.push_back(origElt.getWithType(substEltType)); + } + return CanType(TupleType::get(substElts, TC.Context)); + } + // Block storage types need to substitute their capture type by these same + // rules. + CanType visitSILBlockStorageType(CanSILBlockStorageType origType) { + auto substCaptureType = visit(origType->getCaptureType()); + return SILBlockStorageType::get(substCaptureType); + } + + /// Optionals need to have their object types substituted by these rules. + CanType visitBoundGenericEnumType(CanBoundGenericEnumType origType) { + // Only use a special rule if it's Optional. + if (!origType->getDecl()->isOptionalDecl()) { + return visitType(origType); + } + + CanType origObjectType = origType.getGenericArgs()[0]; + CanType substObjectType = visit(origObjectType); + return CanType(BoundGenericType::get(origType->getDecl(), Type(), + substObjectType)); + } + + /// Any other type would be a valid type in the AST. Just apply the + /// substitution on the AST level and then lower that. + CanType visitType(CanType origType) { + assert(!isa(origType)); + assert(!isa(origType) && !isa(origType)); + + SubstOptions substOptions(None); + if (shouldSubstituteOpaqueArchetypes) + substOptions = SubstFlags::SubstituteOpaqueArchetypes | + SubstFlags::AllowLoweredTypes; + auto substType = + origType.subst(Subst, Conformances, substOptions)->getCanonicalType(); + + // If the substitution didn't change anything, we know that the + // original type was a lowered type, so we're good. + if (origType == substType) { + return origType; + } + + AbstractionPattern abstraction(Sig, origType); + return TC.getLoweredRValueType(typeExpansionContext, abstraction, + substType); + } +}; + +} // end anonymous namespace + +SILType SILType::subst(TypeConverter &tc, TypeSubstitutionFn subs, + LookupConformanceFn conformances, + CanGenericSignature genericSig, + bool shouldSubstituteOpaqueArchetypes) const { + if (!hasArchetype() && !hasTypeParameter() && + (!shouldSubstituteOpaqueArchetypes || + !getASTType()->hasOpaqueArchetype())) + return *this; + + SILTypeSubstituter STST(tc, TypeExpansionContext::minimal(), subs, + conformances, genericSig, + shouldSubstituteOpaqueArchetypes); + return STST.subst(*this); +} + +SILType SILType::subst(SILModule &M, TypeSubstitutionFn subs, + LookupConformanceFn conformances, + CanGenericSignature genericSig, + bool shouldSubstituteOpaqueArchetypes) const { + return subst(M.Types, subs, conformances, genericSig, + shouldSubstituteOpaqueArchetypes); +} + +SILType SILType::subst(TypeConverter &tc, SubstitutionMap subs) const { + auto sig = subs.getGenericSignature(); + return subst(tc, QuerySubstitutionMap{subs}, + LookUpConformanceInSubstitutionMap(subs), + sig.getCanonicalSignature()); +} +SILType SILType::subst(SILModule &M, SubstitutionMap subs) const{ + return subst(M.Types, subs); +} + +/// Apply a substitution to this polymorphic SILFunctionType so that +/// it has the form of the normal SILFunctionType for the substituted +/// type, except using the original conventions. +CanSILFunctionType +SILFunctionType::substGenericArgs(SILModule &silModule, SubstitutionMap subs, + TypeExpansionContext context) { + if (!isPolymorphic()) { + return CanSILFunctionType(this); + } + + if (subs.empty()) { + return CanSILFunctionType(this); + } + + return substGenericArgs(silModule, + QuerySubstitutionMap{subs}, + LookUpConformanceInSubstitutionMap(subs), + context); +} + +CanSILFunctionType +SILFunctionType::substGenericArgs(SILModule &silModule, + TypeSubstitutionFn subs, + LookupConformanceFn conformances, + TypeExpansionContext context) { + if (!isPolymorphic()) return CanSILFunctionType(this); + SILTypeSubstituter substituter(silModule.Types, context, subs, conformances, + getSubstGenericSignature(), + /*shouldSubstituteOpaqueTypes*/ false); + return substituter.substSILFunctionType(CanSILFunctionType(this), true); +} + +CanSILFunctionType +SILFunctionType::substituteOpaqueArchetypes(TypeConverter &TC, + TypeExpansionContext context) { + if (!hasOpaqueArchetype() || + !context.shouldLookThroughOpaqueTypeArchetypes()) + return CanSILFunctionType(this); + + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + context.getContext(), context.getResilienceExpansion(), + context.isWholeModuleContext()); + + SILTypeSubstituter substituter(TC, context, replacer, replacer, + getSubstGenericSignature(), + /*shouldSubstituteOpaqueTypes*/ true); + auto resTy = + substituter.substSILFunctionType(CanSILFunctionType(this), false); + + return resTy; +} + +/// Fast path for bridging types in a function type without uncurrying. +CanAnyFunctionType +TypeConverter::getBridgedFunctionType(AbstractionPattern pattern, + CanAnyFunctionType t, + AnyFunctionType::ExtInfo extInfo, + Bridgeability bridging) { + // Pull out the generic signature. + CanGenericSignature genericSig = t.getOptGenericSignature(); + + switch (auto rep = t->getExtInfo().getSILRepresentation()) { + case SILFunctionTypeRepresentation::Thick: + case SILFunctionTypeRepresentation::Thin: + case SILFunctionTypeRepresentation::Method: + case SILFunctionTypeRepresentation::Closure: + case SILFunctionTypeRepresentation::WitnessMethod: { + // No bridging needed for native functions. + if (t->getExtInfo() == extInfo) + return t; + return CanAnyFunctionType::get(genericSig, t.getParams(), t.getResult(), + extInfo); + } + + case SILFunctionTypeRepresentation::CFunctionPointer: + case SILFunctionTypeRepresentation::Block: + case SILFunctionTypeRepresentation::ObjCMethod: { + SmallVector params; + getBridgedParams(rep, pattern, t->getParams(), params, bridging); + + bool suppressOptional = pattern.hasForeignErrorStrippingResultOptionality(); + auto result = getBridgedResultType(rep, + pattern.getFunctionResultType(), + t.getResult(), + bridging, + suppressOptional); + + return CanAnyFunctionType::get(genericSig, llvm::makeArrayRef(params), + result, extInfo); + } + } + llvm_unreachable("bad calling convention"); +} + +static AbstractFunctionDecl *getBridgedFunction(SILDeclRef declRef) { + switch (declRef.kind) { + case SILDeclRef::Kind::Func: + case SILDeclRef::Kind::Allocator: + case SILDeclRef::Kind::Initializer: + return (declRef.hasDecl() + ? cast(declRef.getDecl()) + : nullptr); + + case SILDeclRef::Kind::EnumElement: + case SILDeclRef::Kind::Destroyer: + case SILDeclRef::Kind::Deallocator: + case SILDeclRef::Kind::GlobalAccessor: + case SILDeclRef::Kind::DefaultArgGenerator: + case SILDeclRef::Kind::StoredPropertyInitializer: + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + case SILDeclRef::Kind::IVarInitializer: + case SILDeclRef::Kind::IVarDestroyer: + return nullptr; + } + llvm_unreachable("bad SILDeclRef kind"); +} + +static AbstractionPattern +getAbstractionPatternForConstant(ASTContext &ctx, SILDeclRef constant, + CanAnyFunctionType fnType, + unsigned numParameterLists) { + if (!constant.isForeign) + return AbstractionPattern(fnType); + + auto bridgedFn = getBridgedFunction(constant); + if (!bridgedFn) + return AbstractionPattern(fnType); + const clang::Decl *clangDecl = bridgedFn->getClangDecl(); + if (!clangDecl) + return AbstractionPattern(fnType); + + // Don't implicitly turn non-optional results to optional if + // we're going to apply a foreign error convention that checks + // for nil results. + if (auto method = dyn_cast(clangDecl)) { + assert(numParameterLists == 2 && "getting curried ObjC method type?"); + auto foreignError = bridgedFn->getForeignErrorConvention(); + return AbstractionPattern::getCurriedObjCMethod(fnType, method, + foreignError); + } else if (auto value = dyn_cast(clangDecl)) { + if (numParameterLists == 1) { + // C function imported as a function. + return AbstractionPattern(fnType, value->getType().getTypePtr()); + } else { + assert(numParameterLists == 2); + if (auto method = dyn_cast(clangDecl)) { + // C++ method. + return AbstractionPattern::getCurriedCXXMethod(fnType, bridgedFn); + } else { + // C function imported as a method. + return AbstractionPattern::getCurriedCFunctionAsMethod(fnType, + bridgedFn); + } + } + } + + return AbstractionPattern(fnType); +} + +TypeConverter::LoweredFormalTypes +TypeConverter::getLoweredFormalTypes(SILDeclRef constant, + CanAnyFunctionType fnType) { + // We always use full bridging when importing a constant because we can + // directly bridge its arguments and results when calling it. + auto bridging = Bridgeability::Full; + + unsigned numParameterLists = constant.getParameterListCount(); + auto extInfo = fnType->getExtInfo(); + + // Form an abstraction pattern for bridging purposes. + AbstractionPattern bridgingFnPattern = + getAbstractionPatternForConstant(Context, constant, fnType, + numParameterLists); + + // Fast path: no uncurrying required. + if (numParameterLists == 1) { + auto bridgedFnType = + getBridgedFunctionType(bridgingFnPattern, fnType, extInfo, bridging); + bridgingFnPattern.rewriteType(bridgingFnPattern.getGenericSignature(), + bridgedFnType); + return { bridgingFnPattern, bridgedFnType }; + } + + SILFunctionTypeRepresentation rep = extInfo.getSILRepresentation(); + assert(rep != SILFunctionType::Representation::Block + && "objc blocks cannot be curried"); + + // The dependent generic signature. + CanGenericSignature genericSig = fnType.getOptGenericSignature(); + + // The 'self' parameter. + assert(fnType.getParams().size() == 1); + AnyFunctionType::Param selfParam = fnType.getParams()[0]; + + // The formal method parameters. + // If we actually partially-apply this, assume we'll need a thick function. + fnType = cast(fnType.getResult()); + auto innerExtInfo = + fnType->getExtInfo().withRepresentation(FunctionTypeRepresentation::Swift); + auto methodParams = fnType->getParams(); + + auto resultType = fnType.getResult(); + bool suppressOptionalResult = + bridgingFnPattern.hasForeignErrorStrippingResultOptionality(); + + // Bridge input and result types. + SmallVector bridgedParams; + CanType bridgedResultType; + + switch (rep) { + case SILFunctionTypeRepresentation::Thin: + case SILFunctionTypeRepresentation::Thick: + case SILFunctionTypeRepresentation::Method: + case SILFunctionTypeRepresentation::Closure: + case SILFunctionTypeRepresentation::WitnessMethod: + // Native functions don't need bridging. + bridgedParams.append(methodParams.begin(), methodParams.end()); + bridgedResultType = resultType; + break; + + case SILFunctionTypeRepresentation::ObjCMethod: + case SILFunctionTypeRepresentation::CFunctionPointer: { + if (rep == SILFunctionTypeRepresentation::ObjCMethod) { + // The "self" parameter should not get bridged unless it's a metatype. + if (selfParam.getPlainType()->is()) { + auto selfPattern = bridgingFnPattern.getFunctionParamType(0); + selfParam = getBridgedParam(rep, selfPattern, selfParam, bridging); + } + } + + auto partialFnPattern = bridgingFnPattern.getFunctionResultType(); + getBridgedParams(rep, partialFnPattern, methodParams, bridgedParams, + bridging); + + bridgedResultType = + getBridgedResultType(rep, + partialFnPattern.getFunctionResultType(), + resultType, bridging, suppressOptionalResult); + break; + } + + case SILFunctionTypeRepresentation::Block: + llvm_unreachable("Cannot uncurry native representation"); + } + + // Build the curried function type. + auto inner = + CanFunctionType::get(llvm::makeArrayRef(bridgedParams), + bridgedResultType, innerExtInfo); + + auto curried = + CanAnyFunctionType::get(genericSig, {selfParam}, inner, extInfo); + + // Replace the type in the abstraction pattern with the curried type. + bridgingFnPattern.rewriteType(genericSig, curried); + + // Build the uncurried function type. + if (innerExtInfo.throws()) + extInfo = extInfo.withThrows(true); + + bridgedParams.push_back(selfParam); + + auto uncurried = + CanAnyFunctionType::get(genericSig, + llvm::makeArrayRef(bridgedParams), + bridgedResultType, + extInfo); + + return { bridgingFnPattern, uncurried }; +} + +// TODO: We should compare generic signatures. Class and witness methods +// allow variance in "self"-fulfilled parameters; other functions must +// match exactly. +// TODO: More sophisticated param and return ABI compatibility rules could +// diverge. +static bool areABICompatibleParamsOrReturns(SILType a, SILType b, + SILFunction *inFunction) { + // Address parameters are all ABI-compatible, though the referenced + // values may not be. Assume whoever's doing this knows what they're + // doing. + if (a.isAddress() && b.isAddress()) + return true; + + // Addresses aren't compatible with values. + // TODO: An exception for pointerish types? + if (a.isAddress() || b.isAddress()) + return false; + + // Tuples are ABI compatible if their elements are. + // TODO: Should destructure recursively. + SmallVector aElements, bElements; + if (auto tup = a.getAs()) { + auto types = tup.getElementTypes(); + aElements.append(types.begin(), types.end()); + } else { + aElements.push_back(a.getASTType()); + } + if (auto tup = b.getAs()) { + auto types = tup.getElementTypes(); + bElements.append(types.begin(), types.end()); + } else { + bElements.push_back(b.getASTType()); + } + + if (aElements.size() != bElements.size()) + return false; + + for (unsigned i : indices(aElements)) { + auto aa = SILType::getPrimitiveObjectType(aElements[i]); + auto bb = SILType::getPrimitiveObjectType(bElements[i]); + // Equivalent types are always ABI-compatible. + if (aa == bb) + continue; + + // Opaque types are compatible with their substitution. + if (inFunction) { + auto opaqueTypesSubsituted = aa; + auto *dc = inFunction->getDeclContext(); + auto *currentModule = inFunction->getModule().getSwiftModule(); + if (!dc || !dc->isChildContextOf(currentModule)) + dc = currentModule; + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + dc, inFunction->getResilienceExpansion(), + inFunction->getModule().isWholeModule()); + if (aa.getASTType()->hasOpaqueArchetype()) + opaqueTypesSubsituted = aa.subst(inFunction->getModule(), replacer, + replacer, CanGenericSignature(), true); + + auto opaqueTypesSubsituted2 = bb; + if (bb.getASTType()->hasOpaqueArchetype()) + opaqueTypesSubsituted2 = + bb.subst(inFunction->getModule(), replacer, replacer, + CanGenericSignature(), true); + if (opaqueTypesSubsituted == opaqueTypesSubsituted2) + continue; + } + + // FIXME: If one or both types are dependent, we can't accurately assess + // whether they're ABI-compatible without a generic context. We can + // do a better job here when dependent types are related to their + // generic signatures. + if (aa.hasTypeParameter() || bb.hasTypeParameter()) + continue; + + // Bridgeable object types are interchangeable. + if (aa.isBridgeableObjectType() && bb.isBridgeableObjectType()) + continue; + + // Optional and IUO are interchangeable if their elements are. + auto aObject = aa.getOptionalObjectType(); + auto bObject = bb.getOptionalObjectType(); + if (aObject && bObject && + areABICompatibleParamsOrReturns(aObject, bObject, inFunction)) + continue; + // Optional objects are ABI-interchangeable with non-optionals; + // None is represented by a null pointer. + if (aObject && aObject.isBridgeableObjectType() && + bb.isBridgeableObjectType()) + continue; + if (bObject && bObject.isBridgeableObjectType() && + aa.isBridgeableObjectType()) + continue; + + // Optional thick metatypes are ABI-interchangeable with non-optionals + // too. + if (aObject) + if (auto aObjMeta = aObject.getAs()) + if (auto bMeta = bb.getAs()) + if (aObjMeta->getRepresentation() == bMeta->getRepresentation() && + bMeta->getRepresentation() != MetatypeRepresentation::Thin) + continue; + if (bObject) + if (auto aMeta = aa.getAs()) + if (auto bObjMeta = bObject.getAs()) + if (aMeta->getRepresentation() == bObjMeta->getRepresentation() && + aMeta->getRepresentation() != MetatypeRepresentation::Thin) + continue; + + // Function types are interchangeable if they're also ABI-compatible. + if (auto aFunc = aa.getAs()) { + if (auto bFunc = bb.getAs()) { + // *NOTE* We swallow the specific error here for now. We will still get + // that the function types are incompatible though, just not more + // specific information. + return aFunc->isABICompatibleWith(bFunc, *inFunction).isCompatible(); + } + } + + // Metatypes are interchangeable with metatypes with the same + // representation. + if (auto aMeta = aa.getAs()) { + if (auto bMeta = bb.getAs()) { + if (aMeta->getRepresentation() == bMeta->getRepresentation()) + continue; + } + } + // Other types must match exactly. + return false; + } + + return true; +} + +namespace { +using ABICompatibilityCheckResult = + SILFunctionType::ABICompatibilityCheckResult; +} // end anonymous namespace + +ABICompatibilityCheckResult +SILFunctionType::isABICompatibleWith(CanSILFunctionType other, + SILFunction &context) const { + // The calling convention and function representation can't be changed. + if (getRepresentation() != other->getRepresentation()) + return ABICompatibilityCheckResult::DifferentFunctionRepresentations; + + // Check the results. + if (getNumResults() != other->getNumResults()) + return ABICompatibilityCheckResult::DifferentNumberOfResults; + + for (unsigned i : indices(getResults())) { + auto result1 = getResults()[i]; + auto result2 = other->getResults()[i]; + + if (result1.getConvention() != result2.getConvention()) + return ABICompatibilityCheckResult::DifferentReturnValueConventions; + + if (!areABICompatibleParamsOrReturns( + result1.getSILStorageType(context.getModule(), this), + result2.getSILStorageType(context.getModule(), other), + &context)) { + return ABICompatibilityCheckResult::ABIIncompatibleReturnValues; + } + } + + // Our error result conventions are designed to be ABI compatible + // with functions lacking error results. Just make sure that the + // actual conventions match up. + if (hasErrorResult() && other->hasErrorResult()) { + auto error1 = getErrorResult(); + auto error2 = other->getErrorResult(); + if (error1.getConvention() != error2.getConvention()) + return ABICompatibilityCheckResult::DifferentErrorResultConventions; + + if (!areABICompatibleParamsOrReturns( + error1.getSILStorageType(context.getModule(), this), + error2.getSILStorageType(context.getModule(), other), + &context)) + return ABICompatibilityCheckResult::ABIIncompatibleErrorResults; + } + + // Check the parameters. + // TODO: Could allow known-empty types to be inserted or removed, but SIL + // doesn't know what empty types are yet. + if (getParameters().size() != other->getParameters().size()) + return ABICompatibilityCheckResult::DifferentNumberOfParameters; + + for (unsigned i : indices(getParameters())) { + auto param1 = getParameters()[i]; + auto param2 = other->getParameters()[i]; + + if (param1.getConvention() != param2.getConvention()) + return {ABICompatibilityCheckResult::DifferingParameterConvention, i}; + if (!areABICompatibleParamsOrReturns( + param1.getSILStorageType(context.getModule(), this), + param2.getSILStorageType(context.getModule(), other), + &context)) + return {ABICompatibilityCheckResult::ABIIncompatibleParameterType, i}; + } + + // This needs to be checked last because the result implies everying else has + // already been checked and this is the only difference. + if (isNoEscape() != other->isNoEscape() && + (getRepresentation() == SILFunctionType::Representation::Thick)) + return ABICompatibilityCheckResult::ABIEscapeToNoEscapeConversion; + + return ABICompatibilityCheckResult::None; +} + +StringRef SILFunctionType::ABICompatibilityCheckResult::getMessage() const { + switch (kind) { + case innerty::None: + return "None"; + case innerty::DifferentFunctionRepresentations: + return "Different function representations"; + case innerty::DifferentNumberOfResults: + return "Different number of results"; + case innerty::DifferentReturnValueConventions: + return "Different return value conventions"; + case innerty::ABIIncompatibleReturnValues: + return "ABI incompatible return values"; + case innerty::DifferentErrorResultConventions: + return "Different error result conventions"; + case innerty::ABIIncompatibleErrorResults: + return "ABI incompatible error results"; + case innerty::DifferentNumberOfParameters: + return "Different number of parameters"; + + // These two have to do with specific parameters, so keep the error message + // non-plural. + case innerty::DifferingParameterConvention: + return "Differing parameter convention"; + case innerty::ABIIncompatibleParameterType: + return "ABI incompatible parameter type."; + case innerty::ABIEscapeToNoEscapeConversion: + return "Escape to no escape conversion"; + } + llvm_unreachable("Covered switch isn't completely covered?!"); +} + +static DeclContext *getDeclContextForExpansion(const SILFunction &f) { + auto *dc = f.getDeclContext(); + if (!dc) + dc = f.getModule().getSwiftModule(); + auto *currentModule = f.getModule().getSwiftModule(); + if (!dc || !dc->isChildContextOf(currentModule)) + dc = currentModule; + return dc; +} + +TypeExpansionContext::TypeExpansionContext(const SILFunction &f) + : expansion(f.getResilienceExpansion()), + inContext(getDeclContextForExpansion(f)), + isContextWholeModule(f.getModule().isWholeModule()) {} + +CanSILFunctionType SILFunction::getLoweredFunctionTypeInContext( + TypeExpansionContext context) const { + auto origFunTy = getLoweredFunctionType(); + auto &M = getModule(); + auto funTy = M.Types.getLoweredType(origFunTy , context); + return cast(funTy.getASTType()); +} diff --git a/test/CXXInterop/Inputs/cxx_constructors.h b/test/CXXInterop/Inputs/cxx_constructors.h new file mode 100644 index 0000000000000..497b1c6f0085a --- /dev/null +++ b/test/CXXInterop/Inputs/cxx_constructors.h @@ -0,0 +1,22 @@ +struct ExplicitDefaultConstructor { + ExplicitDefaultConstructor() : x(42) {} + int x; +}; + +struct ImplicitDefaultConstructor { + int x = 42; +}; + +struct MemberOfClassType { + ImplicitDefaultConstructor member; +}; + +struct DefaultConstructorDeleted { + DefaultConstructorDeleted() = delete; + int &a; +}; + +struct ConstructorWithParam { + ConstructorWithParam(int val) : x(val) {} + int x; +}; diff --git a/test/CXXInterop/Inputs/module.modulemap b/test/CXXInterop/Inputs/module.modulemap new file mode 100644 index 0000000000000..11f93f00a5fc2 --- /dev/null +++ b/test/CXXInterop/Inputs/module.modulemap @@ -0,0 +1,3 @@ +module CxxConstructors { + header "cxx_constructors.h" +} diff --git a/test/CXXInterop/cxx-constructors-executable.swift b/test/CXXInterop/cxx-constructors-executable.swift new file mode 100644 index 0000000000000..1a9531f35865b --- /dev/null +++ b/test/CXXInterop/cxx-constructors-executable.swift @@ -0,0 +1,37 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -I %S/Inputs/ -o %t/cxx_interop -Xfrontend -enable-cxx-interop +// RUN: %target-codesign %t/cxx_interop +// RUN: %target-run %t/cxx_interop +// +// REQUIRES: executable_test + +import StdlibUnittest +import CxxConstructors + +var CxxConstructorTestSuite = TestSuite("CxxConstructors") + +CxxConstructorTestSuite.test("ExplicitDefaultConstructor") { + let instance = ExplicitDefaultConstructor() + + expectEqual(42, instance.x) +} + +CxxConstructorTestSuite.test("ImplicitDefaultConstructor") { + let instance = ImplicitDefaultConstructor() + + expectEqual(42, instance.x) +} + +CxxConstructorTestSuite.test("MemberOfClassType") { + let instance = MemberOfClassType() + + expectEqual(42, instance.member.x) +} + +CxxConstructorTestSuite.test("ConstructorWithParam") { + let instance = ConstructorWithParam(123456) + + expectEqual(123456, instance.x) +} + +runAllTests() diff --git a/test/CXXInterop/cxx-constructors-ir.swift b/test/CXXInterop/cxx-constructors-ir.swift new file mode 100644 index 0000000000000..cf1124505f9f2 --- /dev/null +++ b/test/CXXInterop/cxx-constructors-ir.swift @@ -0,0 +1,14 @@ +// RUN: %target-swift-frontend -I %S/Inputs -enable-cxx-interop -emit-ir %s | %FileCheck %s + +import CxxConstructors + +// Note: +// - The `this` parameter should carry a `noalias` attribute, as it is +// guaranteed that nothing will alias the object before it has been fully +// constructed. It should also carry an `sret` attribute to indicate that this +// is an out parameter for a structure being returned by the function. +// - The `this` parameter should _not_ carry a `nocapture` attribute (unlike +// Swift constructors that return their result indirectly) because the C++ has +// explicit access to `this` and may capture it. +// CHECK: call void @_ZN20ConstructorWithParamC2Ei(%struct.ConstructorWithParam* noalias sret %{{[0-9]+}}, i32 42) +let _ = ConstructorWithParam(42) diff --git a/test/CXXInterop/cxx-constructors-module-interface.swift b/test/CXXInterop/cxx-constructors-module-interface.swift new file mode 100644 index 0000000000000..5c2f0b2dff0a2 --- /dev/null +++ b/test/CXXInterop/cxx-constructors-module-interface.swift @@ -0,0 +1,20 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=CxxConstructors -I %S/Inputs/ -source-filename=x -enable-cxx-interop | %FileCheck %s + +// CHECK: struct ExplicitDefaultConstructor { +// CHECK-NEXT: var x: Int32 +// CHECK-NEXT: init() +// CHECK-NEXT: } +// CHECK-NEXT: struct ImplicitDefaultConstructor { +// CHECK-NEXT: var x: Int32 +// CHECK-NEXT: init() +// CHECK-NEXT: } +// CHECK-NEXT: struct MemberOfClassType { +// CHECK-NEXT: var member: ImplicitDefaultConstructor +// CHECK-NEXT: init() +// CHECK-NEXT: } +// CHECK-NEXT: struct DefaultConstructorDeleted { +// CHECK-NEXT: } +// CHECK-NEXT: struct ConstructorWithParam { +// CHECK-NEXT: var x: Int32 +// CHECK-NEXT: init(_ val: Int32) +// CHECK-NEXT: } diff --git a/test/CXXInterop/cxx-constructors-sil.swift b/test/CXXInterop/cxx-constructors-sil.swift new file mode 100644 index 0000000000000..adbf551eb73c9 --- /dev/null +++ b/test/CXXInterop/cxx-constructors-sil.swift @@ -0,0 +1,13 @@ +// RUN: %target-swift-frontend -I %S/Inputs -enable-cxx-interop -emit-sil %s | %FileCheck %s + +import CxxConstructors + +// The most important thing to test here is that the constructor result is returned +// with an @out attribute. +// CHECK: [[VAR:%[0-9]+]] = alloc_stack $ConstructorWithParam +// CHECK: [[TYPE:%[0-9]+]] = metatype $@thin ConstructorWithParam.Type +// CHECK: [[LITERAL:%[0-9]+]] = integer_literal $Builtin.Int32, 42 +// CHECK: [[INT:%[0-9]+]] = struct $Int32 ([[LITERAL]] : $Builtin.Int32) +// CHECK: [[FUNC:%[0-9]+]] = function_ref @_ZN20ConstructorWithParamC1Ei : $@convention(c) (Int32, @thin ConstructorWithParam.Type) -> @out ConstructorWithParam +// CHECK: %{{[0-9]+}} = apply [[FUNC]]([[VAR]], [[INT]], [[TYPE]]) : $@convention(c) (Int32, @thin ConstructorWithParam.Type) -> @out ConstructorWithParam +let _ = ConstructorWithParam(42) diff --git a/test/CXXInterop/cxx-constructors-typecheck.swift b/test/CXXInterop/cxx-constructors-typecheck.swift new file mode 100644 index 0000000000000..6da3a097ee6dc --- /dev/null +++ b/test/CXXInterop/cxx-constructors-typecheck.swift @@ -0,0 +1,13 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown -I %S/Inputs -enable-cxx-interop + +import CxxConstructors + +let explicit = ExplicitDefaultConstructor() + +let implicit = ImplicitDefaultConstructor() + +let deletedImplicitly = ConstructorWithParam() // expected-error {{missing argument for parameter #1 in call}} + +let deletedExplicitly = DefaultConstructorDeleted() // expected-error {{cannot be constructed because it has no accessible initializers}} + +let withArg = ConstructorWithParam(42) From 7ad2eef26508ad92d8d92f2d5546913c0ced3bbd Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Thu, 26 Mar 2020 11:00:40 +0100 Subject: [PATCH 324/745] Only import constructors marked `noexcept`. --- lib/ClangImporter/ImportDecl.cpp | 7 ++++++- test/CXXInterop/Inputs/cxx_constructors.h | 8 ++++++-- test/CXXInterop/cxx-constructors-typecheck.swift | 3 +++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 9bf0a0e91f309..e5efce65fb99f 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3893,7 +3893,12 @@ namespace { AbstractFunctionDecl *result = nullptr; if (auto *ctordecl = dyn_cast(decl)) { - // TODO: Is failable, throws etc. correct? + // For the time being, we only import `noexcept` constructors. + // TODO: Import throwing constructors too. + auto *prototype = decl->getType()->getAs(); + if (!prototype || !prototype->hasNoexceptExceptionSpec()) + return nullptr; + DeclName ctorName(Impl.SwiftContext, DeclBaseName::createConstructor(), bodyParams); result = Impl.createDeclWithClangNode( diff --git a/test/CXXInterop/Inputs/cxx_constructors.h b/test/CXXInterop/Inputs/cxx_constructors.h index 497b1c6f0085a..d98e4c47979aa 100644 --- a/test/CXXInterop/Inputs/cxx_constructors.h +++ b/test/CXXInterop/Inputs/cxx_constructors.h @@ -1,5 +1,5 @@ struct ExplicitDefaultConstructor { - ExplicitDefaultConstructor() : x(42) {} + ExplicitDefaultConstructor() noexcept : x(42) {} int x; }; @@ -17,6 +17,10 @@ struct DefaultConstructorDeleted { }; struct ConstructorWithParam { - ConstructorWithParam(int val) : x(val) {} + ConstructorWithParam(int val) noexcept : x(val) {} int x; }; + +struct PotentiallyThrowingConstructor { + PotentiallyThrowingConstructor() {} +}; diff --git a/test/CXXInterop/cxx-constructors-typecheck.swift b/test/CXXInterop/cxx-constructors-typecheck.swift index 6da3a097ee6dc..63f78aab39e66 100644 --- a/test/CXXInterop/cxx-constructors-typecheck.swift +++ b/test/CXXInterop/cxx-constructors-typecheck.swift @@ -11,3 +11,6 @@ let deletedImplicitly = ConstructorWithParam() // expected-error {{missing argum let deletedExplicitly = DefaultConstructorDeleted() // expected-error {{cannot be constructed because it has no accessible initializers}} let withArg = ConstructorWithParam(42) + +// For the time being, we only import constructors marked `noexcept`. +let potentiallyThrowing = PotentiallyThrowingConstructor() // expected-error {{cannot be constructed because it has no accessible initializers}} From 384854810a60a69c8df8a8c0248e209adc28c295 Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Tue, 7 Apr 2020 06:58:23 +0200 Subject: [PATCH 325/745] Revert "Only import constructors marked `noexcept`." This reverts commit 29650d8c1f302708a32304d49c703c9ddbf30b75. As discussed here, we want to import all constructors (whether they are marked `noexcept` or not) as non-throwing initializers; https://forums.swift.org/t/handling-c-exceptions/34823/50 --- lib/ClangImporter/ImportDecl.cpp | 7 +------ test/CXXInterop/Inputs/cxx_constructors.h | 8 ++------ test/CXXInterop/cxx-constructors-typecheck.swift | 3 --- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index e5efce65fb99f..9bf0a0e91f309 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3893,12 +3893,7 @@ namespace { AbstractFunctionDecl *result = nullptr; if (auto *ctordecl = dyn_cast(decl)) { - // For the time being, we only import `noexcept` constructors. - // TODO: Import throwing constructors too. - auto *prototype = decl->getType()->getAs(); - if (!prototype || !prototype->hasNoexceptExceptionSpec()) - return nullptr; - + // TODO: Is failable, throws etc. correct? DeclName ctorName(Impl.SwiftContext, DeclBaseName::createConstructor(), bodyParams); result = Impl.createDeclWithClangNode( diff --git a/test/CXXInterop/Inputs/cxx_constructors.h b/test/CXXInterop/Inputs/cxx_constructors.h index d98e4c47979aa..497b1c6f0085a 100644 --- a/test/CXXInterop/Inputs/cxx_constructors.h +++ b/test/CXXInterop/Inputs/cxx_constructors.h @@ -1,5 +1,5 @@ struct ExplicitDefaultConstructor { - ExplicitDefaultConstructor() noexcept : x(42) {} + ExplicitDefaultConstructor() : x(42) {} int x; }; @@ -17,10 +17,6 @@ struct DefaultConstructorDeleted { }; struct ConstructorWithParam { - ConstructorWithParam(int val) noexcept : x(val) {} + ConstructorWithParam(int val) : x(val) {} int x; }; - -struct PotentiallyThrowingConstructor { - PotentiallyThrowingConstructor() {} -}; diff --git a/test/CXXInterop/cxx-constructors-typecheck.swift b/test/CXXInterop/cxx-constructors-typecheck.swift index 63f78aab39e66..6da3a097ee6dc 100644 --- a/test/CXXInterop/cxx-constructors-typecheck.swift +++ b/test/CXXInterop/cxx-constructors-typecheck.swift @@ -11,6 +11,3 @@ let deletedImplicitly = ConstructorWithParam() // expected-error {{missing argum let deletedExplicitly = DefaultConstructorDeleted() // expected-error {{cannot be constructed because it has no accessible initializers}} let withArg = ConstructorWithParam(42) - -// For the time being, we only import constructors marked `noexcept`. -let potentiallyThrowing = PotentiallyThrowingConstructor() // expected-error {{cannot be constructed because it has no accessible initializers}} From fd00bc1f01357ecd264b8216373c23014771c124 Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Wed, 13 May 2020 15:06:05 +0200 Subject: [PATCH 326/745] Move tests from `CXXInterop` to `Interop/Cxx`. This is the canonical location that we've agreed on. --- test/CXXInterop/Inputs/module.modulemap | 3 --- .../Cxx/class/Inputs/cxx-constructors.h} | 0 test/Interop/Cxx/class/Inputs/module.modulemap | 4 ++++ .../Cxx/class}/cxx-constructors-executable.swift | 0 .../Cxx/class}/cxx-constructors-ir.swift | 0 .../Cxx/class}/cxx-constructors-module-interface.swift | 0 .../Cxx/class}/cxx-constructors-sil.swift | 0 .../Cxx/class}/cxx-constructors-typecheck.swift | 0 8 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 test/CXXInterop/Inputs/module.modulemap rename test/{CXXInterop/Inputs/cxx_constructors.h => Interop/Cxx/class/Inputs/cxx-constructors.h} (100%) rename test/{CXXInterop => Interop/Cxx/class}/cxx-constructors-executable.swift (100%) rename test/{CXXInterop => Interop/Cxx/class}/cxx-constructors-ir.swift (100%) rename test/{CXXInterop => Interop/Cxx/class}/cxx-constructors-module-interface.swift (100%) rename test/{CXXInterop => Interop/Cxx/class}/cxx-constructors-sil.swift (100%) rename test/{CXXInterop => Interop/Cxx/class}/cxx-constructors-typecheck.swift (100%) diff --git a/test/CXXInterop/Inputs/module.modulemap b/test/CXXInterop/Inputs/module.modulemap deleted file mode 100644 index 11f93f00a5fc2..0000000000000 --- a/test/CXXInterop/Inputs/module.modulemap +++ /dev/null @@ -1,3 +0,0 @@ -module CxxConstructors { - header "cxx_constructors.h" -} diff --git a/test/CXXInterop/Inputs/cxx_constructors.h b/test/Interop/Cxx/class/Inputs/cxx-constructors.h similarity index 100% rename from test/CXXInterop/Inputs/cxx_constructors.h rename to test/Interop/Cxx/class/Inputs/cxx-constructors.h diff --git a/test/Interop/Cxx/class/Inputs/module.modulemap b/test/Interop/Cxx/class/Inputs/module.modulemap index 04c3bdedfda87..475c54c41a7e2 100644 --- a/test/Interop/Cxx/class/Inputs/module.modulemap +++ b/test/Interop/Cxx/class/Inputs/module.modulemap @@ -6,6 +6,10 @@ module TypeClassification { header "type-classification.h" } +module CxxConstructors { + header "cxx-constructors.h" +} + module MemberwiseInitializer { header "memberwise-initializer.h" } diff --git a/test/CXXInterop/cxx-constructors-executable.swift b/test/Interop/Cxx/class/cxx-constructors-executable.swift similarity index 100% rename from test/CXXInterop/cxx-constructors-executable.swift rename to test/Interop/Cxx/class/cxx-constructors-executable.swift diff --git a/test/CXXInterop/cxx-constructors-ir.swift b/test/Interop/Cxx/class/cxx-constructors-ir.swift similarity index 100% rename from test/CXXInterop/cxx-constructors-ir.swift rename to test/Interop/Cxx/class/cxx-constructors-ir.swift diff --git a/test/CXXInterop/cxx-constructors-module-interface.swift b/test/Interop/Cxx/class/cxx-constructors-module-interface.swift similarity index 100% rename from test/CXXInterop/cxx-constructors-module-interface.swift rename to test/Interop/Cxx/class/cxx-constructors-module-interface.swift diff --git a/test/CXXInterop/cxx-constructors-sil.swift b/test/Interop/Cxx/class/cxx-constructors-sil.swift similarity index 100% rename from test/CXXInterop/cxx-constructors-sil.swift rename to test/Interop/Cxx/class/cxx-constructors-sil.swift diff --git a/test/CXXInterop/cxx-constructors-typecheck.swift b/test/Interop/Cxx/class/cxx-constructors-typecheck.swift similarity index 100% rename from test/CXXInterop/cxx-constructors-typecheck.swift rename to test/Interop/Cxx/class/cxx-constructors-typecheck.swift From e6067275a6659cfab8640e68e93a5bba36ddca20 Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Thu, 14 May 2020 11:45:19 +0200 Subject: [PATCH 327/745] Duplicate changes to GenClangType in ClangTypeConverter. See discussion here: https://github.com/apple/swift/pull/30630#discussion_r398967412 --- lib/AST/ClangTypeConverter.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index 9fd13f797798d..d2316dd7415ea 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -461,7 +461,19 @@ clang::QualType ClangTypeConverter::visitProtocolType(ProtocolType *type) { // Metatypes can be converted to Class when they are metatypes for concrete // classes. https://github.com/apple/swift/pull/27479#discussion_r344418131 clang::QualType ClangTypeConverter::visitMetatypeType(MetatypeType *type) { - return getClangMetatypeType(ClangASTContext); + assert(type->hasRepresentation() && + "metatype should have been assigned a representation"); + switch (type->getRepresentation()) { + case MetatypeRepresentation::Thin: + return ClangASTContext.VoidTy; + + case MetatypeRepresentation::Thick: + llvm_unreachable("thick metatypes don't have a corresponding Clang type"); + + case MetatypeRepresentation::ObjC: + return getClangMetatypeType(ClangASTContext); + } + llvm_unreachable("bad representation"); } // TODO: [stronger-checking-in-clang-type-conversion] From b2c5a3eeed4e0d80c819f98c816481fbedc49526 Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Mon, 11 May 2020 15:14:29 +0200 Subject: [PATCH 328/745] Add a constructor thunk if required to add additional constructor arguments. Also add more IR tests and make various other changes. --- lib/ClangImporter/ImportDecl.cpp | 14 ++-- lib/IRGen/GenCall.cpp | 40 ++++++---- lib/IRGen/GenDecl.cpp | 78 +++++++++++++++++++ .../Cxx/class/Inputs/cxx-constructors.h | 14 +++- .../class/cxx-constructors-executable.swift | 9 +-- .../Cxx/class/cxx-constructors-ir.swift | 50 +++++++++--- .../cxx-constructors-module-interface.swift | 14 ++++ 7 files changed, 182 insertions(+), 37 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 9bf0a0e91f309..2a5983d0a8733 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3439,14 +3439,19 @@ namespace { bool hasReferenceableFields = !members.empty(); - if (hasZeroInitializableStorage && - !Impl.SwiftContext.LangOpts.EnableCXXInterop) { - // Add constructors for the struct. + const clang::CXXRecordDecl *cxxRecordDecl = + dyn_cast(decl); + if (hasZeroInitializableStorage && !cxxRecordDecl) { + // Add default constructor for the struct if compiling in C mode. + // If we're compiling for C++, we'll import the C++ default constructor + // (if there is one), so we don't need to synthesize one here. ctors.push_back(createDefaultConstructor(Impl, result)); } + bool hasUserDeclaredConstructor = + cxxRecordDecl && cxxRecordDecl->hasUserDeclaredConstructor(); if (hasReferenceableFields && hasMemberwiseInitializer && - !Impl.SwiftContext.LangOpts.EnableCXXInterop) { + !hasUserDeclaredConstructor) { // The default zero initializer suppresses the implicit value // constructor that would normally be formed, so we have to add that // explicitly as well. @@ -3893,7 +3898,6 @@ namespace { AbstractFunctionDecl *result = nullptr; if (auto *ctordecl = dyn_cast(decl)) { - // TODO: Is failable, throws etc. correct? DeclName ctorName(Impl.SwiftContext, DeclBaseName::createConstructor(), bodyParams); result = Impl.createDeclWithClangNode( diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 3fa9365d3c04f..b051e860bd984 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -430,7 +430,7 @@ namespace { private: void expand(SILParameterInfo param); - llvm::Type *addIndirectResult(bool noCapture = true); + llvm::Type *addIndirectResult(); SILFunctionConventions getSILFuncConventions() const { return SILFunctionConventions(FnType, IGM.getSILModule()); @@ -484,8 +484,7 @@ llvm::Type *SignatureExpansion::addIndirectResult() { auto resultType = getSILFuncConventions().getSILResultType( IGM.getMaximalTypeExpansionContext()); const TypeInfo &resultTI = IGM.getTypeInfo(resultType); - addIndirectResultAttributes(IGM, Attrs, ParamIRTypes.size(), claimSRet(), - noCapture); + addIndirectResultAttributes(IGM, Attrs, ParamIRTypes.size(), claimSRet()); addPointerParameter(resultTI.getStorageType()); return IGM.VoidTy; } @@ -1272,6 +1271,16 @@ void SignatureExpansion::expandExternalSignatureTypes() { SmallVector paramTys; auto const &clangCtx = IGM.getClangASTContext(); + bool formalIndirectResult = FnType->getNumResults() > 0 && + FnType->getSingleResult().isFormalIndirect(); + if (formalIndirectResult) { + auto resultType = getSILFuncConventions().getSingleSILResultType( + IGM.getMaximalTypeExpansionContext()); + auto clangTy = + IGM.getClangASTContext().getPointerType(IGM.getClangType(resultType)); + paramTys.push_back(clangTy); + } + switch (FnType->getRepresentation()) { case SILFunctionTypeRepresentation::ObjCMethod: { // ObjC methods take their 'self' argument first, followed by an @@ -1336,17 +1345,8 @@ void SignatureExpansion::expandExternalSignatureTypes() { } // If we return indirectly, that is the first parameter type. - bool formalIndirect = FnType->getNumResults() > 0 && - FnType->getSingleResult().isFormalIndirect(); - if (formalIndirect || returnInfo.isIndirect()) { - // Specify "nocapture" if we're returning the result indirectly for - // low-level ABI reasons, as the called function never sees the implicit - // output parameter. - // On the other hand, if the result is a formal indirect result in SIL, that - // means that the Clang function has an explicit output parameter (e.g. it's - // a C++ constructor), which it might capture -- so don't specify - // "nocapture" in that case. - addIndirectResult(/* noCapture = */ returnInfo.isIndirect()); + if (returnInfo.isIndirect()) { + addIndirectResult(); } size_t firstParamToLowerNormally = 0; @@ -1432,6 +1432,13 @@ void SignatureExpansion::expandExternalSignatureTypes() { } } + if (formalIndirectResult) { + // If the result is a formal indirect result in SIL, that means that the + // Clang function has an explicit output parameter (e.g. it's a C++ + // constructor), which it might capture -- so don't specify "nocapture". + addIndirectResultAttributes(IGM, Attrs, 0, claimSRet(), /* nocapture = */ false); + } + if (returnInfo.isIndirect() || returnInfo.isIgnore()) { ResultIRType = IGM.VoidTy; } else { @@ -2812,6 +2819,11 @@ static void externalizeArguments(IRGenFunction &IGF, const Callee &callee, == SILFunctionTypeRepresentation::Block) { // Ignore the physical block-object parameter. firstParam += 1; + // Or the indirect result parameter. + } else if (fnType->getNumResults() > 0 && + fnType->getSingleResult().isFormalIndirect()) { + // Ignore the indirect result parameter. + firstParam += 1; } for (unsigned i = firstParam, e = FI.arg_size(); i != e; ++i) { diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 15e88148e0afe..e8aff92311183 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -38,6 +38,8 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/GlobalDecl.h" +#include "clang/CodeGen/CodeGenABITypes.h" +#include "clang/CodeGen/ModuleBuilder.h" #include "llvm/ADT/SmallString.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/GlobalAlias.h" @@ -2761,6 +2763,77 @@ void IRGenModule::emitDynamicReplacementOriginalFunctionThunk(SILFunction *f) { IGF.Builder.CreateRet(Res); } +/// If the calling convention for `ctor` doesn't match the calling convention +/// that we assumed for it when we imported it as `initializer`, emit and +/// return a thunk that conforms to the assumed calling convention. The thunk +/// is marked `alwaysinline`, so it doesn't generate any runtime overhead. +/// If the assumed calling convention was correct, just return `ctor`. +/// +/// See also comments in CXXMethodConventions in SIL/IR/SILFunctionType.cpp. +static llvm::Constant * +emitCXXConstructorThunkIfNeeded(IRGenModule &IGM, SILFunction *initializer, + const clang::CXXConstructorDecl *ctor, + const LinkEntity &entity, + llvm::Constant *ctorAddress) { + Signature signature = IGM.getSignature(initializer->getLoweredFunctionType()); + + llvm::FunctionType *assumedFnType = signature.getType(); + llvm::FunctionType *ctorFnType = + cast(ctorAddress->getType()->getPointerElementType()); + + if (assumedFnType == ctorFnType) { + return ctorAddress; + } + + // The thunk has private linkage, so it doesn't need to have a predictable + // mangled name -- we just need to make sure the name is unique. + llvm::SmallString<32> name; + llvm::raw_svector_ostream stream(name); + stream << "__swift_cxx_ctor"; + entity.mangle(stream); + + llvm::Function *thunk = llvm::Function::Create( + assumedFnType, llvm::Function::PrivateLinkage, name, &IGM.Module); + + thunk->setCallingConv(llvm::CallingConv::C); + + llvm::AttrBuilder attrBuilder; + IGM.constructInitialFnAttributes(attrBuilder); + attrBuilder.addAttribute(llvm::Attribute::AlwaysInline); + llvm::AttributeList attr = signature.getAttributes().addAttributes( + IGM.getLLVMContext(), llvm::AttributeList::FunctionIndex, attrBuilder); + thunk->setAttributes(attr); + + IRGenFunction subIGF(IGM, thunk); + if (IGM.DebugInfo) + IGM.DebugInfo->emitArtificialFunction(subIGF, thunk); + + SmallVector Args; + for (auto i = thunk->arg_begin(), e = thunk->arg_end(); i != e; ++i) { + auto *argTy = i->getType(); + auto *paramTy = ctorFnType->getParamType(i - thunk->arg_begin()); + if (paramTy != argTy) + Args.push_back(subIGF.coerceValue(i, paramTy, IGM.DataLayout)); + else + Args.push_back(i); + } + + clang::CodeGen::ImplicitCXXConstructorArgs implicitArgs = + clang::CodeGen::getImplicitCXXConstructorArgs(IGM.ClangCodeGen->CGM(), + ctor); + for (size_t i = 0; i < implicitArgs.Prefix.size(); ++i) { + Args.insert(Args.begin() + 1 + i, implicitArgs.Prefix[i]); + } + for (const auto &arg : implicitArgs.Suffix) { + Args.push_back(arg); + } + + subIGF.Builder.CreateCall(ctorFnType, ctorAddress, Args); + subIGF.Builder.CreateRetVoid(); + + return thunk; +} + /// Find the entry point for a SIL function. llvm::Function *IRGenModule::getAddrOfSILFunction( SILFunction *f, ForDefinition_t forDefinition, @@ -2789,6 +2862,11 @@ llvm::Function *IRGenModule::getAddrOfSILFunction( if (auto clangDecl = f->getClangDecl()) { auto globalDecl = getClangGlobalDeclForFunction(clangDecl); clangAddr = getAddrOfClangGlobalDecl(globalDecl, forDefinition); + + if (auto ctor = dyn_cast(clangDecl)) { + clangAddr = + emitCXXConstructorThunkIfNeeded(*this, f, ctor, entity, clangAddr); + } } bool isDefinition = f->isDefinition(); diff --git a/test/Interop/Cxx/class/Inputs/cxx-constructors.h b/test/Interop/Cxx/class/Inputs/cxx-constructors.h index 497b1c6f0085a..72da5a217ab81 100644 --- a/test/Interop/Cxx/class/Inputs/cxx-constructors.h +++ b/test/Interop/Cxx/class/Inputs/cxx-constructors.h @@ -17,6 +17,18 @@ struct DefaultConstructorDeleted { }; struct ConstructorWithParam { - ConstructorWithParam(int val) : x(val) {} + ConstructorWithParam(int val) : x(val + 42) {} int x; }; + +struct Base {}; + +struct ArgType { + int i = 42; +}; + +struct HasVirtualBase : public virtual Base { + HasVirtualBase() = delete; + HasVirtualBase(ArgType Arg) {} + int i; +}; diff --git a/test/Interop/Cxx/class/cxx-constructors-executable.swift b/test/Interop/Cxx/class/cxx-constructors-executable.swift index 1a9531f35865b..06417c3d1f731 100644 --- a/test/Interop/Cxx/class/cxx-constructors-executable.swift +++ b/test/Interop/Cxx/class/cxx-constructors-executable.swift @@ -1,7 +1,4 @@ -// RUN: %empty-directory(%t) -// RUN: %target-build-swift %s -I %S/Inputs/ -o %t/cxx_interop -Xfrontend -enable-cxx-interop -// RUN: %target-codesign %t/cxx_interop -// RUN: %target-run %t/cxx_interop +// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-cxx-interop) // // REQUIRES: executable_test @@ -29,9 +26,9 @@ CxxConstructorTestSuite.test("MemberOfClassType") { } CxxConstructorTestSuite.test("ConstructorWithParam") { - let instance = ConstructorWithParam(123456) + let instance = ConstructorWithParam(2) - expectEqual(123456, instance.x) + expectEqual(44, instance.x) } runAllTests() diff --git a/test/Interop/Cxx/class/cxx-constructors-ir.swift b/test/Interop/Cxx/class/cxx-constructors-ir.swift index cf1124505f9f2..1e2d3f7dffeef 100644 --- a/test/Interop/Cxx/class/cxx-constructors-ir.swift +++ b/test/Interop/Cxx/class/cxx-constructors-ir.swift @@ -1,14 +1,42 @@ -// RUN: %target-swift-frontend -I %S/Inputs -enable-cxx-interop -emit-ir %s | %FileCheck %s +// Target-specific tests for C++ constructor call code generation. + +// RUN: %swift -module-name Swift -target x86_64-apple-macosx10.9 -dump-clang-diagnostics -I %S/Inputs -enable-cxx-interop -emit-ir %s -parse-stdlib -parse-as-library -disable-legacy-type-info | %FileCheck %s -check-prefix=ITANIUM_X64 +// RUN: %swift -module-name Swift -target armv7-none-linux-androideabi -dump-clang-diagnostics -I %S/Inputs -enable-cxx-interop -emit-ir %s -parse-stdlib -parse-as-library -disable-legacy-type-info | %FileCheck %s -check-prefix=ITANIUM_ARM +// RUN: %swift -module-name Swift -target x86_64-unknown-windows-msvc -dump-clang-diagnostics -I %S/Inputs -enable-cxx-interop -emit-ir %s -parse-stdlib -parse-as-library -disable-legacy-type-info | %FileCheck %s -check-prefix=MICROSOFT_X64 import CxxConstructors -// Note: -// - The `this` parameter should carry a `noalias` attribute, as it is -// guaranteed that nothing will alias the object before it has been fully -// constructed. It should also carry an `sret` attribute to indicate that this -// is an out parameter for a structure being returned by the function. -// - The `this` parameter should _not_ carry a `nocapture` attribute (unlike -// Swift constructors that return their result indirectly) because the C++ has -// explicit access to `this` and may capture it. -// CHECK: call void @_ZN20ConstructorWithParamC2Ei(%struct.ConstructorWithParam* noalias sret %{{[0-9]+}}, i32 42) -let _ = ConstructorWithParam(42) +typealias Void = () + +public func createHasVirtualBase() -> HasVirtualBase { + // - The `this` parameter should carry a `noalias` attribute, as it is + // guaranteed that nothing will alias the object before it has been fully + // constructed. It should also carry an `sret` attribute to indicate that + // this is an out parameter for a structure being returned by the function. + // Note that this doesn't apply on ABIs (Itanium ARM, Microsoft x64) + // where we insert an (inlined) thunk; in this case, we're getting the + // attributes of the constructor that was generated by Clang, which doesn't + // insert these attributes. + // + // - The `this` parameter should _not_ carry a `nocapture` attribute (unlike + // Swift constructors that return their result indirectly) because the C++ + // constructor has explicit access to `this` and may capture it. + // + // ITANIUM_X64: define swiftcc { i8*, i32 } @"$ss20createHasVirtualBaseSo0bcD0VyF"() + // ITANIUM_X64-NOT: define + // ITANIUM_X64: call void @_ZN14HasVirtualBaseC1E7ArgType(%struct.HasVirtualBase* noalias sret %{{[0-9]+}}, i32 %{{[0-9]+}}) + // + // ITANIUM_ARM: define protected swiftcc { i8*, i32 } @"$ss20createHasVirtualBaseSo0bcD0VyF"() + // To verify that the thunk is inlined, make sure there's no intervening + // `define`, i.e. the call to the C++ constructor happens in + // createHasVirtualBase(), not some later function. + // ITANIUM_ARM-NOT: define + // Note `this` return type. + // ITANIUM_ARM: call %struct.HasVirtualBase* @_ZN14HasVirtualBaseC1E7ArgType(%struct.HasVirtualBase* %{{[0-9]+}}, [1 x i32] %{{[0-9]+}}) + // + // MICROSOFT_X64: define dllexport swiftcc { i8*, i32 } @"$ss20createHasVirtualBaseSo0bcD0VyF"() + // MICROSOFT_X64-NOT: define + // Note `this` return type and implicit "most derived" argument. + // MICROSOFT_X64: call %struct.HasVirtualBase* @"??0HasVirtualBase@@QEAA@UArgType@@@Z"(%struct.HasVirtualBase* %{{[0-9]+}}, i32 %{{[0-9]+}}, i32 1) + return HasVirtualBase(ArgType()) +} diff --git a/test/Interop/Cxx/class/cxx-constructors-module-interface.swift b/test/Interop/Cxx/class/cxx-constructors-module-interface.swift index 5c2f0b2dff0a2..696b5ea49ba8e 100644 --- a/test/Interop/Cxx/class/cxx-constructors-module-interface.swift +++ b/test/Interop/Cxx/class/cxx-constructors-module-interface.swift @@ -7,10 +7,12 @@ // CHECK-NEXT: struct ImplicitDefaultConstructor { // CHECK-NEXT: var x: Int32 // CHECK-NEXT: init() +// CHECK-NEXT: init(x: Int32) // CHECK-NEXT: } // CHECK-NEXT: struct MemberOfClassType { // CHECK-NEXT: var member: ImplicitDefaultConstructor // CHECK-NEXT: init() +// CHECK-NEXT: init(member: ImplicitDefaultConstructor) // CHECK-NEXT: } // CHECK-NEXT: struct DefaultConstructorDeleted { // CHECK-NEXT: } @@ -18,3 +20,15 @@ // CHECK-NEXT: var x: Int32 // CHECK-NEXT: init(_ val: Int32) // CHECK-NEXT: } +// CHECK-NEXT: struct Base { +// CHECK-NEXT: init() +// CHECK-NEXT: } +// CHECK-NEXT: struct ArgType { +// CHECK-NEXT: var i: Int32 +// CHECK-NEXT: init() +// CHECK-NEXT: init(i: Int32) +// CHECK-NEXT: } +// CHECK-NEXT: struct HasVirtualBase { +// CHECK-NEXT: var i: Int32 +// CHECK-NEXT: init(_ Arg: ArgType) +// CHECK-NEXT: } From 3066e16c37e26a5fea370877409b4d0bd1c0fea6 Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Fri, 15 May 2020 14:11:54 +0200 Subject: [PATCH 329/745] Remove redundant "cxx" from test names. --- .../Cxx/class/Inputs/{cxx-constructors.h => constructors.h} | 0 test/Interop/Cxx/class/Inputs/module.modulemap | 4 ++-- ...ructors-executable.swift => constructors-executable.swift} | 2 +- .../{cxx-constructors-ir.swift => constructors-ir.swift} | 2 +- ...le-interface.swift => constructors-module-interface.swift} | 2 +- .../{cxx-constructors-sil.swift => constructors-sil.swift} | 2 +- ...ructors-typecheck.swift => constructors-typechecker.swift} | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename test/Interop/Cxx/class/Inputs/{cxx-constructors.h => constructors.h} (100%) rename test/Interop/Cxx/class/{cxx-constructors-executable.swift => constructors-executable.swift} (97%) rename test/Interop/Cxx/class/{cxx-constructors-ir.swift => constructors-ir.swift} (99%) rename test/Interop/Cxx/class/{cxx-constructors-module-interface.swift => constructors-module-interface.swift} (87%) rename test/Interop/Cxx/class/{cxx-constructors-sil.swift => constructors-sil.swift} (97%) rename test/Interop/Cxx/class/{cxx-constructors-typecheck.swift => constructors-typechecker.swift} (95%) diff --git a/test/Interop/Cxx/class/Inputs/cxx-constructors.h b/test/Interop/Cxx/class/Inputs/constructors.h similarity index 100% rename from test/Interop/Cxx/class/Inputs/cxx-constructors.h rename to test/Interop/Cxx/class/Inputs/constructors.h diff --git a/test/Interop/Cxx/class/Inputs/module.modulemap b/test/Interop/Cxx/class/Inputs/module.modulemap index 475c54c41a7e2..0370a978cb949 100644 --- a/test/Interop/Cxx/class/Inputs/module.modulemap +++ b/test/Interop/Cxx/class/Inputs/module.modulemap @@ -6,8 +6,8 @@ module TypeClassification { header "type-classification.h" } -module CxxConstructors { - header "cxx-constructors.h" +module Constructors { + header "constructors.h" } module MemberwiseInitializer { diff --git a/test/Interop/Cxx/class/cxx-constructors-executable.swift b/test/Interop/Cxx/class/constructors-executable.swift similarity index 97% rename from test/Interop/Cxx/class/cxx-constructors-executable.swift rename to test/Interop/Cxx/class/constructors-executable.swift index 06417c3d1f731..04a8b9912b926 100644 --- a/test/Interop/Cxx/class/cxx-constructors-executable.swift +++ b/test/Interop/Cxx/class/constructors-executable.swift @@ -3,7 +3,7 @@ // REQUIRES: executable_test import StdlibUnittest -import CxxConstructors +import Constructors var CxxConstructorTestSuite = TestSuite("CxxConstructors") diff --git a/test/Interop/Cxx/class/cxx-constructors-ir.swift b/test/Interop/Cxx/class/constructors-ir.swift similarity index 99% rename from test/Interop/Cxx/class/cxx-constructors-ir.swift rename to test/Interop/Cxx/class/constructors-ir.swift index 1e2d3f7dffeef..3838ba51b681c 100644 --- a/test/Interop/Cxx/class/cxx-constructors-ir.swift +++ b/test/Interop/Cxx/class/constructors-ir.swift @@ -4,7 +4,7 @@ // RUN: %swift -module-name Swift -target armv7-none-linux-androideabi -dump-clang-diagnostics -I %S/Inputs -enable-cxx-interop -emit-ir %s -parse-stdlib -parse-as-library -disable-legacy-type-info | %FileCheck %s -check-prefix=ITANIUM_ARM // RUN: %swift -module-name Swift -target x86_64-unknown-windows-msvc -dump-clang-diagnostics -I %S/Inputs -enable-cxx-interop -emit-ir %s -parse-stdlib -parse-as-library -disable-legacy-type-info | %FileCheck %s -check-prefix=MICROSOFT_X64 -import CxxConstructors +import Constructors typealias Void = () diff --git a/test/Interop/Cxx/class/cxx-constructors-module-interface.swift b/test/Interop/Cxx/class/constructors-module-interface.swift similarity index 87% rename from test/Interop/Cxx/class/cxx-constructors-module-interface.swift rename to test/Interop/Cxx/class/constructors-module-interface.swift index 696b5ea49ba8e..a41e120f0b301 100644 --- a/test/Interop/Cxx/class/cxx-constructors-module-interface.swift +++ b/test/Interop/Cxx/class/constructors-module-interface.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-ide-test -print-module -module-to-print=CxxConstructors -I %S/Inputs/ -source-filename=x -enable-cxx-interop | %FileCheck %s +// RUN: %target-swift-ide-test -print-module -module-to-print=Constructors -I %S/Inputs/ -source-filename=x -enable-cxx-interop | %FileCheck %s // CHECK: struct ExplicitDefaultConstructor { // CHECK-NEXT: var x: Int32 diff --git a/test/Interop/Cxx/class/cxx-constructors-sil.swift b/test/Interop/Cxx/class/constructors-sil.swift similarity index 97% rename from test/Interop/Cxx/class/cxx-constructors-sil.swift rename to test/Interop/Cxx/class/constructors-sil.swift index adbf551eb73c9..9eef642b22d0a 100644 --- a/test/Interop/Cxx/class/cxx-constructors-sil.swift +++ b/test/Interop/Cxx/class/constructors-sil.swift @@ -1,6 +1,6 @@ // RUN: %target-swift-frontend -I %S/Inputs -enable-cxx-interop -emit-sil %s | %FileCheck %s -import CxxConstructors +import Constructors // The most important thing to test here is that the constructor result is returned // with an @out attribute. diff --git a/test/Interop/Cxx/class/cxx-constructors-typecheck.swift b/test/Interop/Cxx/class/constructors-typechecker.swift similarity index 95% rename from test/Interop/Cxx/class/cxx-constructors-typecheck.swift rename to test/Interop/Cxx/class/constructors-typechecker.swift index 6da3a097ee6dc..3cc008807c6a1 100644 --- a/test/Interop/Cxx/class/cxx-constructors-typecheck.swift +++ b/test/Interop/Cxx/class/constructors-typechecker.swift @@ -1,6 +1,6 @@ // RUN: %target-typecheck-verify-swift -verify-ignore-unknown -I %S/Inputs -enable-cxx-interop -import CxxConstructors +import Constructors let explicit = ExplicitDefaultConstructor() From 5644137ea0696164c5b5835179b1ec450d508c88 Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Fri, 15 May 2020 14:22:11 +0200 Subject: [PATCH 330/745] Eliminate duplication of code for adding empty argument names. --- lib/ClangImporter/ImportName.cpp | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 65df344f664a0..1a02a454ede0d 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -1366,6 +1366,16 @@ static bool suppressFactoryMethodAsInit(const clang::ObjCMethodDecl *method, initKind == CtorInitializerKind::ConvenienceFactory); } +static void +addEmptyArgNamesForCxxFunc(const clang::FunctionDecl *funcDecl, + SmallVectorImpl &argumentNames) { + for (size_t i = 0; i < funcDecl->param_size(); ++i) { + argumentNames.push_back(StringRef()); + } + if (funcDecl->isVariadic()) + argumentNames.push_back(StringRef()); +} + ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, ImportNameVersion version, clang::DeclarationName givenName) { @@ -1604,13 +1614,8 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, isFunction = true; result.info.initKind = CtorInitializerKind::Designated; baseName = "init"; - // Add empty argument names. if (auto ctor = dyn_cast(D)) { - for (size_t i = 0; i < ctor->param_size(); ++i) { - argumentNames.push_back(StringRef()); - } - if (ctor->isVariadic()) - argumentNames.push_back(StringRef()); + addEmptyArgNamesForCxxFunc(ctor, argumentNames); } break; @@ -1684,16 +1689,9 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, } } - // For C functions, create empty argument names. if (auto function = dyn_cast(D)) { isFunction = true; - params = {function->param_begin(), function->param_end()}; - for (auto param : params) { - (void)param; - argumentNames.push_back(StringRef()); - } - if (function->isVariadic()) - argumentNames.push_back(StringRef()); + addEmptyArgNamesForCxxFunc(function, argumentNames); } break; From bed26039446c189a82c453126e0a622e43c6256d Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Wed, 20 May 2020 07:47:20 +0200 Subject: [PATCH 331/745] Various changes after merging master: - Adapt tests to changes that have happened in the meantime (e.g. `HasVirtualBase` is rightly no longer considered loadable) - Don't import copy or move constructors (noticed this because references are now imported correctly, so copy and move constructors suddenly started showing up in the SIL test) - Don't try to define an implicitly-deleted default constructor (this previously broke loadable-types-silgen.swift) --- lib/ClangImporter/ImportDecl.cpp | 15 +++++++++++---- test/Interop/Cxx/class/Inputs/constructors.h | 5 +++++ test/Interop/Cxx/class/constructors-ir.swift | 6 +++--- .../Cxx/class/constructors-module-interface.swift | 3 +++ 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 2a5983d0a8733..46a135d9573d9 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3482,8 +3482,9 @@ namespace { result->setHasUnreferenceableStorage(hasUnreferenceableStorage); - if (auto cxxRecordDecl = dyn_cast(decl)) { - result->setIsCxxNonTrivial(!cxxRecordDecl->isTriviallyCopyable()); + if (cxxRecordDecl) { + result->setIsCxxNonTrivial( + !cxxRecordDecl->isTriviallyCopyable()); for (auto ctor : cxxRecordDecl->ctors()) { if (ctor->isCopyConstructor() && @@ -3518,8 +3519,9 @@ namespace { clang::CXXConstructorDecl *ctor = clangSema.DeclareImplicitDefaultConstructor( const_cast(decl)); - clangSema.DefineImplicitDefaultConstructor(clang::SourceLocation(), - ctor); + if (!ctor->isDeleted()) + clangSema.DefineImplicitDefaultConstructor(clang::SourceLocation(), + ctor); } return VisitRecordDecl(decl); @@ -3898,6 +3900,11 @@ namespace { AbstractFunctionDecl *result = nullptr; if (auto *ctordecl = dyn_cast(decl)) { + // Don't import copy constructor or move constructor -- these will be + // provided through the value witness table. + if (ctordecl->isCopyConstructor() || ctordecl->isMoveConstructor()) + return nullptr; + DeclName ctorName(Impl.SwiftContext, DeclBaseName::createConstructor(), bodyParams); result = Impl.createDeclWithClangNode( diff --git a/test/Interop/Cxx/class/Inputs/constructors.h b/test/Interop/Cxx/class/Inputs/constructors.h index 72da5a217ab81..7959be4e0a4fe 100644 --- a/test/Interop/Cxx/class/Inputs/constructors.h +++ b/test/Interop/Cxx/class/Inputs/constructors.h @@ -21,6 +21,11 @@ struct ConstructorWithParam { int x; }; +struct CopyAndMoveConstructor { + CopyAndMoveConstructor(const CopyAndMoveConstructor &) = default; + CopyAndMoveConstructor(CopyAndMoveConstructor &&) = default; +}; + struct Base {}; struct ArgType { diff --git a/test/Interop/Cxx/class/constructors-ir.swift b/test/Interop/Cxx/class/constructors-ir.swift index 3838ba51b681c..27df20e161098 100644 --- a/test/Interop/Cxx/class/constructors-ir.swift +++ b/test/Interop/Cxx/class/constructors-ir.swift @@ -22,11 +22,11 @@ public func createHasVirtualBase() -> HasVirtualBase { // Swift constructors that return their result indirectly) because the C++ // constructor has explicit access to `this` and may capture it. // - // ITANIUM_X64: define swiftcc { i8*, i32 } @"$ss20createHasVirtualBaseSo0bcD0VyF"() + // ITANIUM_X64: define swiftcc void @"$ss20createHasVirtualBaseSo0bcD0VyF"(%TSo14HasVirtualBaseV* noalias nocapture sret %0) // ITANIUM_X64-NOT: define // ITANIUM_X64: call void @_ZN14HasVirtualBaseC1E7ArgType(%struct.HasVirtualBase* noalias sret %{{[0-9]+}}, i32 %{{[0-9]+}}) // - // ITANIUM_ARM: define protected swiftcc { i8*, i32 } @"$ss20createHasVirtualBaseSo0bcD0VyF"() + // ITANIUM_ARM: define protected swiftcc void @"$ss20createHasVirtualBaseSo0bcD0VyF"(%TSo14HasVirtualBaseV* noalias nocapture sret %0) // To verify that the thunk is inlined, make sure there's no intervening // `define`, i.e. the call to the C++ constructor happens in // createHasVirtualBase(), not some later function. @@ -34,7 +34,7 @@ public func createHasVirtualBase() -> HasVirtualBase { // Note `this` return type. // ITANIUM_ARM: call %struct.HasVirtualBase* @_ZN14HasVirtualBaseC1E7ArgType(%struct.HasVirtualBase* %{{[0-9]+}}, [1 x i32] %{{[0-9]+}}) // - // MICROSOFT_X64: define dllexport swiftcc { i8*, i32 } @"$ss20createHasVirtualBaseSo0bcD0VyF"() + // MICROSOFT_X64: define dllexport swiftcc void @"$ss20createHasVirtualBaseSo0bcD0VyF"(%TSo14HasVirtualBaseV* noalias nocapture sret %0) // MICROSOFT_X64-NOT: define // Note `this` return type and implicit "most derived" argument. // MICROSOFT_X64: call %struct.HasVirtualBase* @"??0HasVirtualBase@@QEAA@UArgType@@@Z"(%struct.HasVirtualBase* %{{[0-9]+}}, i32 %{{[0-9]+}}, i32 1) diff --git a/test/Interop/Cxx/class/constructors-module-interface.swift b/test/Interop/Cxx/class/constructors-module-interface.swift index a41e120f0b301..acc321c4bca4e 100644 --- a/test/Interop/Cxx/class/constructors-module-interface.swift +++ b/test/Interop/Cxx/class/constructors-module-interface.swift @@ -15,11 +15,14 @@ // CHECK-NEXT: init(member: ImplicitDefaultConstructor) // CHECK-NEXT: } // CHECK-NEXT: struct DefaultConstructorDeleted { +// CHECK-NEXT: var a: UnsafeMutablePointer // CHECK-NEXT: } // CHECK-NEXT: struct ConstructorWithParam { // CHECK-NEXT: var x: Int32 // CHECK-NEXT: init(_ val: Int32) // CHECK-NEXT: } +// CHECK-NEXT: struct CopyAndMoveConstructor { +// CHECK-NEXT: } // CHECK-NEXT: struct Base { // CHECK-NEXT: init() // CHECK-NEXT: } From beaaa742c3e774b6739de26bb1a3f0c8761512a5 Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Mon, 25 May 2020 16:06:18 +0200 Subject: [PATCH 332/745] Don't put an `sret` attribute on the `this` argument. --- lib/IRGen/GenCall.cpp | 9 +++++++-- test/Interop/Cxx/class/constructors-ir.swift | 15 ++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index b051e860bd984..a2faf3b227c7b 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -1435,8 +1435,13 @@ void SignatureExpansion::expandExternalSignatureTypes() { if (formalIndirectResult) { // If the result is a formal indirect result in SIL, that means that the // Clang function has an explicit output parameter (e.g. it's a C++ - // constructor), which it might capture -- so don't specify "nocapture". - addIndirectResultAttributes(IGM, Attrs, 0, claimSRet(), /* nocapture = */ false); + // constructor). This means: + // - Don't mark it `sret`, as this should only be used for C++ return + // values. + // - The Clang function might capture the pointer, so don't specify + // `nocapture`. + addIndirectResultAttributes(IGM, Attrs, 0, /* allowSRet = */ false, + /* noCapture = */ false); } if (returnInfo.isIndirect() || returnInfo.isIgnore()) { diff --git a/test/Interop/Cxx/class/constructors-ir.swift b/test/Interop/Cxx/class/constructors-ir.swift index 27df20e161098..1886a95601861 100644 --- a/test/Interop/Cxx/class/constructors-ir.swift +++ b/test/Interop/Cxx/class/constructors-ir.swift @@ -11,12 +11,13 @@ typealias Void = () public func createHasVirtualBase() -> HasVirtualBase { // - The `this` parameter should carry a `noalias` attribute, as it is // guaranteed that nothing will alias the object before it has been fully - // constructed. It should also carry an `sret` attribute to indicate that - // this is an out parameter for a structure being returned by the function. - // Note that this doesn't apply on ABIs (Itanium ARM, Microsoft x64) - // where we insert an (inlined) thunk; in this case, we're getting the - // attributes of the constructor that was generated by Clang, which doesn't - // insert these attributes. + // constructed. Note that this doesn't apply on ABIs (Itanium ARM, + // Microsoft x64) where we insert an (inlined) thunk; in this case, we're + // getting the attributes of the constructor that was generated by Clang, + // which doesn't insert these attributes. + // + // - The `this` parameter should _not_ carry an `sret` attribute because the + // constructor doesn't return the constructed object as a return value. // // - The `this` parameter should _not_ carry a `nocapture` attribute (unlike // Swift constructors that return their result indirectly) because the C++ @@ -24,7 +25,7 @@ public func createHasVirtualBase() -> HasVirtualBase { // // ITANIUM_X64: define swiftcc void @"$ss20createHasVirtualBaseSo0bcD0VyF"(%TSo14HasVirtualBaseV* noalias nocapture sret %0) // ITANIUM_X64-NOT: define - // ITANIUM_X64: call void @_ZN14HasVirtualBaseC1E7ArgType(%struct.HasVirtualBase* noalias sret %{{[0-9]+}}, i32 %{{[0-9]+}}) + // ITANIUM_X64: call void @_ZN14HasVirtualBaseC1E7ArgType(%struct.HasVirtualBase* noalias %{{[0-9]+}}, i32 %{{[0-9]+}}) // // ITANIUM_ARM: define protected swiftcc void @"$ss20createHasVirtualBaseSo0bcD0VyF"(%TSo14HasVirtualBaseV* noalias nocapture sret %0) // To verify that the thunk is inlined, make sure there's no intervening From 8416ccfa06d05da67af70a8ed57a2f120ea251b2 Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Mon, 25 May 2020 16:11:03 +0200 Subject: [PATCH 333/745] Rename constructors-ir.swift to constructors-irgen.swift. --- .../Cxx/class/{constructors-ir.swift => constructors-irgen.swift} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/Interop/Cxx/class/{constructors-ir.swift => constructors-irgen.swift} (100%) diff --git a/test/Interop/Cxx/class/constructors-ir.swift b/test/Interop/Cxx/class/constructors-irgen.swift similarity index 100% rename from test/Interop/Cxx/class/constructors-ir.swift rename to test/Interop/Cxx/class/constructors-irgen.swift From 8f6042aa0870a527505262c7dbe89ec7ffe20e12 Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Mon, 25 May 2020 16:23:03 +0200 Subject: [PATCH 334/745] Add additional IR tests for a class without a virtual base class. --- test/Interop/Cxx/class/constructors-irgen.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/Interop/Cxx/class/constructors-irgen.swift b/test/Interop/Cxx/class/constructors-irgen.swift index 1886a95601861..cb98844dbf3a2 100644 --- a/test/Interop/Cxx/class/constructors-irgen.swift +++ b/test/Interop/Cxx/class/constructors-irgen.swift @@ -41,3 +41,20 @@ public func createHasVirtualBase() -> HasVirtualBase { // MICROSOFT_X64: call %struct.HasVirtualBase* @"??0HasVirtualBase@@QEAA@UArgType@@@Z"(%struct.HasVirtualBase* %{{[0-9]+}}, i32 %{{[0-9]+}}, i32 1) return HasVirtualBase(ArgType()) } + +public func createImplicitDefaultConstructor() -> ImplicitDefaultConstructor { + // ITANIUM_X64: define swiftcc i32 @"$ss32createImplicitDefaultConstructorSo0bcD0VyF"() + // ITANIUM_X64-NOT: define + // ITANIUM_X64: call void @_ZN26ImplicitDefaultConstructorC1Ev(%struct.ImplicitDefaultConstructor* noalias %{{[0-9]+}}) + // + // ITANIUM_ARM: define protected swiftcc i32 @"$ss32createImplicitDefaultConstructorSo0bcD0VyF"() + // ITANIUM_ARM-NOT: define + // Note `this` return type. + // ITANIUM_ARM: call %struct.ImplicitDefaultConstructor* @_ZN26ImplicitDefaultConstructorC2Ev(%struct.ImplicitDefaultConstructor* %{{[0-9]+}}) + // + // MICROSOFT_X64: define dllexport swiftcc i32 @"$ss32createImplicitDefaultConstructorSo0bcD0VyF"() + // MICROSOFT_X64-NOT: define + // Note `this` return type but no implicit "most derived" argument. + // MICROSOFT_X64: call %struct.ImplicitDefaultConstructor* @"??0ImplicitDefaultConstructor@@QEAA@XZ"(%struct.ImplicitDefaultConstructor* %{{[0-9]+}}) + return ImplicitDefaultConstructor() +} From c9405fb7fc338f59db4386488245a3ce82227e00 Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Tue, 26 May 2020 06:48:17 +0200 Subject: [PATCH 335/745] Add Windows-name-mangled version of symbol to SIL test. --- test/Interop/Cxx/class/constructors-sil.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Interop/Cxx/class/constructors-sil.swift b/test/Interop/Cxx/class/constructors-sil.swift index 9eef642b22d0a..95b748ebb89eb 100644 --- a/test/Interop/Cxx/class/constructors-sil.swift +++ b/test/Interop/Cxx/class/constructors-sil.swift @@ -8,6 +8,6 @@ import Constructors // CHECK: [[TYPE:%[0-9]+]] = metatype $@thin ConstructorWithParam.Type // CHECK: [[LITERAL:%[0-9]+]] = integer_literal $Builtin.Int32, 42 // CHECK: [[INT:%[0-9]+]] = struct $Int32 ([[LITERAL]] : $Builtin.Int32) -// CHECK: [[FUNC:%[0-9]+]] = function_ref @_ZN20ConstructorWithParamC1Ei : $@convention(c) (Int32, @thin ConstructorWithParam.Type) -> @out ConstructorWithParam +// CHECK: [[FUNC:%[0-9]+]] = function_ref @{{_ZN20ConstructorWithParamC1Ei|\?\?0ConstructorWithParam@@QEAA@H@Z}} : $@convention(c) (Int32, @thin ConstructorWithParam.Type) -> @out ConstructorWithParam // CHECK: %{{[0-9]+}} = apply [[FUNC]]([[VAR]], [[INT]], [[TYPE]]) : $@convention(c) (Int32, @thin ConstructorWithParam.Type) -> @out ConstructorWithParam let _ = ConstructorWithParam(42) From cb4ddda6e2292c584cb83905f3f9df17a4e017f1 Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Tue, 2 Jun 2020 09:32:12 +0200 Subject: [PATCH 336/745] Avoid crashing if lldb imports C++ structs without enabling C++ interop. --- lib/ClangImporter/ImportDecl.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 46a135d9573d9..df44ddc2d4ba2 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3505,6 +3505,13 @@ namespace { } Decl *VisitCXXRecordDecl(const clang::CXXRecordDecl *decl) { + // lldb can call this without enabling C++ interop. To avoid crashing in + // Clang's Sema, fall back to importing this as a plain RecordDecl. + // FIXME: Fix lldb to enable C++ interop when appropriate, then remove + // this fallback. + if (!Impl.SwiftContext.LangOpts.EnableCXXInterop) + return VisitRecordDecl(decl); + auto &clangSema = Impl.getClangSema(); // Make Clang define the implicit default constructor if the class needs // it. Make sure we only do this if the class has been fully defined and From 33e8c717f2682b913eb04e1c7746c84315be90ef Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Mon, 8 Jun 2020 13:56:37 +0200 Subject: [PATCH 337/745] Update comment in VisitCXXRecordDecl(). --- lib/ClangImporter/ImportDecl.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index df44ddc2d4ba2..7d2ebfe33929f 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3505,10 +3505,11 @@ namespace { } Decl *VisitCXXRecordDecl(const clang::CXXRecordDecl *decl) { - // lldb can call this without enabling C++ interop. To avoid crashing in - // Clang's Sema, fall back to importing this as a plain RecordDecl. - // FIXME: Fix lldb to enable C++ interop when appropriate, then remove - // this fallback. + // This can be called from lldb without C++ interop being enabled: There + // may be C++ declarations in imported modules, but the interface for + // those modules may be a pure C or Objective-C interface. + // To avoid crashing in Clang's Sema, fall back to importing this as a + // plain RecordDecl. if (!Impl.SwiftContext.LangOpts.EnableCXXInterop) return VisitRecordDecl(decl); From 7e8ea120701b33958a1adca9e885a99f5d583607 Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Wed, 10 Jun 2020 09:53:55 +0200 Subject: [PATCH 338/745] Respond to review comments. --- test/Interop/Cxx/class/constructors-sil.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Interop/Cxx/class/constructors-sil.swift b/test/Interop/Cxx/class/constructors-sil.swift index 95b748ebb89eb..def974a1e9a3d 100644 --- a/test/Interop/Cxx/class/constructors-sil.swift +++ b/test/Interop/Cxx/class/constructors-sil.swift @@ -2,8 +2,8 @@ import Constructors -// The most important thing to test here is that the constructor result is returned -// with an @out attribute. +// The most important thing to test here is that the constructor result is +// returned with an @out attribute. // CHECK: [[VAR:%[0-9]+]] = alloc_stack $ConstructorWithParam // CHECK: [[TYPE:%[0-9]+]] = metatype $@thin ConstructorWithParam.Type // CHECK: [[LITERAL:%[0-9]+]] = integer_literal $Builtin.Int32, 42 From 1ce3753d08522cefe255f4acf498dba5085aa60a Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Wed, 10 Jun 2020 10:01:07 +0200 Subject: [PATCH 339/745] Another response to review comments. --- lib/IRGen/GenCall.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index a2faf3b227c7b..0166efb11799b 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -1318,6 +1318,8 @@ void SignatureExpansion::expandExternalSignatureTypes() { // Convert each parameter to a Clang type. for (auto param : params) { auto clangTy = IGM.getClangType(param, FnType); + // If a parameter type is lowered to void, this means it should be ignored. + // For example, this happens for thin metatypes. if (clangTy->isVoidType()) { continue; } From faca489c6f524096019fd124cba847949972e0db Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Wed, 10 Jun 2020 14:23:33 +0200 Subject: [PATCH 340/745] Add a test that Objective-C types passed to a C++ constructor are bridged correctly. --- test/Interop/Cxx/class/Inputs/constructors-objc.h | 5 +++++ test/Interop/Cxx/class/Inputs/module.modulemap | 8 ++++++++ .../Cxx/class/constructors-objc-module-interface.swift | 10 ++++++++++ 3 files changed, 23 insertions(+) create mode 100644 test/Interop/Cxx/class/Inputs/constructors-objc.h create mode 100644 test/Interop/Cxx/class/constructors-objc-module-interface.swift diff --git a/test/Interop/Cxx/class/Inputs/constructors-objc.h b/test/Interop/Cxx/class/Inputs/constructors-objc.h new file mode 100644 index 0000000000000..46d7667a0095d --- /dev/null +++ b/test/Interop/Cxx/class/Inputs/constructors-objc.h @@ -0,0 +1,5 @@ +#import + +struct ConstructorWithNSArrayParam { + ConstructorWithNSArrayParam(NSArray *array) {} +}; diff --git a/test/Interop/Cxx/class/Inputs/module.modulemap b/test/Interop/Cxx/class/Inputs/module.modulemap index 0370a978cb949..53ee329a23d7a 100644 --- a/test/Interop/Cxx/class/Inputs/module.modulemap +++ b/test/Interop/Cxx/class/Inputs/module.modulemap @@ -10,6 +10,14 @@ module Constructors { header "constructors.h" } +module ConstructorsObjC { + header "constructors-objc.h" +} + +module LoadableTypes { + header "loadable-types.h" +} + module MemberwiseInitializer { header "memberwise-initializer.h" } diff --git a/test/Interop/Cxx/class/constructors-objc-module-interface.swift b/test/Interop/Cxx/class/constructors-objc-module-interface.swift new file mode 100644 index 0000000000000..b636bfae24c64 --- /dev/null +++ b/test/Interop/Cxx/class/constructors-objc-module-interface.swift @@ -0,0 +1,10 @@ +// Test that Objective-C types passed to a C++ constructor are bridged +// correctly. + +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -module-to-print=ConstructorsObjC -I %S/Inputs/ -source-filename=x -enable-cxx-interop | %FileCheck %s + +// REQUIRES: objc_interop + +// CHECK: struct ConstructorWithNSArrayParam { +// CHECK-NEXT: init(_ array: [Any]!) +// CHECK-NEXT: } From 83b51b95b534108494de1032bb4e098274fe606b Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Wed, 10 Jun 2020 15:36:11 +0200 Subject: [PATCH 341/745] Add SILGen and IRGen tests for passing Objective-C types to C++ constructors. --- .../Cxx/class/constructors-objc-irgen.swift | 16 ++++++++++++++++ .../Cxx/class/constructors-objc-silgen.swift | 12 ++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 test/Interop/Cxx/class/constructors-objc-irgen.swift create mode 100644 test/Interop/Cxx/class/constructors-objc-silgen.swift diff --git a/test/Interop/Cxx/class/constructors-objc-irgen.swift b/test/Interop/Cxx/class/constructors-objc-irgen.swift new file mode 100644 index 0000000000000..a5e8d4d162f87 --- /dev/null +++ b/test/Interop/Cxx/class/constructors-objc-irgen.swift @@ -0,0 +1,16 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs -enable-cxx-interop -emit-ir %s | %FileCheck %s + +// REQUIRES: CPU=x86_64 +// REQUIRES: objc_interop + +import ConstructorsObjC + +public func createConstructorWithNSArrayParam() -> ConstructorWithNSArrayParam { + // CHECK: define swiftcc void @"$s4main33createConstructorWithNSArrayParamSo0cdeF0VyF"() + // CHECK-NOT: define + // CHECK: [[VAR:%[0-9]+]] = alloca %TSo27ConstructorWithNSArrayParamV, align 1 + // CHECK: %{{[0-9]+}} = call swiftcc %TSo7NSArrayC* @"$sSa10FoundationE19_bridgeToObjectiveCSo7NSArrayCyF"(%swift.bridge* %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sypN", i32 0, i32 1)) + // CHECK: [[CAST_VAR:%[0-9]+]] = bitcast %TSo27ConstructorWithNSArrayParamV* [[VAR]] to %struct.ConstructorWithNSArrayParam* + // CHECK: call void @_ZN27ConstructorWithNSArrayParamC1EP7NSArray(%struct.ConstructorWithNSArrayParam* noalias [[CAST_VAR]], [[VAR]]* %{{[0-9]+}}) + return ConstructorWithNSArrayParam([]) +} diff --git a/test/Interop/Cxx/class/constructors-objc-silgen.swift b/test/Interop/Cxx/class/constructors-objc-silgen.swift new file mode 100644 index 0000000000000..c7199ee889811 --- /dev/null +++ b/test/Interop/Cxx/class/constructors-objc-silgen.swift @@ -0,0 +1,12 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs -enable-cxx-interop -emit-sil %s | %FileCheck %s + +// REQUIRES: objc_interop + +import ConstructorsObjC + +// CHECK: [[VAR:%[0-9]+]] = alloc_stack $ConstructorWithNSArrayParam +// CHECK: [[TYPE:%[0-9]+]] = metatype $@thin ConstructorWithNSArrayParam.Type +// CHECK: [[OPT_ARRAY:%[0-9]+]] = enum $Optional, #Optional.some!enumelt, %{{[0-9]+}} : $NSArray +// CHECK: [[FUNC:%[0-9]+]] = function_ref @_ZN27ConstructorWithNSArrayParamC1EP7NSArray : $@convention(c) (Optional, @thin ConstructorWithNSArrayParam.Type) -> @out ConstructorWithNSArrayParam +// CHECK: %{{[0-9]+}} = apply [[FUNC]]([[VAR]], [[OPT_ARRAY]], [[TYPE]]) : $@convention(c) (Optional, @thin ConstructorWithNSArrayParam.Type) -> @out ConstructorWithNSArrayParam +let _ = ConstructorWithNSArrayParam([]) From cc7564ecd1902901dc93844790a9ea11b3225df5 Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Wed, 10 Jun 2020 15:36:57 +0200 Subject: [PATCH 342/745] Rename constructors-sil.swift to constructors-silgen.swift. --- .../class/{constructors-sil.swift => constructors-silgen.swift} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/Interop/Cxx/class/{constructors-sil.swift => constructors-silgen.swift} (100%) diff --git a/test/Interop/Cxx/class/constructors-sil.swift b/test/Interop/Cxx/class/constructors-silgen.swift similarity index 100% rename from test/Interop/Cxx/class/constructors-sil.swift rename to test/Interop/Cxx/class/constructors-silgen.swift From 2713edb19ecd23a27ccadb524008b641f805d9fe Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 21 Sep 2020 13:22:24 -0700 Subject: [PATCH 343/745] [cxx-interop] Fix missing APIs and tests after rebase. * Update tests that relied on old behavior. * Use mangleCXXName instead of mangleCXXCtor. * Call VisitCXXRecordDecl not VisitRecordDecl from VisitClassTemplateSpecializationDecl. This allows template constructors to be imported and called correctly. --- lib/ClangImporter/ClangImporter.cpp | 4 +- lib/ClangImporter/ImportDecl.cpp | 6 +- lib/SIL/SILFunctionType.cpp | 4284 ----------------- .../Cxx/class/Inputs/type-classification.h | 5 + .../Cxx/class/constructors-irgen.swift | 34 + .../synthesized-initializers-silgen.swift | 42 +- ...ype-classification-non-trivial-irgen.swift | 38 +- ...pe-classification-non-trivial-silgen.swift | 78 +- .../type-classification-non-trivial.swift | 8 +- .../canonical-types-module-interface.swift | 25 +- ...ined-class-template-module-interface.swift | 24 +- .../using-directive-module-interface.swift | 24 +- 12 files changed, 148 insertions(+), 4424 deletions(-) delete mode 100644 lib/SIL/SILFunctionType.cpp diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 2aad8328ac8cd..370a3cdd1f91d 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3590,7 +3590,9 @@ void ClangImporter::getMangledName(raw_ostream &os, Impl.Mangler.reset(Impl.getClangASTContext().createMangleContext()); if (auto ctor = dyn_cast(clangDecl)) { - Impl.Mangler->mangleCXXCtor(ctor, clang::Ctor_Complete, os); + auto ctorGlobalDecl = clang::GlobalDecl(ctor, + clang::CXXCtorType::Ctor_Complete); + Impl.Mangler->mangleCXXName(ctorGlobalDecl, os); } else { Impl.Mangler->mangleName(clangDecl, os); } diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 7d2ebfe33929f..8b5fb67fab9da 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -1241,6 +1241,10 @@ synthesizeStructDefaultConstructorBody(AbstractFunctionDecl *afd, ASTContext &ctx = constructor->getASTContext(); auto structDecl = static_cast(context); + // We should call into C++ constructors directly. + assert(!isa(structDecl->getClangDecl()) && + "Should not synthesize a C++ object constructor."); + // Use a builtin to produce a zero initializer, and assign it to self. // Construct the left-hand reference to self. @@ -3558,7 +3562,7 @@ namespace { Impl.getClangSema().InstantiateClassTemplateSpecializationMembers( def->getLocation(), def, clang::TSK_ExplicitInstantiationDefinition); - return VisitRecordDecl(def); + return VisitCXXRecordDecl(def); } Decl *VisitClassTemplatePartialSpecializationDecl( diff --git a/lib/SIL/SILFunctionType.cpp b/lib/SIL/SILFunctionType.cpp deleted file mode 100644 index e50568874f4e0..0000000000000 --- a/lib/SIL/SILFunctionType.cpp +++ /dev/null @@ -1,4284 +0,0 @@ -//===--- SILFunctionType.cpp - Giving SIL types to AST functions ----------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -// -// This file defines the native Swift ownership transfer conventions -// and works in concert with the importer to give the correct -// conventions to imported functions and types. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "libsil" -#include "swift/AST/AnyFunctionRef.h" -#include "swift/AST/Decl.h" -#include "swift/AST/DiagnosticsSIL.h" -#include "swift/AST/ForeignInfo.h" -#include "swift/AST/GenericEnvironment.h" -#include "swift/AST/GenericSignatureBuilder.h" -#include "swift/AST/Module.h" -#include "swift/AST/ModuleLoader.h" -#include "swift/AST/ProtocolConformance.h" -#include "swift/SIL/SILModule.h" -#include "swift/SIL/SILType.h" -#include "clang/AST/Attr.h" -#include "clang/AST/DeclCXX.h" -#include "clang/AST/DeclObjC.h" -#include "clang/Analysis/DomainSpecific/CocoaConventions.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Compiler.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/SaveAndRestore.h" - -using namespace swift; -using namespace swift::Lowering; - -SILType SILFunctionType::substInterfaceType(SILModule &M, - SILType interfaceType) const { - // Apply pattern substitutions first, then invocation substitutions. - if (auto subs = getPatternSubstitutions()) - interfaceType = interfaceType.subst(M, subs); - if (auto subs = getInvocationSubstitutions()) - interfaceType = interfaceType.subst(M, subs); - return interfaceType; -} - -CanSILFunctionType SILFunctionType::getUnsubstitutedType(SILModule &M) const { - auto mutableThis = const_cast(this); - - // If we have no substitutions, there's nothing to do. - if (!hasPatternSubstitutions() && !hasInvocationSubstitutions()) - return CanSILFunctionType(mutableThis); - - // Otherwise, substitute the component types. - - SmallVector params; - SmallVector yields; - SmallVector results; - Optional errorResult; - - auto subs = getCombinedSubstitutions(); - auto substComponentType = [&](CanType type) { - if (!type->hasTypeParameter()) return type; - return SILType::getPrimitiveObjectType(type) - .subst(M, subs).getASTType(); - }; - - for (auto param : getParameters()) { - params.push_back(param.map(substComponentType)); - } - - for (auto yield : getYields()) { - yields.push_back(yield.map(substComponentType)); - } - - for (auto result : getResults()) { - results.push_back(result.map(substComponentType)); - } - - if (auto error = getOptionalErrorResult()) { - errorResult = error->map(substComponentType); - } - - auto signature = isPolymorphic() ? getInvocationGenericSignature() - : CanGenericSignature(); - return SILFunctionType::get(signature, - getExtInfo(), - getCoroutineKind(), - getCalleeConvention(), - params, yields, results, errorResult, - SubstitutionMap(), - SubstitutionMap(), - mutableThis->getASTContext(), - getWitnessMethodConformanceOrInvalid()); -} - -CanType SILParameterInfo::getArgumentType(SILModule &M, - const SILFunctionType *t) const { - // TODO: We should always require a function type. - if (t) - return t->substInterfaceType(M, - SILType::getPrimitiveAddressType(getInterfaceType())) - .getASTType(); - - return getInterfaceType(); -} - -CanType SILResultInfo::getReturnValueType(SILModule &M, - const SILFunctionType *t) const { - // TODO: We should always require a function type. - if (t) - return t->substInterfaceType(M, - SILType::getPrimitiveAddressType(getInterfaceType())) - .getASTType(); - - return getInterfaceType(); -} - -SILType SILFunctionType::getDirectFormalResultsType(SILModule &M) { - CanType type; - if (getNumDirectFormalResults() == 0) { - type = getASTContext().TheEmptyTupleType; - } else if (getNumDirectFormalResults() == 1) { - type = getSingleDirectFormalResult().getReturnValueType(M, this); - } else { - auto &cache = getMutableFormalResultsCache(); - if (cache) { - type = cache; - } else { - SmallVector elts; - for (auto result : getResults()) - if (!result.isFormalIndirect()) - elts.push_back(result.getReturnValueType(M, this)); - type = CanType(TupleType::get(elts, getASTContext())); - cache = type; - } - } - return SILType::getPrimitiveObjectType(type); -} - -SILType SILFunctionType::getAllResultsInterfaceType() { - CanType type; - if (getNumResults() == 0) { - type = getASTContext().TheEmptyTupleType; - } else if (getNumResults() == 1) { - type = getResults()[0].getInterfaceType(); - } else { - auto &cache = getMutableAllResultsCache(); - if (cache) { - type = cache; - } else { - SmallVector elts; - for (auto result : getResults()) - elts.push_back(result.getInterfaceType()); - type = CanType(TupleType::get(elts, getASTContext())); - cache = type; - } - } - return SILType::getPrimitiveObjectType(type); -} - -SILType SILFunctionType::getAllResultsSubstType(SILModule &M) { - return substInterfaceType(M, getAllResultsInterfaceType()); -} - -SILType SILFunctionType::getFormalCSemanticResult(SILModule &M) { - assert(getLanguage() == SILFunctionLanguage::C); - assert(getNumResults() <= 1); - return getDirectFormalResultsType(M); -} - -CanType SILFunctionType::getSelfInstanceType(SILModule &M) const { - auto selfTy = getSelfParameter().getArgumentType(M, this); - - // If this is a static method, get the instance type. - if (auto metaTy = dyn_cast(selfTy)) - return metaTy.getInstanceType(); - - return selfTy; -} - -ClassDecl * -SILFunctionType::getWitnessMethodClass(SILModule &M) const { - // TODO: When witnesses use substituted types, we'd get this from the - // substitution map. - auto selfTy = getSelfInstanceType(M); - auto genericSig = getSubstGenericSignature(); - if (auto paramTy = dyn_cast(selfTy)) { - assert(paramTy->getDepth() == 0 && paramTy->getIndex() == 0); - auto superclass = genericSig->getSuperclassBound(paramTy); - if (superclass) - return superclass->getClassOrBoundGenericClass(); - } - - return nullptr; -} - -IndexSubset * -SILFunctionType::getDifferentiabilityParameterIndices() { - assert(isDifferentiable() && "Must be a differentiable function"); - SmallVector result; - for (auto valueAndIndex : enumerate(getParameters())) - if (valueAndIndex.value().getDifferentiability() != - SILParameterDifferentiability::NotDifferentiable) - result.push_back(valueAndIndex.index()); - return IndexSubset::get(getASTContext(), getNumParameters(), result); -} - -CanSILFunctionType -SILFunctionType::getWithDifferentiability(DifferentiabilityKind kind, - IndexSubset *parameterIndices) { - assert(kind != DifferentiabilityKind::NonDifferentiable && - "Differentiability kind must be normal or linear"); - SmallVector newParameters; - for (auto paramAndIndex : enumerate(getParameters())) { - auto ¶m = paramAndIndex.value(); - unsigned index = paramAndIndex.index(); - newParameters.push_back(param.getWithDifferentiability( - index < parameterIndices->getCapacity() && - parameterIndices->contains(index) - ? SILParameterDifferentiability::DifferentiableOrNotApplicable - : SILParameterDifferentiability::NotDifferentiable)); - } - auto newExtInfo = getExtInfo().withDifferentiabilityKind(kind); - return get(getInvocationGenericSignature(), newExtInfo, getCoroutineKind(), - getCalleeConvention(), newParameters, getYields(), getResults(), - getOptionalErrorResult(), getPatternSubstitutions(), - getInvocationSubstitutions(), getASTContext(), - getWitnessMethodConformanceOrInvalid()); -} - -CanSILFunctionType SILFunctionType::getWithoutDifferentiability() { - if (!isDifferentiable()) - return CanSILFunctionType(this); - auto nondiffExtInfo = getExtInfo().withDifferentiabilityKind( - DifferentiabilityKind::NonDifferentiable); - SmallVector newParams; - for (auto ¶m : getParameters()) - newParams.push_back(param.getWithDifferentiability( - SILParameterDifferentiability::DifferentiableOrNotApplicable)); - return SILFunctionType::get(getInvocationGenericSignature(), nondiffExtInfo, - getCoroutineKind(), getCalleeConvention(), - newParams, getYields(), getResults(), - getOptionalErrorResult(), - getPatternSubstitutions(), - getInvocationSubstitutions(), - getASTContext()); -} - -/// Collects the differentiability parameters of the given original function -/// type in `diffParams`. -static void -getDifferentiabilityParameters(SILFunctionType *originalFnTy, - IndexSubset *parameterIndices, - SmallVectorImpl &diffParams) { - // Returns true if `index` is a differentiability parameter index. - auto isDiffParamIndex = [&](unsigned index) -> bool { - return index < parameterIndices->getCapacity() && - parameterIndices->contains(index); - }; - // Calculate differentiability parameter infos. - for (auto valueAndIndex : enumerate(originalFnTy->getParameters())) - if (isDiffParamIndex(valueAndIndex.index())) - diffParams.push_back(valueAndIndex.value()); -} - -/// Collects the semantic results of the given function type in -/// `originalResults`. The semantic results are formal results followed by -/// `inout` parameters, in type order. -// TODO(TF-983): Generalize to support multiple `inout` parameters. The current -// singular `inoutParam` and `isWrtInoutParameter` are hacky. -static void -getSemanticResults(SILFunctionType *functionType, IndexSubset *parameterIndices, - Optional &inoutParam, - bool &isWrtInoutParameter, - SmallVectorImpl &originalResults) { - inoutParam = None; - isWrtInoutParameter = false; - // Collect original formal results. - originalResults.append(functionType->getResults().begin(), - functionType->getResults().end()); - // Collect original `inout` parameters. - for (auto i : range(functionType->getNumParameters())) { - auto param = functionType->getParameters()[i]; - if (!param.isIndirectInOut()) - continue; - inoutParam = param; - isWrtInoutParameter = parameterIndices->contains(i); - originalResults.push_back( - SILResultInfo(param.getInterfaceType(), ResultConvention::Indirect)); - } -} - -/// Returns the differential type for the given original function type, -/// parameter indices, and result index. -static CanSILFunctionType -getAutoDiffDifferentialType(SILFunctionType *originalFnTy, - IndexSubset *parameterIndices, unsigned resultIndex, - LookupConformanceFn lookupConformance) { - auto &ctx = originalFnTy->getASTContext(); - SmallVector substGenericParams; - SmallVector substRequirements; - SmallVector substReplacements; - SmallVector substConformances; - - Optional inoutParam = None; - bool isWrtInoutParameter = false; - SmallVector originalResults; - getSemanticResults(originalFnTy, parameterIndices, inoutParam, - isWrtInoutParameter, originalResults); - - SmallVector diffParams; - getDifferentiabilityParameters(originalFnTy, parameterIndices, diffParams); - SmallVector differentialParams; - for (auto ¶m : diffParams) { - auto paramTan = - param.getInterfaceType()->getAutoDiffTangentSpace(lookupConformance); - assert(paramTan && "Parameter type does not have a tangent space?"); - auto paramTanType = paramTan->getCanonicalType(); - if (!paramTanType->hasArchetype() && !paramTanType->hasTypeParameter()) { - differentialParams.push_back( - {paramTan->getCanonicalType(), param.getConvention()}); - } else { - auto gpIndex = substGenericParams.size(); - auto gpType = CanGenericTypeParamType::get(0, gpIndex, ctx); - substGenericParams.push_back(gpType); - substReplacements.push_back(paramTanType); - differentialParams.push_back({gpType, param.getConvention()}); - } - } - SmallVector differentialResults; - if (!inoutParam || !isWrtInoutParameter) { - auto &result = originalResults[resultIndex]; - auto resultTan = - result.getInterfaceType()->getAutoDiffTangentSpace(lookupConformance); - assert(resultTan && "Result type does not have a tangent space?"); - auto resultTanType = resultTan->getCanonicalType(); - if (!resultTanType->hasArchetype() && !resultTanType->hasTypeParameter()) { - differentialResults.push_back( - {resultTan->getCanonicalType(), result.getConvention()}); - } else { - auto gpIndex = substGenericParams.size(); - auto gpType = CanGenericTypeParamType::get(0, gpIndex, ctx); - substGenericParams.push_back(gpType); - substReplacements.push_back(resultTanType); - differentialResults.push_back({gpType, result.getConvention()}); - } - } - SubstitutionMap substitutions; - if (!substGenericParams.empty()) { - auto genericSig = - GenericSignature::get(substGenericParams, substRequirements) - .getCanonicalSignature(); - substitutions = - SubstitutionMap::get(genericSig, llvm::makeArrayRef(substReplacements), - llvm::makeArrayRef(substConformances)); - } - return SILFunctionType::get( - GenericSignature(), SILFunctionType::ExtInfo(), SILCoroutineKind::None, - ParameterConvention::Direct_Guaranteed, differentialParams, {}, - differentialResults, None, substitutions, - /*invocationSubstitutions*/ SubstitutionMap(), ctx); -} - -/// Returns the pullback type for the given original function type, parameter -/// indices, and result index. -static CanSILFunctionType -getAutoDiffPullbackType(SILFunctionType *originalFnTy, - IndexSubset *parameterIndices, unsigned resultIndex, - LookupConformanceFn lookupConformance, - TypeConverter &TC) { - auto &ctx = originalFnTy->getASTContext(); - SmallVector substGenericParams; - SmallVector substRequirements; - SmallVector substReplacements; - SmallVector substConformances; - - Optional inoutParam = None; - bool isWrtInoutParameter = false; - SmallVector originalResults; - getSemanticResults(originalFnTy, parameterIndices, inoutParam, - isWrtInoutParameter, originalResults); - - // Given a type, returns its formal SIL parameter info. - auto getTangentParameterConventionForOriginalResult = - [&](CanType tanType, - ResultConvention origResConv) -> ParameterConvention { - tanType = - tanType->getCanonicalType(originalFnTy->getSubstGenericSignature()); - AbstractionPattern pattern(originalFnTy->getSubstGenericSignature(), - tanType); - auto &tl = - TC.getTypeLowering(pattern, tanType, TypeExpansionContext::minimal()); - ParameterConvention conv; - switch (origResConv) { - case ResultConvention::Owned: - case ResultConvention::Autoreleased: - if (tl.isAddressOnly()) { - conv = ParameterConvention::Indirect_In_Guaranteed; - } else { - conv = tl.isTrivial() ? ParameterConvention::Direct_Unowned - : ParameterConvention::Direct_Guaranteed; - } - break; - case ResultConvention::Unowned: - case ResultConvention::UnownedInnerPointer: - conv = ParameterConvention::Direct_Unowned; - break; - case ResultConvention::Indirect: - conv = ParameterConvention::Indirect_In_Guaranteed; - break; - } - return conv; - }; - - // Given a type, returns its formal SIL result info. - auto getTangentResultConventionForOriginalParameter = - [&](CanType tanType, - ParameterConvention origParamConv) -> ResultConvention { - tanType = - tanType->getCanonicalType(originalFnTy->getSubstGenericSignature()); - AbstractionPattern pattern(originalFnTy->getSubstGenericSignature(), - tanType); - auto &tl = - TC.getTypeLowering(pattern, tanType, TypeExpansionContext::minimal()); - ResultConvention conv; - switch (origParamConv) { - case ParameterConvention::Direct_Owned: - case ParameterConvention::Direct_Guaranteed: - case ParameterConvention::Direct_Unowned: - if (tl.isAddressOnly()) { - conv = ResultConvention::Indirect; - } else { - conv = tl.isTrivial() ? ResultConvention::Unowned - : ResultConvention::Owned; - } - break; - case ParameterConvention::Indirect_In: - case ParameterConvention::Indirect_Inout: - case ParameterConvention::Indirect_In_Constant: - case ParameterConvention::Indirect_In_Guaranteed: - case ParameterConvention::Indirect_InoutAliasable: - conv = ResultConvention::Indirect; - break; - } - return conv; - }; - - SmallVector pullbackParams; - if (inoutParam) { - auto paramTan = inoutParam->getInterfaceType()->getAutoDiffTangentSpace( - lookupConformance); - assert(paramTan && "Parameter type does not have a tangent space?"); - auto paramTanConvention = isWrtInoutParameter - ? inoutParam->getConvention() - : ParameterConvention::Indirect_In_Guaranteed; - auto paramTanType = paramTan->getCanonicalType(); - if (!paramTanType->hasArchetype() && !paramTanType->hasTypeParameter()) { - pullbackParams.push_back( - SILParameterInfo(paramTanType, paramTanConvention)); - } else { - auto gpIndex = substGenericParams.size(); - auto gpType = CanGenericTypeParamType::get(0, gpIndex, ctx); - substGenericParams.push_back(gpType); - substReplacements.push_back(paramTanType); - pullbackParams.push_back({gpType, paramTanConvention}); - } - } else { - auto &origRes = originalResults[resultIndex]; - auto resultTan = - origRes.getInterfaceType()->getAutoDiffTangentSpace(lookupConformance); - assert(resultTan && "Result type does not have a tangent space?"); - auto resultTanType = resultTan->getCanonicalType(); - auto paramTanConvention = getTangentParameterConventionForOriginalResult( - resultTanType, origRes.getConvention()); - if (!resultTanType->hasArchetype() && !resultTanType->hasTypeParameter()) { - auto resultTanType = resultTan->getCanonicalType(); - pullbackParams.push_back({resultTanType, paramTanConvention}); - } else { - auto gpIndex = substGenericParams.size(); - auto gpType = CanGenericTypeParamType::get(0, gpIndex, ctx); - substGenericParams.push_back(gpType); - substReplacements.push_back(resultTanType); - pullbackParams.push_back({gpType, paramTanConvention}); - } - } - SmallVector diffParams; - getDifferentiabilityParameters(originalFnTy, parameterIndices, diffParams); - SmallVector pullbackResults; - for (auto ¶m : diffParams) { - if (param.isIndirectInOut()) - continue; - auto paramTan = - param.getInterfaceType()->getAutoDiffTangentSpace(lookupConformance); - assert(paramTan && "Parameter type does not have a tangent space?"); - auto paramTanType = paramTan->getCanonicalType(); - auto resultTanConvention = getTangentResultConventionForOriginalParameter( - paramTanType, param.getConvention()); - if (!paramTanType->hasArchetype() && !paramTanType->hasTypeParameter()) { - pullbackResults.push_back({paramTanType, resultTanConvention}); - } else { - auto gpIndex = substGenericParams.size(); - auto gpType = CanGenericTypeParamType::get(0, gpIndex, ctx); - substGenericParams.push_back(gpType); - substReplacements.push_back(paramTanType); - pullbackResults.push_back({gpType, resultTanConvention}); - } - } - SubstitutionMap substitutions; - if (!substGenericParams.empty()) { - auto genericSig = - GenericSignature::get(substGenericParams, substRequirements) - .getCanonicalSignature(); - substitutions = - SubstitutionMap::get(genericSig, llvm::makeArrayRef(substReplacements), - llvm::makeArrayRef(substConformances)); - } - return SILFunctionType::get( - GenericSignature(), SILFunctionType::ExtInfo(), SILCoroutineKind::None, - ParameterConvention::Direct_Guaranteed, pullbackParams, {}, - pullbackResults, None, substitutions, - /*invocationSubstitutions*/ SubstitutionMap(), ctx); -} - -/// Constrains the `original` function type according to differentiability -/// requirements: -/// - All differentiability parameters are constrained to conform to -/// `Differentiable`. -/// - The invocation generic signature is replaced by the -/// `constrainedInvocationGenSig` argument. -static SILFunctionType *getConstrainedAutoDiffOriginalFunctionType( - SILFunctionType *original, IndexSubset *parameterIndices, - LookupConformanceFn lookupConformance, - CanGenericSignature constrainedInvocationGenSig) { - auto originalInvocationGenSig = original->getInvocationGenericSignature(); - if (!originalInvocationGenSig) { - assert(!constrainedInvocationGenSig || - constrainedInvocationGenSig->areAllParamsConcrete() && - "derivative function cannot have invocation generic signature " - "when original function doesn't"); - return original; - } - - assert(!original->getPatternSubstitutions() && - "cannot constrain substituted function type"); - if (!constrainedInvocationGenSig) - constrainedInvocationGenSig = originalInvocationGenSig; - if (!constrainedInvocationGenSig) - return original; - constrainedInvocationGenSig = - autodiff::getConstrainedDerivativeGenericSignature( - original, parameterIndices, constrainedInvocationGenSig, - lookupConformance) - .getCanonicalSignature(); - - SmallVector newParameters; - newParameters.reserve(original->getNumParameters()); - for (auto ¶m : original->getParameters()) { - newParameters.push_back( - param.getWithInterfaceType(param.getInterfaceType()->getCanonicalType( - constrainedInvocationGenSig))); - } - - SmallVector newResults; - newResults.reserve(original->getNumResults()); - for (auto &result : original->getResults()) { - newResults.push_back( - result.getWithInterfaceType(result.getInterfaceType()->getCanonicalType( - constrainedInvocationGenSig))); - } - return SILFunctionType::get( - constrainedInvocationGenSig->areAllParamsConcrete() - ? GenericSignature() - : constrainedInvocationGenSig, - original->getExtInfo(), original->getCoroutineKind(), - original->getCalleeConvention(), newParameters, original->getYields(), - newResults, original->getOptionalErrorResult(), - /*patternSubstitutions*/ SubstitutionMap(), - /*invocationSubstitutions*/ SubstitutionMap(), original->getASTContext(), - original->getWitnessMethodConformanceOrInvalid()); -} - -CanSILFunctionType SILFunctionType::getAutoDiffDerivativeFunctionType( - IndexSubset *parameterIndices, unsigned resultIndex, - AutoDiffDerivativeFunctionKind kind, TypeConverter &TC, - LookupConformanceFn lookupConformance, - CanGenericSignature derivativeFnInvocationGenSig, - bool isReabstractionThunk) { - auto &ctx = getASTContext(); - - // Look up result in cache. - auto *resultIndices = IndexSubset::get( - ctx, getNumResults() + getNumIndirectMutatingParameters(), {resultIndex}); - SILAutoDiffDerivativeFunctionKey key{this, - parameterIndices, - resultIndices, - kind, - derivativeFnInvocationGenSig, - isReabstractionThunk}; - auto insertion = - ctx.SILAutoDiffDerivativeFunctions.try_emplace(key, CanSILFunctionType()); - auto &cachedResult = insertion.first->getSecond(); - if (!insertion.second) - return cachedResult; - - SILFunctionType *constrainedOriginalFnTy = - getConstrainedAutoDiffOriginalFunctionType(this, parameterIndices, - lookupConformance, - derivativeFnInvocationGenSig); - // Compute closure type. - CanSILFunctionType closureType; - switch (kind) { - case AutoDiffDerivativeFunctionKind::JVP: - closureType = - getAutoDiffDifferentialType(constrainedOriginalFnTy, parameterIndices, - resultIndex, lookupConformance); - break; - case AutoDiffDerivativeFunctionKind::VJP: - closureType = - getAutoDiffPullbackType(constrainedOriginalFnTy, parameterIndices, - resultIndex, lookupConformance, TC); - break; - } - // Compute the derivative function parameters. - SmallVector newParameters; - newParameters.reserve(constrainedOriginalFnTy->getNumParameters()); - for (auto ¶m : constrainedOriginalFnTy->getParameters()) { - newParameters.push_back(param); - } - // Reabstraction thunks have a function-typed parameter (the function to - // reabstract) as their last parameter. Reabstraction thunk JVPs/VJPs have a - // `@differentiable` function-typed last parameter instead. - if (isReabstractionThunk) { - assert(!parameterIndices->contains(getNumParameters() - 1) && - "Function-typed parameter should not be wrt"); - auto fnParam = newParameters.back(); - auto fnParamType = dyn_cast(fnParam.getInterfaceType()); - assert(fnParamType); - auto diffFnType = fnParamType->getWithDifferentiability( - DifferentiabilityKind::Normal, parameterIndices); - newParameters.back() = fnParam.getWithInterfaceType(diffFnType); - } - - // Compute the derivative function results. - SmallVector newResults; - newResults.reserve(getNumResults() + 1); - for (auto &result : constrainedOriginalFnTy->getResults()) { - newResults.push_back(result); - } - newResults.push_back({closureType, ResultConvention::Owned}); - - // Compute the derivative function ExtInfo. - // If original function is `@convention(c)`, the derivative function should - // have `@convention(thin)`. IRGen does not support `@convention(c)` functions - // with multiple results. - auto extInfo = constrainedOriginalFnTy->getExtInfo(); - if (getRepresentation() == SILFunctionTypeRepresentation::CFunctionPointer) - extInfo = extInfo.withRepresentation(SILFunctionTypeRepresentation::Thin); - - // Put everything together to get the derivative function type. Then, store in - // cache and return. - cachedResult = SILFunctionType::get( - constrainedOriginalFnTy->getSubstGenericSignature(), extInfo, - constrainedOriginalFnTy->getCoroutineKind(), - constrainedOriginalFnTy->getCalleeConvention(), newParameters, - constrainedOriginalFnTy->getYields(), newResults, - constrainedOriginalFnTy->getOptionalErrorResult(), - /*patternSubstitutions*/ SubstitutionMap(), - /*invocationSubstitutions*/ SubstitutionMap(), - constrainedOriginalFnTy->getASTContext(), - constrainedOriginalFnTy->getWitnessMethodConformanceOrInvalid()); - return cachedResult; -} - -CanSILFunctionType SILFunctionType::getAutoDiffTransposeFunctionType( - IndexSubset *parameterIndices, Lowering::TypeConverter &TC, - LookupConformanceFn lookupConformance, - CanGenericSignature transposeFnGenSig) { - // Get the "constrained" transpose function generic signature. - if (!transposeFnGenSig) - transposeFnGenSig = getSubstGenericSignature(); - transposeFnGenSig = autodiff::getConstrainedDerivativeGenericSignature( - this, parameterIndices, transposeFnGenSig, - lookupConformance, /*isLinear*/ true) - .getCanonicalSignature(); - - // Given a type, returns its formal SIL parameter info. - auto getParameterInfoForOriginalResult = - [&](const SILResultInfo &result) -> SILParameterInfo { - AbstractionPattern pattern(transposeFnGenSig, result.getInterfaceType()); - auto &tl = TC.getTypeLowering(pattern, result.getInterfaceType(), - TypeExpansionContext::minimal()); - ParameterConvention newConv; - switch (result.getConvention()) { - case ResultConvention::Owned: - case ResultConvention::Autoreleased: - newConv = tl.isTrivial() ? ParameterConvention::Direct_Unowned - : ParameterConvention::Direct_Guaranteed; - break; - case ResultConvention::Unowned: - case ResultConvention::UnownedInnerPointer: - newConv = ParameterConvention::Direct_Unowned; - break; - case ResultConvention::Indirect: - newConv = ParameterConvention::Indirect_In_Guaranteed; - break; - } - return {result.getInterfaceType(), newConv}; - }; - - // Given a type, returns its formal SIL result info. - auto getResultInfoForOriginalParameter = - [&](const SILParameterInfo ¶m) -> SILResultInfo { - AbstractionPattern pattern(transposeFnGenSig, param.getInterfaceType()); - auto &tl = TC.getTypeLowering(pattern, param.getInterfaceType(), - TypeExpansionContext::minimal()); - ResultConvention newConv; - switch (param.getConvention()) { - case ParameterConvention::Direct_Owned: - case ParameterConvention::Direct_Guaranteed: - case ParameterConvention::Direct_Unowned: - newConv = - tl.isTrivial() ? ResultConvention::Unowned : ResultConvention::Owned; - break; - case ParameterConvention::Indirect_In: - case ParameterConvention::Indirect_Inout: - case ParameterConvention::Indirect_In_Constant: - case ParameterConvention::Indirect_In_Guaranteed: - case ParameterConvention::Indirect_InoutAliasable: - newConv = ResultConvention::Indirect; - break; - } - return {param.getInterfaceType(), newConv}; - }; - - SmallVector newParameters; - SmallVector newResults; - for (auto pair : llvm::enumerate(getParameters())) { - auto index = pair.index(); - auto param = pair.value(); - if (parameterIndices->contains(index)) - newResults.push_back(getResultInfoForOriginalParameter(param)); - else - newParameters.push_back(param); - } - for (auto &res : getResults()) - newParameters.push_back(getParameterInfoForOriginalResult(res)); - return SILFunctionType::get( - getInvocationGenericSignature(), getExtInfo(), getCoroutineKind(), - getCalleeConvention(), newParameters, getYields(), newResults, - getOptionalErrorResult(), getPatternSubstitutions(), - /*invocationSubstitutions*/ {}, getASTContext()); -} - -static CanType getKnownType(Optional &cacheSlot, ASTContext &C, - StringRef moduleName, StringRef typeName) { - if (!cacheSlot) { - cacheSlot = ([&] { - ModuleDecl *mod = C.getLoadedModule(C.getIdentifier(moduleName)); - if (!mod) - return CanType(); - - // Do a general qualified lookup instead of a direct lookupValue because - // some of the types we want are reexported through overlays and - // lookupValue would only give us types actually declared in the overlays - // themselves. - SmallVector decls; - mod->lookupQualified(mod, DeclNameRef(C.getIdentifier(typeName)), - NL_QualifiedDefault | NL_KnownNonCascadingDependency, - decls); - if (decls.size() != 1) - return CanType(); - - const auto *typeDecl = dyn_cast(decls.front()); - if (!typeDecl) - return CanType(); - - return typeDecl->getDeclaredInterfaceType()->getCanonicalType(); - })(); - } - CanType t = *cacheSlot; - - // It is possible that we won't find a bridging type (e.g. String) when we're - // parsing the stdlib itself. - if (t) { - LLVM_DEBUG(llvm::dbgs() << "Bridging type " << moduleName << '.' << typeName - << " mapped to "; - if (t) - t->print(llvm::dbgs()); - else - llvm::dbgs() << ""; - llvm::dbgs() << '\n'); - } - return t; -} - -#define BRIDGING_KNOWN_TYPE(BridgedModule,BridgedType) \ - CanType TypeConverter::get##BridgedType##Type() { \ - return getKnownType(BridgedType##Ty, Context, \ - #BridgedModule, #BridgedType); \ - } -#include "swift/SIL/BridgedTypes.def" - -/// Adjust a function type to have a slightly different type. -CanAnyFunctionType -Lowering::adjustFunctionType(CanAnyFunctionType t, - AnyFunctionType::ExtInfo extInfo) { - if (t->getExtInfo() == extInfo) - return t; - return CanAnyFunctionType(t->withExtInfo(extInfo)); -} - -/// Adjust a function type to have a slightly different type. -CanSILFunctionType -Lowering::adjustFunctionType(CanSILFunctionType type, - SILFunctionType::ExtInfo extInfo, - ParameterConvention callee, - ProtocolConformanceRef witnessMethodConformance) { - if (type->getExtInfo() == extInfo && type->getCalleeConvention() == callee && - type->getWitnessMethodConformanceOrInvalid() == witnessMethodConformance) - return type; - - return SILFunctionType::get(type->getInvocationGenericSignature(), - extInfo, type->getCoroutineKind(), callee, - type->getParameters(), type->getYields(), - type->getResults(), - type->getOptionalErrorResult(), - type->getPatternSubstitutions(), - type->getInvocationSubstitutions(), - type->getASTContext(), - witnessMethodConformance); -} - -CanSILFunctionType -SILFunctionType::getWithRepresentation(Representation repr) { - return getWithExtInfo(getExtInfo().withRepresentation(repr)); -} - -CanSILFunctionType SILFunctionType::getWithExtInfo(ExtInfo newExt) { - auto oldExt = getExtInfo(); - if (newExt == oldExt) - return CanSILFunctionType(this); - - auto calleeConvention = - (newExt.hasContext() - ? (oldExt.hasContext() - ? getCalleeConvention() - : Lowering::DefaultThickCalleeConvention) - : ParameterConvention::Direct_Unowned); - - return get(getInvocationGenericSignature(), newExt, getCoroutineKind(), - calleeConvention, getParameters(), getYields(), getResults(), - getOptionalErrorResult(), getPatternSubstitutions(), - getInvocationSubstitutions(), getASTContext(), - getWitnessMethodConformanceOrInvalid()); -} - -namespace { - -enum class ConventionsKind : uint8_t { - Default = 0, - DefaultBlock = 1, - ObjCMethod = 2, - CFunctionType = 3, - CFunction = 4, - ObjCSelectorFamily = 5, - Deallocator = 6, - Capture = 7, - CXXMethod = 8, -}; - -class Conventions { - ConventionsKind kind; - -protected: - virtual ~Conventions() = default; - -public: - Conventions(ConventionsKind k) : kind(k) {} - - ConventionsKind getKind() const { return kind; } - - virtual ParameterConvention - getIndirectParameter(unsigned index, - const AbstractionPattern &type, - const TypeLowering &substTL) const = 0; - virtual ParameterConvention - getDirectParameter(unsigned index, - const AbstractionPattern &type, - const TypeLowering &substTL) const = 0; - virtual ParameterConvention getCallee() const = 0; - virtual ResultConvention getResult(const TypeLowering &resultTL) const = 0; - virtual ParameterConvention - getIndirectSelfParameter(const AbstractionPattern &type) const = 0; - virtual ParameterConvention - getDirectSelfParameter(const AbstractionPattern &type) const = 0; - - // Helpers that branch based on a value ownership. - ParameterConvention getIndirect(ValueOwnership ownership, bool forSelf, - unsigned index, - const AbstractionPattern &type, - const TypeLowering &substTL) const { - switch (ownership) { - case ValueOwnership::Default: - if (forSelf) - return getIndirectSelfParameter(type); - return getIndirectParameter(index, type, substTL); - case ValueOwnership::InOut: - return ParameterConvention::Indirect_Inout; - case ValueOwnership::Shared: - return ParameterConvention::Indirect_In_Guaranteed; - case ValueOwnership::Owned: - return ParameterConvention::Indirect_In; - } - llvm_unreachable("unhandled ownership"); - } - - ParameterConvention getDirect(ValueOwnership ownership, bool forSelf, - unsigned index, const AbstractionPattern &type, - const TypeLowering &substTL) const { - switch (ownership) { - case ValueOwnership::Default: - if (forSelf) - return getDirectSelfParameter(type); - return getDirectParameter(index, type, substTL); - case ValueOwnership::InOut: - return ParameterConvention::Indirect_Inout; - case ValueOwnership::Shared: - return ParameterConvention::Direct_Guaranteed; - case ValueOwnership::Owned: - return ParameterConvention::Direct_Owned; - } - llvm_unreachable("unhandled ownership"); - } -}; - -/// A structure for building the substituted generic signature of a lowered type. -/// -/// Where the abstraction pattern for a lowered type involves substitutable types, we extract those positions -/// out into generic arguments. This signature only needs to consider the general calling convention, -/// so it can reduce away protocol and base class constraints aside from -/// `AnyObject`. We want similar-shaped generic function types to remain -/// canonically equivalent, like `(T, U) -> ()`, `(T, T) -> ()`, -/// `(U, T) -> ()` or `(T, T.A) -> ()` when given substitutions that produce -/// the same function types, so we also introduce a new generic argument for -/// each position where we see a dependent type, and canonicalize the order in -/// which we see independent generic arguments. -class SubstFunctionTypeCollector { -public: - TypeConverter &TC; - TypeExpansionContext Expansion; - CanGenericSignature GenericSig; - bool Enabled; - - SmallVector substGenericParams; - SmallVector substRequirements; - SmallVector substReplacements; - SmallVector substConformances; - - SubstFunctionTypeCollector(TypeConverter &TC, TypeExpansionContext context, - CanGenericSignature genericSig, bool enabled) - : TC(TC), Expansion(context), GenericSig(genericSig), Enabled(enabled) { - } - SubstFunctionTypeCollector(const SubstFunctionTypeCollector &) = delete; - - // Add a substitution for a fresh type variable, with the given replacement - // type and layout constraint. - CanType addSubstitution(LayoutConstraint layout, - CanType substType, - ArchetypeType *upperBound, - ArrayRef substTypeConformances) { - auto paramIndex = substGenericParams.size(); - auto param = CanGenericTypeParamType::get(0, paramIndex, TC.Context); - - // Expand the bound type according to the expansion context. - if (Expansion.shouldLookThroughOpaqueTypeArchetypes() - && substType->hasOpaqueArchetype()) { - substType = substOpaqueTypesWithUnderlyingTypes(substType, Expansion); - } - - substGenericParams.push_back(param); - substReplacements.push_back(substType); - - LayoutConstraint upperBoundLayout; - Type upperBoundSuperclass; - ArrayRef upperBoundConformances; - - // If the parameter is in a position with upper bound constraints, such - // as a generic nominal type with type constraints on its arguments, then - // preserve the constraints from that upper bound. - if (upperBound) { - upperBoundSuperclass = upperBound->getSuperclass(); - upperBoundConformances = upperBound->getConformsTo(); - upperBoundLayout = upperBound->getLayoutConstraint(); - } - - if (upperBoundSuperclass) { - upperBoundSuperclass = upperBoundSuperclass->mapTypeOutOfContext(); - substRequirements.push_back( - Requirement(RequirementKind::Superclass, param, upperBoundSuperclass)); - } - - // Preserve the layout constraint, if any, on the archetype in the - // generic signature, generalizing away some constraints that - // shouldn't affect ABI substitutability. - if (layout) { - switch (layout->getKind()) { - // Keep these layout constraints as is. - case LayoutConstraintKind::RefCountedObject: - case LayoutConstraintKind::TrivialOfAtMostSize: - break; - - case LayoutConstraintKind::UnknownLayout: - case LayoutConstraintKind::Trivial: - // These constraints don't really constrain the ABI, so we can - // eliminate them. - layout = LayoutConstraint(); - break; - - // Replace these specific constraints with one of the more general - // constraints above. - case LayoutConstraintKind::NativeClass: - case LayoutConstraintKind::Class: - case LayoutConstraintKind::NativeRefCountedObject: - // These can all be generalized to RefCountedObject. - layout = LayoutConstraint::getLayoutConstraint( - LayoutConstraintKind::RefCountedObject); - break; - - case LayoutConstraintKind::TrivialOfExactSize: - // Generalize to TrivialOfAtMostSize. - layout = LayoutConstraint::getLayoutConstraint( - LayoutConstraintKind::TrivialOfAtMostSize, - layout->getTrivialSizeInBits(), - layout->getAlignmentInBits(), - TC.Context); - break; - } - - if (layout) { - // Pick the more specific of the upper bound layout and the layout - // we chose above. - if (upperBoundLayout) { - layout = layout.merge(upperBoundLayout); - } - - substRequirements.push_back( - Requirement(RequirementKind::Layout, param, layout)); - } - } else { - (void)0; - } - - for (unsigned i : indices(upperBoundConformances)) { - auto proto = upperBoundConformances[i]; - auto conformance = substTypeConformances[i]; - substRequirements.push_back(Requirement(RequirementKind::Conformance, - param, proto->getDeclaredType())); - substConformances.push_back(conformance); - } - - return param; - } - - /// Given the destructured original abstraction pattern and substituted type for a destructured - /// parameter or result, introduce substituted generic parameters and requirements as needed for - /// the lowered type, and return the substituted type in terms of the substituted generic signature. - CanType getSubstitutedInterfaceType(AbstractionPattern origType, - CanType substType) { - if (!Enabled) - return substType; - - // Replace every dependent type we see with a fresh type variable in - // the substituted signature, substituted by the corresponding concrete - // type. - - // The entire original context could be a generic parameter. - if (origType.isTypeParameter()) { - return addSubstitution(origType.getLayoutConstraint(), substType, - nullptr, {}); - } - - auto origContextType = origType.getType(); - - // If the substituted type is a subclass of the abstraction pattern - // type, build substitutions for any type parameters in it. This only - // comes up when lowering override types for vtable entries. - auto areDifferentClasses = [](Type a, Type b) -> bool { - if (auto dynA = a->getAs()) { - a = dynA->getSelfType(); - } - if (auto dynB = b->getAs()) { - b = dynB->getSelfType(); - } - if (auto aClass = a->getClassOrBoundGenericClass()) { - if (auto bClass = b->getClassOrBoundGenericClass()) { - return aClass != bClass; - } - } - - return false; - }; - - bool substituteBindingsInSubstType = false; - if (areDifferentClasses(substType, origContextType)) { - substituteBindingsInSubstType = true; - } - if (auto substMeta = dyn_cast(substType)) { - if (auto origMeta = dyn_cast(origContextType)) { - if (areDifferentClasses(substMeta->getInstanceType(), - origMeta->getInstanceType())) { - substituteBindingsInSubstType = true; - } - } - } - - CanGenericSignature origSig = origType.getGenericSignature(); - if (substituteBindingsInSubstType) { - origContextType = substType; - origSig = TC.getCurGenericSignature(); - assert((!substType->hasTypeParameter() || origSig) && - "lowering mismatched interface types in a context without " - "a generic signature"); - } - - if (!origContextType->hasTypeParameter() - && !origContextType->hasArchetype()) { - // If the abstraction pattern doesn't have substitutable positions, nor - // should the concrete type. - assert(!substType->hasTypeParameter() - && !substType->hasArchetype()); - return substType; - } - - // Extract structural substitutions. - if (origContextType->hasTypeParameter()) { - origContextType = origSig->getGenericEnvironment() - ->mapTypeIntoContext(origContextType) - ->getCanonicalType(origSig); - } - - auto result = origContextType - ->substituteBindingsTo(substType, - [&](ArchetypeType *archetype, - CanType binding, - ArchetypeType *upperBound, - ArrayRef bindingConformances) -> CanType { - // TODO: ArchetypeType::getLayoutConstraint sometimes misses out on - // implied layout constraints. For now AnyObject is the only one we - // care about. - return addSubstitution(archetype->requiresClass() - ? LayoutConstraint::getLayoutConstraint(LayoutConstraintKind::Class) - : LayoutConstraint(), - binding, - upperBound, - bindingConformances); - }); - - assert(result && "substType was not bindable to abstraction pattern type?"); - return result; - } -}; - -/// A visitor for breaking down formal result types into a SILResultInfo -/// and possibly some number of indirect-out SILParameterInfos, -/// matching the abstraction patterns of the original type. -class DestructureResults { - TypeConverter &TC; - const Conventions &Convs; - SmallVectorImpl &Results; - TypeExpansionContext context; - SubstFunctionTypeCollector &Subst; - -public: - DestructureResults(TypeExpansionContext context, TypeConverter &TC, - const Conventions &conventions, - SmallVectorImpl &results, - SubstFunctionTypeCollector &subst) - : TC(TC), Convs(conventions), Results(results), context(context), - Subst(subst) {} - - void destructure(AbstractionPattern origType, CanType substType) { - // Recur into tuples. - if (origType.isTuple()) { - auto substTupleType = cast(substType); - for (auto eltIndex : indices(substTupleType.getElementTypes())) { - AbstractionPattern origEltType = - origType.getTupleElementType(eltIndex); - CanType substEltType = substTupleType.getElementType(eltIndex); - destructure(origEltType, substEltType); - } - return; - } - - auto substInterfaceType = Subst.getSubstitutedInterfaceType(origType, - substType); - - auto &substResultTLForConvention = TC.getTypeLowering( - origType, substInterfaceType, TypeExpansionContext::minimal()); - auto &substResultTL = TC.getTypeLowering(origType, substInterfaceType, - context); - - - // Determine the result convention. - ResultConvention convention; - if (isFormallyReturnedIndirectly(origType, substType, - substResultTLForConvention)) { - convention = ResultConvention::Indirect; - } else { - convention = Convs.getResult(substResultTLForConvention); - - // Reduce conventions for trivial types to an unowned convention. - if (substResultTL.isTrivial()) { - switch (convention) { - case ResultConvention::Indirect: - case ResultConvention::Unowned: - case ResultConvention::UnownedInnerPointer: - // Leave these as-is. - break; - - case ResultConvention::Autoreleased: - case ResultConvention::Owned: - // These aren't distinguishable from unowned for trivial types. - convention = ResultConvention::Unowned; - break; - } - } - } - - SILResultInfo result(substResultTL.getLoweredType().getASTType(), - convention); - Results.push_back(result); - } - - /// Query whether the original type is returned indirectly for the purpose - /// of reabstraction given complete lowering information about its - /// substitution. - bool isFormallyReturnedIndirectly(AbstractionPattern origType, - CanType substType, - const TypeLowering &substTL) { - // If the substituted type is returned indirectly, so must the - // unsubstituted type. - if ((origType.isTypeParameter() - && !origType.isConcreteType() - && !origType.requiresClass()) - || substTL.isAddressOnly()) { - return true; - - // If the substitution didn't change the type, then a negative - // response to the above is determinative as well. - } else if (origType.getType() == substType && - !origType.getType()->hasTypeParameter()) { - return false; - - // Otherwise, query specifically for the original type. - } else { - return SILType::isFormallyReturnedIndirectly( - origType.getType(), TC, origType.getGenericSignature()); - } - } -}; - -static bool isClangTypeMoreIndirectThanSubstType(TypeConverter &TC, - const clang::Type *clangTy, - CanType substTy) { - // A const pointer argument might have been imported as - // UnsafePointer, COpaquePointer, or a CF foreign class. - // (An ObjC class type wouldn't be const-qualified.) - if (clangTy->isPointerType() - && clangTy->getPointeeType().isConstQualified()) { - // Peek through optionals. - if (auto substObjTy = substTy.getOptionalObjectType()) - substTy = substObjTy; - - // Void pointers aren't usefully indirectable. - if (clangTy->isVoidPointerType()) - return false; - - if (auto eltTy = substTy->getAnyPointerElementType()) - return isClangTypeMoreIndirectThanSubstType(TC, - clangTy->getPointeeType().getTypePtr(), CanType(eltTy)); - - if (substTy->getAnyNominal() == - TC.Context.getOpaquePointerDecl()) - // TODO: We could conceivably have an indirect opaque ** imported - // as COpaquePointer. That shouldn't ever happen today, though, - // since we only ever indirect the 'self' parameter of functions - // imported as methods. - return false; - - if (clangTy->getPointeeType()->getAs()) { - // CF type as foreign class - if (substTy->getClassOrBoundGenericClass() && - substTy->getClassOrBoundGenericClass()->getForeignClassKind() == - ClassDecl::ForeignKind::CFType) { - return false; - } - } - - // swift_newtypes are always passed directly - if (auto typedefTy = clangTy->getAs()) { - if (typedefTy->getDecl()->getAttr()) - return false; - } - - return true; - } - return false; -} - -static bool isFormallyPassedIndirectly(TypeConverter &TC, - AbstractionPattern origType, - CanType substType, - const TypeLowering &substTL) { - // If the C type of the argument is a const pointer, but the Swift type - // isn't, treat it as indirect. - if (origType.isClangType() - && isClangTypeMoreIndirectThanSubstType(TC, origType.getClangType(), - substType)) { - return true; - } - - // If the substituted type is passed indirectly, so must the - // unsubstituted type. - if ((origType.isTypeParameter() && !origType.isConcreteType() - && !origType.requiresClass()) - || substTL.isAddressOnly()) { - return true; - - // If the substitution didn't change the type, then a negative - // response to the above is determinative as well. - } else if (origType.getType() == substType && - !origType.getType()->hasTypeParameter()) { - return false; - - // Otherwise, query specifically for the original type. - } else { - return SILType::isFormallyPassedIndirectly( - origType.getType(), TC, origType.getGenericSignature()); - } -} - -/// A visitor for turning formal input types into SILParameterInfos, matching -/// the abstraction patterns of the original type. -/// -/// If the original abstraction pattern is fully opaque, we must pass the -/// function's parameters and results indirectly, as if the original type were -/// the most general function signature (expressed entirely in generic -/// parameters) which can be substituted to equal the given signature. -/// -/// See the comment in AbstractionPattern.h for details. -class DestructureInputs { - TypeExpansionContext expansion; - TypeConverter &TC; - const Conventions &Convs; - const ForeignInfo &Foreign; - Optional> HandleForeignSelf; - SmallVectorImpl &Inputs; - SubstFunctionTypeCollector &Subst; - unsigned NextOrigParamIndex = 0; -public: - DestructureInputs(TypeExpansionContext expansion, TypeConverter &TC, - const Conventions &conventions, const ForeignInfo &foreign, - SmallVectorImpl &inputs, - SubstFunctionTypeCollector &subst) - : expansion(expansion), TC(TC), Convs(conventions), Foreign(foreign), - Inputs(inputs), Subst(subst) {} - - void destructure(AbstractionPattern origType, - CanAnyFunctionType::CanParamArrayRef params, - AnyFunctionType::ExtInfo extInfo) { - visitTopLevelParams(origType, params, extInfo); - } - -private: - /// Query whether the original type is address-only given complete - /// lowering information about its substitution. - bool isFormallyPassedIndirectly(AbstractionPattern origType, - CanType substType, - const TypeLowering &substTL) { - return ::isFormallyPassedIndirectly(TC, origType, substType, substTL); - } - - /// This is a special entry point that allows destructure inputs to handle - /// self correctly. - void visitTopLevelParams(AbstractionPattern origType, - CanAnyFunctionType::CanParamArrayRef params, - AnyFunctionType::ExtInfo extInfo) { - unsigned numEltTypes = params.size(); - - bool hasSelf = (extInfo.hasSelfParam() || Foreign.Self.isImportAsMember()); - unsigned numNonSelfParams = (hasSelf ? numEltTypes - 1 : numEltTypes); - - auto silRepresentation = extInfo.getSILRepresentation(); - - // We have to declare this out here so that the lambda scope lasts for - // the duration of the loop below. - auto handleForeignSelf = [&] { - // This is a "self", but it's not a Swift self, we handle it differently. - auto selfParam = params[numNonSelfParams]; - visit(selfParam.getValueOwnership(), - /*forSelf=*/false, - origType.getFunctionParamType(numNonSelfParams), - selfParam.getParameterType(), silRepresentation); - }; - - // If we have a foreign-self, install handleSelf as the handler. - if (Foreign.Self.isInstance()) { - assert(hasSelf && numEltTypes > 0); - // This is safe because function_ref just stores a pointer to the - // existing lambda object. - HandleForeignSelf = handleForeignSelf; - } - - // Add any leading foreign parameters. - maybeAddForeignParameters(); - - // Process all the non-self parameters. - for (unsigned i = 0; i != numNonSelfParams; ++i) { - auto ty = params[i].getParameterType(); - auto eltPattern = origType.getFunctionParamType(i); - auto flags = params[i].getParameterFlags(); - - visit(flags.getValueOwnership(), /*forSelf=*/false, eltPattern, ty, - silRepresentation, flags.isNoDerivative()); - } - - // Process the self parameter. Note that we implicitly drop self - // if this is a static foreign-self import. - if (hasSelf && !Foreign.Self.isImportAsMember()) { - auto selfParam = params[numNonSelfParams]; - auto ty = selfParam.getParameterType(); - auto eltPattern = origType.getFunctionParamType(numNonSelfParams); - auto flags = selfParam.getParameterFlags(); - - visit(flags.getValueOwnership(), /*forSelf=*/true, - eltPattern, ty, silRepresentation); - } - - // Clear the foreign-self handler for safety. - HandleForeignSelf.reset(); - } - - void visit(ValueOwnership ownership, bool forSelf, - AbstractionPattern origType, CanType substType, - SILFunctionTypeRepresentation rep, - bool isNonDifferentiable = false) { - assert(!isa(substType)); - - // Tuples get handled specially, in some cases: - CanTupleType substTupleTy = dyn_cast(substType); - if (substTupleTy && !origType.isTypeParameter()) { - assert(origType.getNumTupleElements() == substTupleTy->getNumElements()); - switch (ownership) { - case ValueOwnership::Default: - case ValueOwnership::Owned: - case ValueOwnership::Shared: - // Expand the tuple. - for (auto i : indices(substTupleTy.getElementTypes())) { - auto &elt = substTupleTy->getElement(i); - auto ownership = elt.getParameterFlags().getValueOwnership(); - assert(ownership == ValueOwnership::Default); - assert(!elt.isVararg()); - visit(ownership, forSelf, - origType.getTupleElementType(i), - CanType(elt.getRawType()), rep); - } - return; - case ValueOwnership::InOut: - // handled below - break; - } - } - - unsigned origParamIndex = NextOrigParamIndex++; - - auto substInterfaceType = - Subst.getSubstitutedInterfaceType(origType, substType); - - auto &substTLConv = TC.getTypeLowering(origType, substInterfaceType, - TypeExpansionContext::minimal()); - auto &substTL = TC.getTypeLowering(origType, substInterfaceType, expansion); - - ParameterConvention convention; - if (ownership == ValueOwnership::InOut) { - convention = ParameterConvention::Indirect_Inout; - } else if (isFormallyPassedIndirectly(origType, substType, substTLConv)) { - convention = Convs.getIndirect(ownership, forSelf, origParamIndex, - origType, substTLConv); - assert(isIndirectFormalParameter(convention)); - } else if (substTL.isTrivial()) { - convention = ParameterConvention::Direct_Unowned; - } else { - convention = Convs.getDirect(ownership, forSelf, origParamIndex, origType, - substTLConv); - assert(!isIndirectFormalParameter(convention)); - } - - SILParameterInfo param(substTL.getLoweredType().getASTType(), convention); - if (isNonDifferentiable) - param = param.getWithDifferentiability( - SILParameterDifferentiability::NotDifferentiable); - Inputs.push_back(param); - - maybeAddForeignParameters(); - } - - /// Given that we've just reached an argument index for the - /// first time, add any foreign parameters. - void maybeAddForeignParameters() { - while (maybeAddForeignErrorParameter() || - maybeAddForeignSelfParameter()) { - // Continue to see, just in case there are more parameters to add. - } - } - - bool maybeAddForeignErrorParameter() { - if (!Foreign.Error || - NextOrigParamIndex != Foreign.Error->getErrorParameterIndex()) - return false; - - auto foreignErrorTy = TC.getLoweredRValueType( - expansion, Foreign.Error->getErrorParameterType()); - - // Assume the error parameter doesn't have interesting lowering. - Inputs.push_back(SILParameterInfo(foreignErrorTy, - ParameterConvention::Direct_Unowned)); - NextOrigParamIndex++; - return true; - } - - bool maybeAddForeignSelfParameter() { - if (!Foreign.Self.isInstance() || - NextOrigParamIndex != Foreign.Self.getSelfIndex()) - return false; - - (*HandleForeignSelf)(); - return true; - } -}; - -} // end anonymous namespace - -static bool isPseudogeneric(SILDeclRef c) { - // FIXME: should this be integrated in with the Sema check that prevents - // illegal use of type arguments in pseudo-generic method bodies? - - // The implicitly-generated native initializer thunks for imported - // initializers are never pseudo-generic, because they may need - // to use their type arguments to bridge their value arguments. - if (!c.isForeign && - (c.kind == SILDeclRef::Kind::Allocator || - c.kind == SILDeclRef::Kind::Initializer) && - c.getDecl()->hasClangNode()) - return false; - - // Otherwise, we have to look at the entity's context. - DeclContext *dc; - if (c.hasDecl()) { - dc = c.getDecl()->getDeclContext(); - } else if (auto closure = c.getAbstractClosureExpr()) { - dc = closure->getParent(); - } else { - return false; - } - dc = dc->getInnermostTypeContext(); - if (!dc) return false; - - auto classDecl = dc->getSelfClassDecl(); - return (classDecl && classDecl->usesObjCGenericsModel()); -} - -/// Update the result type given the foreign error convention that we will be -/// using. -static std::pair updateResultTypeForForeignError( - ForeignErrorConvention convention, CanGenericSignature genericSig, - AbstractionPattern origResultType, CanType substFormalResultType) { - switch (convention.getKind()) { - // These conventions replace the result type. - case ForeignErrorConvention::ZeroResult: - case ForeignErrorConvention::NonZeroResult: - assert(substFormalResultType->isVoid()); - substFormalResultType = convention.getResultType(); - origResultType = AbstractionPattern(genericSig, substFormalResultType); - return {origResultType, substFormalResultType}; - - // These conventions wrap the result type in a level of optionality. - case ForeignErrorConvention::NilResult: - assert(!substFormalResultType->getOptionalObjectType()); - substFormalResultType = - OptionalType::get(substFormalResultType)->getCanonicalType(); - origResultType = - AbstractionPattern::getOptional(origResultType); - return {origResultType, substFormalResultType}; - - // These conventions don't require changes to the formal error type. - case ForeignErrorConvention::ZeroPreservedResult: - case ForeignErrorConvention::NonNilError: - return {origResultType, substFormalResultType}; - } - llvm_unreachable("unhandled kind"); -} - -/// Lower any/all capture context parameters. -/// -/// *NOTE* Currently default arg generators can not capture anything. -/// If we ever add that ability, it will be a different capture list -/// from the function to which the argument is attached. -static void -lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function, - CanGenericSignature genericSig, - TypeExpansionContext expansion, - SmallVectorImpl &inputs) { - - // NB: The generic signature may be elided from the lowered function type - // if the function is in a fully-specialized context, but we still need to - // canonicalize references to the generic parameters that may appear in - // non-canonical types in that context. We need the original generic - // signature from the AST for that. - auto origGenericSig = function.getAnyFunctionRef()->getGenericSignature(); - auto loweredCaptures = TC.getLoweredLocalCaptures(function); - - for (auto capture : loweredCaptures.getCaptures()) { - if (capture.isDynamicSelfMetadata()) { - ParameterConvention convention = ParameterConvention::Direct_Unowned; - auto dynamicSelfInterfaceType = - loweredCaptures.getDynamicSelfType()->mapTypeOutOfContext(); - - auto selfMetatype = MetatypeType::get(dynamicSelfInterfaceType, - MetatypeRepresentation::Thick); - - auto canSelfMetatype = selfMetatype->getCanonicalType(origGenericSig); - SILParameterInfo param(canSelfMetatype, convention); - inputs.push_back(param); - - continue; - } - - if (capture.isOpaqueValue()) { - OpaqueValueExpr *opaqueValue = capture.getOpaqueValue(); - auto canType = opaqueValue->getType()->mapTypeOutOfContext() - ->getCanonicalType(origGenericSig); - auto &loweredTL = - TC.getTypeLowering(AbstractionPattern(genericSig, canType), - canType, expansion); - auto loweredTy = loweredTL.getLoweredType(); - - ParameterConvention convention; - if (loweredTL.isAddressOnly()) { - convention = ParameterConvention::Indirect_In; - } else { - convention = ParameterConvention::Direct_Owned; - } - SILParameterInfo param(loweredTy.getASTType(), convention); - inputs.push_back(param); - - continue; - } - - auto *VD = capture.getDecl(); - auto type = VD->getInterfaceType(); - auto canType = type->getCanonicalType(origGenericSig); - - auto &loweredTL = - TC.getTypeLowering(AbstractionPattern(genericSig, canType), canType, - expansion); - auto loweredTy = loweredTL.getLoweredType(); - switch (TC.getDeclCaptureKind(capture, expansion)) { - case CaptureKind::Constant: { - // Constants are captured by value. - ParameterConvention convention; - assert (!loweredTL.isAddressOnly()); - if (loweredTL.isTrivial()) { - convention = ParameterConvention::Direct_Unowned; - } else { - convention = ParameterConvention::Direct_Guaranteed; - } - SILParameterInfo param(loweredTy.getASTType(), convention); - inputs.push_back(param); - break; - } - case CaptureKind::Box: { - // The type in the box is lowered in the minimal context. - auto minimalLoweredTy = - TC.getTypeLowering(AbstractionPattern(genericSig, canType), canType, - TypeExpansionContext::minimal()) - .getLoweredType(); - // Lvalues are captured as a box that owns the captured value. - auto boxTy = TC.getInterfaceBoxTypeForCapture( - VD, minimalLoweredTy.getASTType(), - /*mutable*/ true); - auto convention = ParameterConvention::Direct_Guaranteed; - auto param = SILParameterInfo(boxTy, convention); - inputs.push_back(param); - break; - } - case CaptureKind::StorageAddress: { - // Non-escaping lvalues are captured as the address of the value. - SILType ty = loweredTy.getAddressType(); - auto param = - SILParameterInfo(ty.getASTType(), - ParameterConvention::Indirect_InoutAliasable); - inputs.push_back(param); - break; - } - case CaptureKind::Immutable: { - // 'let' constants that are address-only are captured as the address of - // the value and will be consumed by the closure. - SILType ty = loweredTy.getAddressType(); - auto param = - SILParameterInfo(ty.getASTType(), - ParameterConvention::Indirect_In_Guaranteed); - inputs.push_back(param); - break; - } - } - } -} - -static AccessorDecl *getAsCoroutineAccessor(Optional constant) { - if (!constant || !constant->hasDecl()) - return nullptr;; - - auto accessor = dyn_cast(constant->getDecl()); - if (!accessor || !accessor->isCoroutine()) - return nullptr; - - return accessor; -} - -static void destructureYieldsForReadAccessor(TypeConverter &TC, - TypeExpansionContext expansion, - AbstractionPattern origType, - CanType valueType, - SmallVectorImpl &yields, - SubstFunctionTypeCollector &subst) { - // Recursively destructure tuples. - if (origType.isTuple()) { - auto valueTupleType = cast(valueType); - for (auto i : indices(valueTupleType.getElementTypes())) { - auto origEltType = origType.getTupleElementType(i); - auto valueEltType = valueTupleType.getElementType(i); - destructureYieldsForReadAccessor(TC, expansion, origEltType, valueEltType, - yields, subst); - } - return; - } - - auto valueInterfaceType = - subst.getSubstitutedInterfaceType(origType, valueType); - - auto &tlConv = - TC.getTypeLowering(origType, valueInterfaceType, - TypeExpansionContext::minimal()); - auto &tl = - TC.getTypeLowering(origType, valueInterfaceType, expansion); - auto convention = [&] { - if (isFormallyPassedIndirectly(TC, origType, valueInterfaceType, tlConv)) - return ParameterConvention::Indirect_In_Guaranteed; - if (tlConv.isTrivial()) - return ParameterConvention::Direct_Unowned; - return ParameterConvention::Direct_Guaranteed; - }(); - - yields.push_back(SILYieldInfo(tl.getLoweredType().getASTType(), - convention)); -} - -static void destructureYieldsForCoroutine(TypeConverter &TC, - TypeExpansionContext expansion, - Optional origConstant, - Optional constant, - Optional reqtSubs, - SmallVectorImpl &yields, - SILCoroutineKind &coroutineKind, - SubstFunctionTypeCollector &subst) { - assert(coroutineKind == SILCoroutineKind::None); - assert(yields.empty()); - - auto accessor = getAsCoroutineAccessor(constant); - if (!accessor) - return; - - auto origAccessor = cast(origConstant->getDecl()); - - // Coroutine accessors are implicitly yield-once coroutines, despite - // their function type. - coroutineKind = SILCoroutineKind::YieldOnce; - - // Coroutine accessors are always native, so fetch the native - // abstraction pattern. - auto origStorage = origAccessor->getStorage(); - auto origType = TC.getAbstractionPattern(origStorage, /*nonobjc*/ true) - .getReferenceStorageReferentType(); - - auto storage = accessor->getStorage(); - auto valueType = storage->getValueInterfaceType(); - if (reqtSubs) { - valueType = valueType.subst(*reqtSubs); - } - - auto canValueType = valueType->getCanonicalType( - accessor->getGenericSignature()); - - // 'modify' yields an inout of the target type. - if (accessor->getAccessorKind() == AccessorKind::Modify) { - auto valueInterfaceType = subst.getSubstitutedInterfaceType(origType, - canValueType); - auto loweredValueTy = - TC.getLoweredType(origType, valueInterfaceType, expansion); - yields.push_back(SILYieldInfo(loweredValueTy.getASTType(), - ParameterConvention::Indirect_Inout)); - return; - } - - // 'read' yields a borrowed value of the target type, destructuring - // tuples as necessary. - assert(accessor->getAccessorKind() == AccessorKind::Read); - destructureYieldsForReadAccessor(TC, expansion, origType, canValueType, - yields, subst); -} - -/// Create the appropriate SIL function type for the given formal type -/// and conventions. -/// -/// The lowering of function types is generally sensitive to the -/// declared abstraction pattern. We want to be able to take -/// advantage of declared type information in order to, say, pass -/// arguments separately and directly; but we also want to be able to -/// call functions from generic code without completely embarrassing -/// performance. Therefore, different abstraction patterns induce -/// different argument-passing conventions, and we must introduce -/// implicit reabstracting conversions where necessary to map one -/// convention to another. -/// -/// However, we actually can't reabstract arbitrary thin function -/// values while still leaving them thin, at least without costly -/// page-mapping tricks. Therefore, the representation must remain -/// consistent across all abstraction patterns. -/// -/// We could reabstract block functions in theory, but (1) we don't -/// really need to and (2) doing so would be problematic because -/// stuffing something in an Optional currently forces it to be -/// reabstracted to the most general type, which means that we'd -/// expect the wrong abstraction conventions on bridged block function -/// types. -/// -/// Therefore, we only honor abstraction patterns on thick or -/// polymorphic functions. -/// -/// FIXME: we shouldn't just drop the original abstraction pattern -/// when we can't reabstract. Instead, we should introduce -/// dynamic-indirect argument-passing conventions and map opaque -/// archetypes to that, then respect those conventions in IRGen by -/// using runtime call construction. -/// -/// \param conventions - conventions as expressed for the original type -static CanSILFunctionType getSILFunctionType( - TypeConverter &TC, TypeExpansionContext expansionContext, AbstractionPattern origType, - CanAnyFunctionType substFnInterfaceType, AnyFunctionType::ExtInfo extInfo, - const Conventions &conventions, const ForeignInfo &foreignInfo, - Optional origConstant, Optional constant, - Optional reqtSubs, - ProtocolConformanceRef witnessMethodConformance) { - // Find the generic parameters. - CanGenericSignature genericSig = - substFnInterfaceType.getOptGenericSignature(); - - Optional contextRAII; - if (genericSig) contextRAII.emplace(TC, genericSig); - - // Per above, only fully honor opaqueness in the abstraction pattern - // for thick or polymorphic functions. We don't need to worry about - // non-opaque patterns because the type-checker forbids non-thick - // function types from having generic parameters or results. - if (origType.isTypeParameter() && - substFnInterfaceType->getExtInfo().getSILRepresentation() - != SILFunctionType::Representation::Thick && - isa(substFnInterfaceType)) { - origType = AbstractionPattern(genericSig, - substFnInterfaceType); - } - - // Map 'throws' to the appropriate error convention. - Optional errorResult; - assert((!foreignInfo.Error || substFnInterfaceType->getExtInfo().throws()) && - "foreignError was set but function type does not throw?"); - if (substFnInterfaceType->getExtInfo().throws() && !foreignInfo.Error) { - assert(!origType.isForeign() && - "using native Swift error convention for foreign type!"); - SILType exnType = SILType::getExceptionType(TC.Context); - assert(exnType.isObject()); - errorResult = SILResultInfo(exnType.getASTType(), - ResultConvention::Owned); - } - - // Lower the result type. - AbstractionPattern origResultType = origType.getFunctionResultType(); - CanType substFormalResultType = substFnInterfaceType.getResult(); - - // If we have a foreign error convention, restore the original result type. - if (auto convention = foreignInfo.Error) { - std::tie(origResultType, substFormalResultType) = - updateResultTypeForForeignError(*convention, genericSig, origResultType, - substFormalResultType); - } - - bool shouldBuildSubstFunctionType = [&]{ - if (!TC.Context.LangOpts.EnableSubstSILFunctionTypesForFunctionValues) - return false; - - // We always use substituted function types for coroutines that are - // being lowered in the context of another coroutine, which is to say, - // for class override thunks. This is required to make the yields - // match in abstraction to the base method's yields, which is necessary - // to make the extracted continuation-function signatures match. - if (constant != origConstant && getAsCoroutineAccessor(constant)) - return true; - - // We don't currently use substituted function types for generic function - // type lowering, though we should for generic methods on classes and - // protocols. - if (genericSig) - return false; - - // We only currently use substituted function types for function values, - // which will have standard thin or thick representation. (Per the previous - // comment, it would be useful to do so for generic methods on classes and - // protocols too.) - auto rep = extInfo.getSILRepresentation(); - return (rep == SILFunctionTypeRepresentation::Thick || - rep == SILFunctionTypeRepresentation::Thin); - }(); - - SubstFunctionTypeCollector subst(TC, expansionContext, genericSig, - shouldBuildSubstFunctionType); - - // Destructure the input tuple type. - SmallVector inputs; - { - DestructureInputs destructurer(expansionContext, TC, conventions, - foreignInfo, inputs, subst); - destructurer.destructure(origType, - substFnInterfaceType.getParams(), - extInfo); - } - - // Destructure the coroutine yields. - SILCoroutineKind coroutineKind = SILCoroutineKind::None; - SmallVector yields; - destructureYieldsForCoroutine(TC, expansionContext, origConstant, constant, - reqtSubs, yields, coroutineKind, subst); - - // Destructure the result tuple type. - SmallVector results; - { - DestructureResults destructurer(expansionContext, TC, conventions, - results, subst); - destructurer.destructure(origResultType, substFormalResultType); - } - - // Lower the capture context parameters, if any. - if (constant && constant->getAnyFunctionRef()) { - auto expansion = TypeExpansionContext::maximal( - expansionContext.getContext(), expansionContext.isWholeModuleContext()); - if (constant->isSerialized()) - expansion = TypeExpansionContext::minimal(); - lowerCaptureContextParameters(TC, *constant, genericSig, expansion, inputs); - } - - auto calleeConvention = ParameterConvention::Direct_Unowned; - if (extInfo.hasContext()) - calleeConvention = conventions.getCallee(); - - bool pseudogeneric = genericSig && constant - ? isPseudogeneric(*constant) - : false; - - // NOTE: SILFunctionType::ExtInfo doesn't track everything that - // AnyFunctionType::ExtInfo tracks. For example: 'throws' or 'auto-closure' - auto silExtInfo = SILFunctionType::ExtInfo() - .withRepresentation(extInfo.getSILRepresentation()) - .withIsPseudogeneric(pseudogeneric) - .withNoEscape(extInfo.isNoEscape()) - .withDifferentiabilityKind(extInfo.getDifferentiabilityKind()); - - // Build the substituted generic signature we extracted. - SubstitutionMap substitutions; - if (subst.Enabled) { - if (!subst.substGenericParams.empty()) { - auto subSig = GenericSignature::get(subst.substGenericParams, - subst.substRequirements) - .getCanonicalSignature(); - substitutions = SubstitutionMap::get(subSig, - llvm::makeArrayRef(subst.substReplacements), - llvm::makeArrayRef(subst.substConformances)); - } - } - - return SILFunctionType::get(genericSig, silExtInfo, coroutineKind, - calleeConvention, inputs, yields, - results, errorResult, - substitutions, SubstitutionMap(), - TC.Context, witnessMethodConformance); -} - -//===----------------------------------------------------------------------===// -// Deallocator SILFunctionTypes -//===----------------------------------------------------------------------===// - -namespace { - -// The convention for general deallocators. -struct DeallocatorConventions : Conventions { - DeallocatorConventions() : Conventions(ConventionsKind::Deallocator) {} - - ParameterConvention getIndirectParameter(unsigned index, - const AbstractionPattern &type, - const TypeLowering &substTL) const override { - llvm_unreachable("Deallocators do not have indirect parameters"); - } - - ParameterConvention getDirectParameter(unsigned index, - const AbstractionPattern &type, - const TypeLowering &substTL) const override { - llvm_unreachable("Deallocators do not have non-self direct parameters"); - } - - ParameterConvention getCallee() const override { - llvm_unreachable("Deallocators do not have callees"); - } - - ResultConvention getResult(const TypeLowering &tl) const override { - // TODO: Put an unreachable here? - return ResultConvention::Owned; - } - - ParameterConvention - getDirectSelfParameter(const AbstractionPattern &type) const override { - // TODO: Investigate whether or not it is - return ParameterConvention::Direct_Owned; - } - - ParameterConvention - getIndirectSelfParameter(const AbstractionPattern &type) const override { - llvm_unreachable("Deallocators do not have indirect self parameters"); - } - - static bool classof(const Conventions *C) { - return C->getKind() == ConventionsKind::Deallocator; - } -}; - -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Default Convention FunctionTypes -//===----------------------------------------------------------------------===// - -namespace { - -enum class NormalParameterConvention { Owned, Guaranteed }; - -/// The default Swift conventions. -class DefaultConventions : public Conventions { - NormalParameterConvention normalParameterConvention; - -public: - DefaultConventions(NormalParameterConvention normalParameterConvention) - : Conventions(ConventionsKind::Default), - normalParameterConvention(normalParameterConvention) {} - - bool isNormalParameterConventionGuaranteed() const { - return normalParameterConvention == NormalParameterConvention::Guaranteed; - } - - ParameterConvention getIndirectParameter(unsigned index, - const AbstractionPattern &type, - const TypeLowering &substTL) const override { - if (isNormalParameterConventionGuaranteed()) { - return ParameterConvention::Indirect_In_Guaranteed; - } - return ParameterConvention::Indirect_In; - } - - ParameterConvention getDirectParameter(unsigned index, - const AbstractionPattern &type, - const TypeLowering &substTL) const override { - if (isNormalParameterConventionGuaranteed()) - return ParameterConvention::Direct_Guaranteed; - return ParameterConvention::Direct_Owned; - } - - ParameterConvention getCallee() const override { - return DefaultThickCalleeConvention; - } - - ResultConvention getResult(const TypeLowering &tl) const override { - return ResultConvention::Owned; - } - - ParameterConvention - getDirectSelfParameter(const AbstractionPattern &type) const override { - return ParameterConvention::Direct_Guaranteed; - } - - ParameterConvention - getIndirectSelfParameter(const AbstractionPattern &type) const override { - return ParameterConvention::Indirect_In_Guaranteed; - } - - static bool classof(const Conventions *C) { - return C->getKind() == ConventionsKind::Default; - } -}; - -/// The default conventions for Swift initializing constructors. -/// -/// Initializing constructors take all parameters (including) self at +1. This -/// is because: -/// -/// 1. We are likely to be initializing fields of self implying that the -/// parameters are likely to be forwarded into memory without further -/// copies. -/// 2. Initializers must take 'self' at +1, since they will return it back -/// at +1, and may chain onto Objective-C initializers that replace the -/// instance. -struct DefaultInitializerConventions : DefaultConventions { - DefaultInitializerConventions() - : DefaultConventions(NormalParameterConvention::Owned) {} - - /// Initializers must take 'self' at +1, since they will return it back at +1, - /// and may chain onto Objective-C initializers that replace the instance. - ParameterConvention - getDirectSelfParameter(const AbstractionPattern &type) const override { - return ParameterConvention::Direct_Owned; - } - - ParameterConvention - getIndirectSelfParameter(const AbstractionPattern &type) const override { - return ParameterConvention::Indirect_In; - } -}; - -/// The convention used for allocating inits. Allocating inits take their normal -/// parameters at +1 and do not have a self parameter. -struct DefaultAllocatorConventions : DefaultConventions { - DefaultAllocatorConventions() - : DefaultConventions(NormalParameterConvention::Owned) {} - - ParameterConvention - getDirectSelfParameter(const AbstractionPattern &type) const override { - llvm_unreachable("Allocating inits do not have self parameters"); - } - - ParameterConvention - getIndirectSelfParameter(const AbstractionPattern &type) const override { - llvm_unreachable("Allocating inits do not have self parameters"); - } -}; - -/// The default conventions for Swift setter acccessors. -/// -/// These take self at +0, but all other parameters at +1. This is because we -/// assume that setter parameters are likely to be values to be forwarded into -/// memory. Thus by passing in the +1 value, we avoid a potential copy in that -/// case. -struct DefaultSetterConventions : DefaultConventions { - DefaultSetterConventions() - : DefaultConventions(NormalParameterConvention::Owned) {} -}; - -/// The default conventions for ObjC blocks. -struct DefaultBlockConventions : Conventions { - DefaultBlockConventions() : Conventions(ConventionsKind::DefaultBlock) {} - - ParameterConvention getIndirectParameter(unsigned index, - const AbstractionPattern &type, - const TypeLowering &substTL) const override { - llvm_unreachable("indirect block parameters unsupported"); - } - - ParameterConvention getDirectParameter(unsigned index, - const AbstractionPattern &type, - const TypeLowering &substTL) const override { - return ParameterConvention::Direct_Unowned; - } - - ParameterConvention getCallee() const override { - return ParameterConvention::Direct_Unowned; - } - - ResultConvention getResult(const TypeLowering &substTL) const override { - return ResultConvention::Autoreleased; - } - - ParameterConvention - getDirectSelfParameter(const AbstractionPattern &type) const override { - llvm_unreachable("objc blocks do not have a self parameter"); - } - - ParameterConvention - getIndirectSelfParameter(const AbstractionPattern &type) const override { - llvm_unreachable("objc blocks do not have a self parameter"); - } - - static bool classof(const Conventions *C) { - return C->getKind() == ConventionsKind::DefaultBlock; - } -}; - -} // end anonymous namespace - -static CanSILFunctionType -getSILFunctionTypeForAbstractCFunction(TypeConverter &TC, - AbstractionPattern origType, - CanAnyFunctionType substType, - AnyFunctionType::ExtInfo extInfo, - Optional constant); - -static CanSILFunctionType getNativeSILFunctionType( - TypeConverter &TC, TypeExpansionContext context, AbstractionPattern origType, - CanAnyFunctionType substInterfaceType, AnyFunctionType::ExtInfo extInfo, - Optional origConstant, Optional constant, - Optional reqtSubs, - ProtocolConformanceRef witnessMethodConformance) { - assert(bool(origConstant) == bool(constant)); - switch (extInfo.getSILRepresentation()) { - case SILFunctionType::Representation::Block: - case SILFunctionType::Representation::CFunctionPointer: - return getSILFunctionTypeForAbstractCFunction(TC, origType, - substInterfaceType, - extInfo, constant); - - case SILFunctionType::Representation::Thin: - case SILFunctionType::Representation::ObjCMethod: - case SILFunctionType::Representation::Thick: - case SILFunctionType::Representation::Method: - case SILFunctionType::Representation::Closure: - case SILFunctionType::Representation::WitnessMethod: { - switch (constant ? constant->kind : SILDeclRef::Kind::Func) { - case SILDeclRef::Kind::Initializer: - case SILDeclRef::Kind::EnumElement: - return getSILFunctionType(TC, context, origType, substInterfaceType, - extInfo, DefaultInitializerConventions(), - ForeignInfo(), origConstant, constant, reqtSubs, - witnessMethodConformance); - case SILDeclRef::Kind::Allocator: - return getSILFunctionType(TC, context, origType, substInterfaceType, - extInfo, DefaultAllocatorConventions(), - ForeignInfo(), origConstant, constant, reqtSubs, - witnessMethodConformance); - case SILDeclRef::Kind::Func: - // If we have a setter, use the special setter convention. This ensures - // that we take normal parameters at +1. - if (constant && constant->isSetter()) { - return getSILFunctionType(TC, context, origType, substInterfaceType, - extInfo, DefaultSetterConventions(), - ForeignInfo(), origConstant, constant, - reqtSubs, witnessMethodConformance); - } - LLVM_FALLTHROUGH; - case SILDeclRef::Kind::Destroyer: - case SILDeclRef::Kind::GlobalAccessor: - case SILDeclRef::Kind::DefaultArgGenerator: - case SILDeclRef::Kind::StoredPropertyInitializer: - case SILDeclRef::Kind::PropertyWrapperBackingInitializer: - case SILDeclRef::Kind::IVarInitializer: - case SILDeclRef::Kind::IVarDestroyer: { - auto conv = DefaultConventions(NormalParameterConvention::Guaranteed); - return getSILFunctionType(TC, context, origType, substInterfaceType, - extInfo, conv, ForeignInfo(), origConstant, - constant, reqtSubs, witnessMethodConformance); - } - case SILDeclRef::Kind::Deallocator: - return getSILFunctionType(TC, context, origType, substInterfaceType, - extInfo, DeallocatorConventions(), - ForeignInfo(), origConstant, constant, reqtSubs, - witnessMethodConformance); - } - } - } - - llvm_unreachable("Unhandled SILDeclRefKind in switch."); -} - -CanSILFunctionType swift::getNativeSILFunctionType( - TypeConverter &TC, TypeExpansionContext context, - AbstractionPattern origType, CanAnyFunctionType substType, - Optional origConstant, Optional substConstant, - Optional reqtSubs, - ProtocolConformanceRef witnessMethodConformance) { - AnyFunctionType::ExtInfo extInfo; - - // Preserve type information from the original type if possible. - if (auto origFnType = origType.getAs()) { - extInfo = origFnType->getExtInfo(); - - // Otherwise, preserve function type attributes from the substituted type. - } else { - extInfo = substType->getExtInfo(); - } - - return ::getNativeSILFunctionType(TC, context, origType, substType, extInfo, - origConstant, substConstant, reqtSubs, - witnessMethodConformance); -} - -//===----------------------------------------------------------------------===// -// Foreign SILFunctionTypes -//===----------------------------------------------------------------------===// - -static bool isCFTypedef(const TypeLowering &tl, clang::QualType type) { - // If we imported a C pointer type as a non-trivial type, it was - // a foreign class type. - return !tl.isTrivial() && type->isPointerType(); -} - -/// Given nothing but a formal C parameter type that's passed -/// indirectly, deduce the convention for it. -/// -/// Generally, whether the parameter is +1 is handled before this. -static ParameterConvention getIndirectCParameterConvention(clang::QualType type) { - // Non-trivial C++ types would be Indirect_Inout (at least in Itanium). - // A trivial const * parameter in C should be considered @in. - return ParameterConvention::Indirect_In; -} - -/// Given a C parameter declaration whose type is passed indirectly, -/// deduce the convention for it. -/// -/// Generally, whether the parameter is +1 is handled before this. -static ParameterConvention -getIndirectCParameterConvention(const clang::ParmVarDecl *param) { - return getIndirectCParameterConvention(param->getType()); -} - -/// Given nothing but a formal C parameter type that's passed -/// directly, deduce the convention for it. -/// -/// Generally, whether the parameter is +1 is handled before this. -static ParameterConvention getDirectCParameterConvention(clang::QualType type) { - return ParameterConvention::Direct_Unowned; -} - -/// Given a C parameter declaration whose type is passed directly, -/// deduce the convention for it. -static ParameterConvention -getDirectCParameterConvention(const clang::ParmVarDecl *param) { - if (param->hasAttr() || - param->hasAttr()) - return ParameterConvention::Direct_Owned; - return getDirectCParameterConvention(param->getType()); -} - -// FIXME: that should be Direct_Guaranteed -const auto ObjCSelfConvention = ParameterConvention::Direct_Unowned; - -namespace { - -class ObjCMethodConventions : public Conventions { - const clang::ObjCMethodDecl *Method; - -public: - const clang::ObjCMethodDecl *getMethod() const { return Method; } - - ObjCMethodConventions(const clang::ObjCMethodDecl *method) - : Conventions(ConventionsKind::ObjCMethod), Method(method) {} - - ParameterConvention getIndirectParameter(unsigned index, - const AbstractionPattern &type, - const TypeLowering &substTL) const override { - return getIndirectCParameterConvention(Method->param_begin()[index]); - } - - ParameterConvention getDirectParameter(unsigned index, - const AbstractionPattern &type, - const TypeLowering &substTL) const override { - return getDirectCParameterConvention(Method->param_begin()[index]); - } - - ParameterConvention getCallee() const override { - // Always thin. - return ParameterConvention::Direct_Unowned; - } - - /// Given that a method returns a CF type, infer its method - /// family. Unfortunately, Clang's getMethodFamily() never - /// considers a method to be in a special family if its result - /// doesn't satisfy isObjCRetainable(). - clang::ObjCMethodFamily getMethodFamilyForCFResult() const { - // Trust an explicit attribute. - if (auto attr = Method->getAttr()) { - switch (attr->getFamily()) { - case clang::ObjCMethodFamilyAttr::OMF_None: - return clang::OMF_None; - case clang::ObjCMethodFamilyAttr::OMF_alloc: - return clang::OMF_alloc; - case clang::ObjCMethodFamilyAttr::OMF_copy: - return clang::OMF_copy; - case clang::ObjCMethodFamilyAttr::OMF_init: - return clang::OMF_init; - case clang::ObjCMethodFamilyAttr::OMF_mutableCopy: - return clang::OMF_mutableCopy; - case clang::ObjCMethodFamilyAttr::OMF_new: - return clang::OMF_new; - } - llvm_unreachable("bad attribute value"); - } - - return Method->getSelector().getMethodFamily(); - } - - bool isImplicitPlusOneCFResult() const { - switch (getMethodFamilyForCFResult()) { - case clang::OMF_None: - case clang::OMF_dealloc: - case clang::OMF_finalize: - case clang::OMF_retain: - case clang::OMF_release: - case clang::OMF_autorelease: - case clang::OMF_retainCount: - case clang::OMF_self: - case clang::OMF_initialize: - case clang::OMF_performSelector: - return false; - - case clang::OMF_alloc: - case clang::OMF_new: - case clang::OMF_mutableCopy: - case clang::OMF_copy: - return true; - - case clang::OMF_init: - return Method->isInstanceMethod(); - } - llvm_unreachable("bad method family"); - } - - ResultConvention getResult(const TypeLowering &tl) const override { - // If we imported the result as something trivial, we need to - // use one of the unowned conventions. - if (tl.isTrivial()) { - if (Method->hasAttr()) - return ResultConvention::UnownedInnerPointer; - - auto type = tl.getLoweredType(); - if (type.unwrapOptionalType().getStructOrBoundGenericStruct() - == type.getASTContext().getUnmanagedDecl()) - return ResultConvention::UnownedInnerPointer; - return ResultConvention::Unowned; - } - - // Otherwise, the return type had better be a retainable object pointer. - auto resultType = Method->getReturnType(); - assert(resultType->isObjCRetainableType() || isCFTypedef(tl, resultType)); - - // If it's retainable for the purposes of ObjC ARC, we can trust - // the presence of ns_returns_retained, because Clang will add - // that implicitly based on the method family. - if (resultType->isObjCRetainableType()) { - if (Method->hasAttr()) - return ResultConvention::Owned; - return ResultConvention::Autoreleased; - } - - // Otherwise, it's a CF return type, which unfortunately means - // we can't just trust getMethodFamily(). We should really just - // change that, but that's an annoying change to make to Clang - // right now. - assert(isCFTypedef(tl, resultType)); - - // Trust the explicit attributes. - if (Method->hasAttr()) - return ResultConvention::Owned; - if (Method->hasAttr()) - return ResultConvention::Autoreleased; - - // Otherwise, infer based on the method family. - if (isImplicitPlusOneCFResult()) - return ResultConvention::Owned; - return ResultConvention::Autoreleased; - } - - ParameterConvention - getDirectSelfParameter(const AbstractionPattern &type) const override { - if (Method->hasAttr()) - return ParameterConvention::Direct_Owned; - - // The caller is supposed to take responsibility for ensuring - // that 'self' survives a method call. - return ObjCSelfConvention; - } - - ParameterConvention - getIndirectSelfParameter(const AbstractionPattern &type) const override { - llvm_unreachable("objc methods do not support indirect self parameters"); - } - - static bool classof(const Conventions *C) { - return C->getKind() == ConventionsKind::ObjCMethod; - } -}; - -/// Conventions based on a C function type. -class CFunctionTypeConventions : public Conventions { - const clang::FunctionType *FnType; - - clang::QualType getParamType(unsigned i) const { - return FnType->castAs()->getParamType(i); - } - -protected: - /// Protected constructor for subclasses to override the kind passed to the - /// super class. - CFunctionTypeConventions(ConventionsKind kind, - const clang::FunctionType *type) - : Conventions(kind), FnType(type) {} - -public: - CFunctionTypeConventions(const clang::FunctionType *type) - : Conventions(ConventionsKind::CFunctionType), FnType(type) {} - - ParameterConvention getIndirectParameter(unsigned index, - const AbstractionPattern &type, - const TypeLowering &substTL) const override { - return getIndirectCParameterConvention(getParamType(index)); - } - - ParameterConvention getDirectParameter(unsigned index, - const AbstractionPattern &type, - const TypeLowering &substTL) const override { - if (cast(FnType)->isParamConsumed(index)) - return ParameterConvention::Direct_Owned; - return getDirectCParameterConvention(getParamType(index)); - } - - ParameterConvention getCallee() const override { - // FIXME: blocks should be Direct_Guaranteed. - return ParameterConvention::Direct_Unowned; - } - - ResultConvention getResult(const TypeLowering &tl) const override { - if (tl.isTrivial()) - return ResultConvention::Unowned; - if (FnType->getExtInfo().getProducesResult()) - return ResultConvention::Owned; - return ResultConvention::Autoreleased; - } - - ParameterConvention - getDirectSelfParameter(const AbstractionPattern &type) const override { - llvm_unreachable("c function types do not have a self parameter"); - } - - ParameterConvention - getIndirectSelfParameter(const AbstractionPattern &type) const override { - llvm_unreachable("c function types do not have a self parameter"); - } - - static bool classof(const Conventions *C) { - return C->getKind() == ConventionsKind::CFunctionType; - } -}; - -/// Conventions based on C function declarations. -class CFunctionConventions : public CFunctionTypeConventions { - using super = CFunctionTypeConventions; - const clang::FunctionDecl *TheDecl; -public: - CFunctionConventions(const clang::FunctionDecl *decl) - : CFunctionTypeConventions(ConventionsKind::CFunction, - decl->getType()->castAs()), - TheDecl(decl) {} - - ParameterConvention getDirectParameter(unsigned index, - const AbstractionPattern &type, - const TypeLowering &substTL) const override { - if (auto param = TheDecl->getParamDecl(index)) - if (param->hasAttr()) - return ParameterConvention::Direct_Owned; - return super::getDirectParameter(index, type, substTL); - } - - ResultConvention getResult(const TypeLowering &tl) const override { - if (isCFTypedef(tl, TheDecl->getReturnType())) { - // The CF attributes aren't represented in the type, so we need - // to check them here. - if (TheDecl->hasAttr()) { - return ResultConvention::Owned; - } else if (TheDecl->hasAttr()) { - // Probably not actually autoreleased. - return ResultConvention::Autoreleased; - - // The CF Create/Copy rule only applies to functions that return - // a CF-runtime type; it does not apply to methods, and it does - // not apply to functions returning ObjC types. - } else if (clang::ento::coreFoundation::followsCreateRule(TheDecl)) { - return ResultConvention::Owned; - } else { - return ResultConvention::Autoreleased; - } - } - - // Otherwise, fall back on the ARC annotations, which are part - // of the type. - return super::getResult(tl); - } - - static bool classof(const Conventions *C) { - return C->getKind() == ConventionsKind::CFunction; - } -}; - -/// Conventions based on C++ method declarations. -class CXXMethodConventions : public CFunctionTypeConventions { - using super = CFunctionTypeConventions; - const clang::CXXMethodDecl *TheDecl; - -public: - CXXMethodConventions(const clang::CXXMethodDecl *decl) - : CFunctionTypeConventions( - ConventionsKind::CXXMethod, - decl->getType()->castAs()), - TheDecl(decl) {} - ParameterConvention - getIndirectSelfParameter(const AbstractionPattern &type) const override { - if (TheDecl->isConst()) - return ParameterConvention::Indirect_In_Guaranteed; - return ParameterConvention::Indirect_Inout; - } - ResultConvention getResult(const TypeLowering &resultTL) const override { - if (dyn_cast(TheDecl)) { - return ResultConvention::Indirect; - } - return CFunctionTypeConventions::getResult(resultTL); - } - static bool classof(const Conventions *C) { - return C->getKind() == ConventionsKind::CXXMethod; - } -}; - -} // end anonymous namespace - -/// Given that we have an imported Clang declaration, deduce the -/// ownership conventions for calling it and build the SILFunctionType. -static CanSILFunctionType -getSILFunctionTypeForClangDecl(TypeConverter &TC, const clang::Decl *clangDecl, - CanAnyFunctionType origType, - CanAnyFunctionType substInterfaceType, - AnyFunctionType::ExtInfo extInfo, - const ForeignInfo &foreignInfo, - Optional constant) { - if (auto method = dyn_cast(clangDecl)) { - auto origPattern = - AbstractionPattern::getObjCMethod(origType, method, foreignInfo.Error); - return getSILFunctionType(TC, TypeExpansionContext::minimal(), origPattern, - substInterfaceType, extInfo, - ObjCMethodConventions(method), foreignInfo, - constant, constant, None, - ProtocolConformanceRef()); - } - - if (auto method = dyn_cast(clangDecl)) { - AbstractionPattern origPattern = - AbstractionPattern::getCXXMethod(origType, method); - auto conventions = CXXMethodConventions(method); - return getSILFunctionType(TC, TypeExpansionContext::minimal(), origPattern, - substInterfaceType, extInfo, conventions, - foreignInfo, constant, constant, None, - ProtocolConformanceRef()); - } - - if (auto func = dyn_cast(clangDecl)) { - auto clangType = func->getType().getTypePtr(); - AbstractionPattern origPattern = - foreignInfo.Self.isImportAsMember() - ? AbstractionPattern::getCFunctionAsMethod(origType, clangType, - foreignInfo.Self) - : AbstractionPattern(origType, clangType); - return getSILFunctionType(TC, TypeExpansionContext::minimal(), origPattern, - substInterfaceType, extInfo, - CFunctionConventions(func), foreignInfo, constant, - constant, None, ProtocolConformanceRef()); - } - - llvm_unreachable("call to unknown kind of C function"); -} - -static CanSILFunctionType -getSILFunctionTypeForAbstractCFunction(TypeConverter &TC, - AbstractionPattern origType, - CanAnyFunctionType substType, - AnyFunctionType::ExtInfo extInfo, - Optional constant) { - if (origType.isClangType()) { - auto clangType = origType.getClangType(); - const clang::FunctionType *fnType; - if (auto blockPtr = clangType->getAs()) { - fnType = blockPtr->getPointeeType()->castAs(); - } else if (auto ptr = clangType->getAs()) { - fnType = ptr->getPointeeType()->getAs(); - } else if (auto ref = clangType->getAs()) { - fnType = ref->getPointeeType()->getAs(); - } else if (auto fn = clangType->getAs()) { - fnType = fn; - } else { - llvm_unreachable("unexpected type imported as a function type"); - } - if (fnType) { - return getSILFunctionType( - TC, TypeExpansionContext::minimal(), origType, substType, extInfo, - CFunctionTypeConventions(fnType), ForeignInfo(), constant, constant, - None, ProtocolConformanceRef()); - } - } - - // TODO: Ought to support captures in block funcs. - return getSILFunctionType(TC, TypeExpansionContext::minimal(), origType, - substType, extInfo, DefaultBlockConventions(), - ForeignInfo(), constant, constant, None, - ProtocolConformanceRef()); -} - -/// Try to find a clang method declaration for the given function. -static const clang::Decl *findClangMethod(ValueDecl *method) { - if (auto *methodFn = dyn_cast(method)) { - if (auto *decl = methodFn->getClangDecl()) - return decl; - - if (auto overridden = methodFn->getOverriddenDecl()) - return findClangMethod(overridden); - } - - if (auto *constructor = dyn_cast(method)) { - if (auto *decl = constructor->getClangDecl()) - return decl; - } - - return nullptr; -} - -//===----------------------------------------------------------------------===// -// Selector Family SILFunctionTypes -//===----------------------------------------------------------------------===// - -/// Derive the ObjC selector family from an identifier. -/// -/// Note that this will never derive the Init family, which is too dangerous -/// to leave to chance. Swift functions starting with "init" are always -/// emitted as if they are part of the "none" family. -static ObjCSelectorFamily getObjCSelectorFamily(ObjCSelector name) { - auto result = name.getSelectorFamily(); - - if (result == ObjCSelectorFamily::Init) - return ObjCSelectorFamily::None; - - return result; -} - -/// Get the ObjC selector family a foreign SILDeclRef belongs to. -static ObjCSelectorFamily getObjCSelectorFamily(SILDeclRef c) { - assert(c.isForeign); - switch (c.kind) { - case SILDeclRef::Kind::Func: { - if (!c.hasDecl()) - return ObjCSelectorFamily::None; - - auto *FD = cast(c.getDecl()); - if (auto accessor = dyn_cast(FD)) { - switch (accessor->getAccessorKind()) { - case AccessorKind::Get: - case AccessorKind::Set: - break; -#define OBJC_ACCESSOR(ID, KEYWORD) -#define ACCESSOR(ID) \ - case AccessorKind::ID: -#include "swift/AST/AccessorKinds.def" - llvm_unreachable("Unexpected AccessorKind of foreign FuncDecl"); - } - } - - return getObjCSelectorFamily(FD->getObjCSelector()); - } - case SILDeclRef::Kind::Initializer: - case SILDeclRef::Kind::IVarInitializer: - return ObjCSelectorFamily::Init; - - /// Currently IRGen wraps alloc/init methods into Swift constructors - /// with Swift conventions. - case SILDeclRef::Kind::Allocator: - /// These constants don't correspond to method families we care about yet. - case SILDeclRef::Kind::Destroyer: - case SILDeclRef::Kind::Deallocator: - case SILDeclRef::Kind::IVarDestroyer: - return ObjCSelectorFamily::None; - - case SILDeclRef::Kind::EnumElement: - case SILDeclRef::Kind::GlobalAccessor: - case SILDeclRef::Kind::DefaultArgGenerator: - case SILDeclRef::Kind::StoredPropertyInitializer: - case SILDeclRef::Kind::PropertyWrapperBackingInitializer: - llvm_unreachable("Unexpected Kind of foreign SILDeclRef"); - } - - llvm_unreachable("Unhandled SILDeclRefKind in switch."); -} - -namespace { - -class ObjCSelectorFamilyConventions : public Conventions { - ObjCSelectorFamily Family; - -public: - ObjCSelectorFamilyConventions(ObjCSelectorFamily family) - : Conventions(ConventionsKind::ObjCSelectorFamily), Family(family) {} - - ParameterConvention getIndirectParameter(unsigned index, - const AbstractionPattern &type, - const TypeLowering &substTL) const override { - return ParameterConvention::Indirect_In; - } - - ParameterConvention getDirectParameter(unsigned index, - const AbstractionPattern &type, - const TypeLowering &substTL) const override { - return ParameterConvention::Direct_Unowned; - } - - ParameterConvention getCallee() const override { - // Always thin. - return ParameterConvention::Direct_Unowned; - } - - ResultConvention getResult(const TypeLowering &tl) const override { - switch (Family) { - case ObjCSelectorFamily::Alloc: - case ObjCSelectorFamily::Copy: - case ObjCSelectorFamily::Init: - case ObjCSelectorFamily::MutableCopy: - case ObjCSelectorFamily::New: - return ResultConvention::Owned; - - case ObjCSelectorFamily::None: - // Defaults below. - break; - } - - // Get the underlying AST type, potentially stripping off one level of - // optionality while we do it. - CanType type = tl.getLoweredType().unwrapOptionalType().getASTType(); - if (type->hasRetainablePointerRepresentation() - || (type->getSwiftNewtypeUnderlyingType() && !tl.isTrivial())) - return ResultConvention::Autoreleased; - - return ResultConvention::Unowned; - } - - ParameterConvention - getDirectSelfParameter(const AbstractionPattern &type) const override { - if (Family == ObjCSelectorFamily::Init) - return ParameterConvention::Direct_Owned; - return ObjCSelfConvention; - } - - ParameterConvention - getIndirectSelfParameter(const AbstractionPattern &type) const override { - llvm_unreachable("selector family objc function types do not support " - "indirect self parameters"); - } - - static bool classof(const Conventions *C) { - return C->getKind() == ConventionsKind::ObjCSelectorFamily; - } -}; - -} // end anonymous namespace - -static CanSILFunctionType -getSILFunctionTypeForObjCSelectorFamily(TypeConverter &TC, ObjCSelectorFamily family, - CanAnyFunctionType origType, - CanAnyFunctionType substInterfaceType, - AnyFunctionType::ExtInfo extInfo, - const ForeignInfo &foreignInfo, - Optional constant) { - return getSILFunctionType( - TC, TypeExpansionContext::minimal(), AbstractionPattern(origType), - substInterfaceType, extInfo, ObjCSelectorFamilyConventions(family), - foreignInfo, constant, constant, - /*requirement subs*/ None, ProtocolConformanceRef()); -} - -static bool isImporterGeneratedAccessor(const clang::Decl *clangDecl, - SILDeclRef constant) { - // Must be an accessor. - auto accessor = dyn_cast(constant.getDecl()); - if (!accessor) - return false; - - // Must be a type member. - if (constant.getParameterListCount() != 2) - return false; - - // Must be imported from a function. - if (!isa(clangDecl)) - return false; - - return true; -} - -static CanSILFunctionType getUncachedSILFunctionTypeForConstant( - TypeConverter &TC, TypeExpansionContext context, SILDeclRef constant, - CanAnyFunctionType origLoweredInterfaceType) { - assert(origLoweredInterfaceType->getExtInfo().getSILRepresentation() - != SILFunctionTypeRepresentation::Thick - && origLoweredInterfaceType->getExtInfo().getSILRepresentation() - != SILFunctionTypeRepresentation::Block); - - auto extInfo = origLoweredInterfaceType->getExtInfo(); - - if (!constant.isForeign) { - ProtocolConformanceRef witnessMethodConformance; - - if (extInfo.getSILRepresentation() == - SILFunctionTypeRepresentation::WitnessMethod) { - auto proto = constant.getDecl()->getDeclContext()->getSelfProtocolDecl(); - witnessMethodConformance = ProtocolConformanceRef(proto); - } - - return ::getNativeSILFunctionType( - TC, context, AbstractionPattern(origLoweredInterfaceType), - origLoweredInterfaceType, extInfo, constant, constant, None, - witnessMethodConformance); - } - - ForeignInfo foreignInfo; - - // If we have a clang decl associated with the Swift decl, derive its - // ownership conventions. - if (constant.hasDecl()) { - auto decl = constant.getDecl(); - if (auto funcDecl = dyn_cast(decl)) { - foreignInfo.Error = funcDecl->getForeignErrorConvention(); - foreignInfo.Self = funcDecl->getImportAsMemberStatus(); - } - - if (auto clangDecl = findClangMethod(decl)) { - // The importer generates accessors that are not actually - // import-as-member but do involve the same gymnastics with the - // formal type. That's all that SILFunctionType cares about, so - // pretend that it's import-as-member. - if (!foreignInfo.Self.isImportAsMember() && - isImporterGeneratedAccessor(clangDecl, constant)) { - unsigned selfIndex = cast(decl)->isSetter() ? 1 : 0; - foreignInfo.Self.setSelfIndex(selfIndex); - } - - return getSILFunctionTypeForClangDecl(TC, clangDecl, - origLoweredInterfaceType, - origLoweredInterfaceType, - extInfo, foreignInfo, constant); - } - } - - // If the decl belongs to an ObjC method family, use that family's - // ownership conventions. - return getSILFunctionTypeForObjCSelectorFamily( - TC, getObjCSelectorFamily(constant), - origLoweredInterfaceType, origLoweredInterfaceType, - extInfo, foreignInfo, constant); -} - -CanSILFunctionType TypeConverter::getUncachedSILFunctionTypeForConstant( - TypeExpansionContext context, SILDeclRef constant, - CanAnyFunctionType origInterfaceType) { - auto origLoweredInterfaceType = - getLoweredFormalTypes(constant, origInterfaceType).Uncurried; - return ::getUncachedSILFunctionTypeForConstant(*this, context, constant, - origLoweredInterfaceType); -} - -static bool isClassOrProtocolMethod(ValueDecl *vd) { - if (!vd->getDeclContext()) - return false; - Type contextType = vd->getDeclContext()->getDeclaredInterfaceType(); - if (!contextType) - return false; - return contextType->getClassOrBoundGenericClass() - || contextType->isClassExistentialType(); -} - -SILFunctionTypeRepresentation -TypeConverter::getDeclRefRepresentation(SILDeclRef c) { - // If this is a foreign thunk, it always has the foreign calling convention. - if (c.isForeign) { - if (!c.hasDecl() || - c.getDecl()->isImportAsMember()) - return SILFunctionTypeRepresentation::CFunctionPointer; - - if (isClassOrProtocolMethod(c.getDecl()) || - c.kind == SILDeclRef::Kind::IVarInitializer || - c.kind == SILDeclRef::Kind::IVarDestroyer) - return SILFunctionTypeRepresentation::ObjCMethod; - - return SILFunctionTypeRepresentation::CFunctionPointer; - } - - // Anonymous functions currently always have Freestanding CC. - if (!c.hasDecl()) - return SILFunctionTypeRepresentation::Thin; - - // FIXME: Assert that there is a native entry point - // available. There's no great way to do this. - - // Protocol witnesses are called using the witness calling convention. - if (auto proto = dyn_cast(c.getDecl()->getDeclContext())) { - // Use the regular method convention for foreign-to-native thunks. - if (c.isForeignToNativeThunk()) - return SILFunctionTypeRepresentation::Method; - assert(!c.isNativeToForeignThunk() && "shouldn't be possible"); - return getProtocolWitnessRepresentation(proto); - } - - switch (c.kind) { - case SILDeclRef::Kind::GlobalAccessor: - case SILDeclRef::Kind::DefaultArgGenerator: - case SILDeclRef::Kind::StoredPropertyInitializer: - case SILDeclRef::Kind::PropertyWrapperBackingInitializer: - return SILFunctionTypeRepresentation::Thin; - - case SILDeclRef::Kind::Func: - if (c.getDecl()->getDeclContext()->isTypeContext()) - return SILFunctionTypeRepresentation::Method; - return SILFunctionTypeRepresentation::Thin; - - case SILDeclRef::Kind::Destroyer: - case SILDeclRef::Kind::Deallocator: - case SILDeclRef::Kind::Allocator: - case SILDeclRef::Kind::Initializer: - case SILDeclRef::Kind::EnumElement: - case SILDeclRef::Kind::IVarInitializer: - case SILDeclRef::Kind::IVarDestroyer: - return SILFunctionTypeRepresentation::Method; - } - - llvm_unreachable("Unhandled SILDeclRefKind in switch."); -} - -// Provide the ability to turn off the type converter cache to ease debugging. -static llvm::cl::opt - DisableConstantInfoCache("sil-disable-typelowering-constantinfo-cache", - llvm::cl::init(false)); - -const SILConstantInfo & -TypeConverter::getConstantInfo(TypeExpansionContext expansion, - SILDeclRef constant) { - if (!DisableConstantInfoCache) { - auto found = ConstantTypes.find(std::make_pair(expansion, constant)); - if (found != ConstantTypes.end()) - return *found->second; - } - - // First, get a function type for the constant. This creates the - // right type for a getter or setter. - auto formalInterfaceType = makeConstantInterfaceType(constant); - - // The formal type is just that with the right representation. - auto rep = getDeclRefRepresentation(constant); - formalInterfaceType = adjustFunctionType(formalInterfaceType, rep); - - // The lowered type is the formal type, but uncurried and with - // parameters automatically turned into their bridged equivalents. - auto bridgedTypes = getLoweredFormalTypes(constant, formalInterfaceType); - - CanAnyFunctionType loweredInterfaceType = bridgedTypes.Uncurried; - - // The SIL type encodes conventions according to the original type. - CanSILFunctionType silFnType = - ::getUncachedSILFunctionTypeForConstant(*this, expansion, constant, - loweredInterfaceType); - - // If the constant refers to a derivative function, get the SIL type of the - // original function and use it to compute the derivative SIL type. - // - // This is necessary because the "lowered AST derivative function type" (BC) - // may differ from the "derivative type of the lowered original function type" - // (AD): - // - // +--------------------+ lowering +--------------------+ - // | AST orig. fn type | -------(A)------> | SIL orig. fn type | - // +--------------------+ +--------------------+ - // | | - // (B, Sema) getAutoDiffDerivativeFunctionType (D, here) - // V V - // +--------------------+ lowering +--------------------+ - // | AST deriv. fn type | -------(C)------> | SIL deriv. fn type | - // +--------------------+ +--------------------+ - // - // (AD) does not always commute with (BC): - // - (BC) is the result of computing the AST derivative type (Sema), then - // lowering it via SILGen. This is the default lowering behavior, but may - // break SIL typing invariants because expected lowered derivative types are - // computed from lowered original function types. - // - (AD) is the result of lowering the original function type, then computing - // its derivative type. This is the expected lowered derivative type, - // preserving SIL typing invariants. - // - // Always use (AD) to compute lowered derivative function types. - if (auto *derivativeId = constant.derivativeFunctionIdentifier) { - // Get lowered original function type. - auto origFnConstantInfo = getConstantInfo( - TypeExpansionContext::minimal(), constant.asAutoDiffOriginalFunction()); - // Use it to compute lowered derivative function type. - auto *loweredIndices = autodiff::getLoweredParameterIndices( - derivativeId->getParameterIndices(), formalInterfaceType); - silFnType = origFnConstantInfo.SILFnType->getAutoDiffDerivativeFunctionType( - loweredIndices, /*resultIndex*/ 0, derivativeId->getKind(), *this, - LookUpConformanceInModule(&M)); - } - - LLVM_DEBUG(llvm::dbgs() << "lowering type for constant "; - constant.print(llvm::dbgs()); - llvm::dbgs() << "\n formal type: "; - formalInterfaceType.print(llvm::dbgs()); - llvm::dbgs() << "\n lowered AST type: "; - loweredInterfaceType.print(llvm::dbgs()); - llvm::dbgs() << "\n SIL type: "; - silFnType.print(llvm::dbgs()); - llvm::dbgs() << "\n Expansion context: " - << expansion.shouldLookThroughOpaqueTypeArchetypes(); - llvm::dbgs() << "\n"); - - auto resultBuf = Context.Allocate(sizeof(SILConstantInfo), - alignof(SILConstantInfo)); - - auto result = ::new (resultBuf) SILConstantInfo{formalInterfaceType, - bridgedTypes.Pattern, - loweredInterfaceType, - silFnType}; - if (DisableConstantInfoCache) - return *result; - - auto inserted = - ConstantTypes.insert({std::make_pair(expansion, constant), result}); - assert(inserted.second); - (void)inserted; - return *result; -} - -/// Returns the SILParameterInfo for the given declaration's `self` parameter. -/// `constant` must refer to a method. -SILParameterInfo -TypeConverter::getConstantSelfParameter(TypeExpansionContext context, - SILDeclRef constant) { - auto ty = getConstantFunctionType(context, constant); - - // In most cases the "self" parameter is lowered as the back parameter. - // The exception is C functions imported as methods. - if (!constant.isForeign) - return ty->getParameters().back(); - if (!constant.hasDecl()) - return ty->getParameters().back(); - auto fn = dyn_cast(constant.getDecl()); - if (!fn) - return ty->getParameters().back(); - if (fn->isImportAsStaticMember()) - return SILParameterInfo(); - if (fn->isImportAsInstanceMember()) - return ty->getParameters()[fn->getSelfIndex()]; - return ty->getParameters().back(); -} - -// This check duplicates TypeConverter::checkForABIDifferences(), -// but on AST types. The issue is we only want to introduce a new -// vtable thunk if the AST type changes, but an abstraction change -// is OK; we don't want a new entry if an @in parameter became -// @guaranteed or whatever. -static bool checkASTTypeForABIDifferences(CanType type1, - CanType type2) { - return !type1->matches(type2, TypeMatchFlags::AllowABICompatible); -} - -// FIXME: This makes me very upset. Can we do without this? -static CanType copyOptionalityFromDerivedToBase(TypeConverter &tc, - CanType derived, - CanType base) { - // Unwrap optionals, but remember that we did. - bool derivedWasOptional = false; - if (auto object = derived.getOptionalObjectType()) { - derivedWasOptional = true; - derived = object; - } - if (auto object = base.getOptionalObjectType()) { - base = object; - } - - // T? +> S = (T +> S)? - // T? +> S? = (T +> S)? - if (derivedWasOptional) { - base = copyOptionalityFromDerivedToBase(tc, derived, base); - - auto optDecl = tc.Context.getOptionalDecl(); - return CanType(BoundGenericEnumType::get(optDecl, Type(), base)); - } - - // (T1, T2, ...) +> (S1, S2, ...) = (T1 +> S1, T2 +> S2, ...) - if (auto derivedTuple = dyn_cast(derived)) { - if (auto baseTuple = dyn_cast(base)) { - assert(derivedTuple->getNumElements() == baseTuple->getNumElements()); - SmallVector elements; - for (unsigned i = 0, e = derivedTuple->getNumElements(); i < e; i++) { - elements.push_back( - baseTuple->getElement(i).getWithType( - copyOptionalityFromDerivedToBase( - tc, - derivedTuple.getElementType(i), - baseTuple.getElementType(i)))); - } - return CanType(TupleType::get(elements, tc.Context)); - } - } - - // (T1 -> T2) +> (S1 -> S2) = (T1 +> S1) -> (T2 +> S2) - if (auto derivedFunc = dyn_cast(derived)) { - if (auto baseFunc = dyn_cast(base)) { - SmallVector params; - - auto derivedParams = derivedFunc.getParams(); - auto baseParams = baseFunc.getParams(); - assert(derivedParams.size() == baseParams.size()); - for (unsigned i = 0, e = derivedParams.size(); i < e; i++) { - assert(derivedParams[i].getParameterFlags() == - baseParams[i].getParameterFlags()); - - params.emplace_back( - copyOptionalityFromDerivedToBase( - tc, - derivedParams[i].getPlainType(), - baseParams[i].getPlainType()), - Identifier(), - baseParams[i].getParameterFlags()); - } - - auto result = copyOptionalityFromDerivedToBase(tc, - derivedFunc.getResult(), - baseFunc.getResult()); - return CanAnyFunctionType::get(baseFunc.getOptGenericSignature(), - llvm::makeArrayRef(params), result, - baseFunc->getExtInfo()); - } - } - - return base; -} - -/// Returns the ConstantInfo corresponding to the VTable thunk for overriding. -/// Will be the same as getConstantInfo if the declaration does not override. -const SILConstantInfo & -TypeConverter::getConstantOverrideInfo(TypeExpansionContext context, - SILDeclRef derived, SILDeclRef base) { - // Foreign overrides currently don't need reabstraction. - if (derived.isForeign) - return getConstantInfo(context, derived); - - auto found = ConstantOverrideTypes.find({derived, base}); - if (found != ConstantOverrideTypes.end()) - return *found->second; - - assert(base.requiresNewVTableEntry() && "base must not be an override"); - - // Figure out the generic signature for the class method call. This is the - // signature of the derived class, with requirements transplanted from - // the base method. The derived method is allowed to have fewer - // requirements, in which case the thunk will translate the calling - // convention appropriately before calling the derived method. - bool hasGenericRequirementDifference = false; - - auto derivedSig = derived.getDecl()->getAsGenericContext() - ->getGenericSignature(); - auto genericSig = Context.getOverrideGenericSignature(base.getDecl(), - derived.getDecl()); - if (genericSig) { - hasGenericRequirementDifference = - !genericSig->requirementsNotSatisfiedBy(derivedSig).empty(); - } - - auto baseInfo = getConstantInfo(context, base); - auto derivedInfo = getConstantInfo(context, derived); - - auto params = derivedInfo.FormalType.getParams(); - assert(params.size() == 1); - auto selfInterfaceTy = params[0].getPlainType()->getMetatypeInstanceType(); - - auto overrideInterfaceTy = - cast( - selfInterfaceTy->adjustSuperclassMemberDeclType( - base.getDecl(), derived.getDecl(), baseInfo.FormalType) - ->getCanonicalType()); - - // Build the formal AST function type for the class method call. - auto basePattern = AbstractionPattern(baseInfo.LoweredType); - - if (!hasGenericRequirementDifference && - !checkASTTypeForABIDifferences(derivedInfo.FormalType, - overrideInterfaceTy)) { - - // The derived method is ABI-compatible with the base method. Let's - // just use the derived method's formal type. - basePattern = AbstractionPattern( - copyOptionalityFromDerivedToBase( - *this, - derivedInfo.LoweredType, - baseInfo.LoweredType)); - overrideInterfaceTy = derivedInfo.FormalType; - } - - if (genericSig && !genericSig->areAllParamsConcrete()) { - overrideInterfaceTy = - cast( - GenericFunctionType::get(genericSig, - overrideInterfaceTy->getParams(), - overrideInterfaceTy->getResult(), - overrideInterfaceTy->getExtInfo()) - ->getCanonicalType()); - } - - // Build the lowered AST function type for the class method call. - auto bridgedTypes = getLoweredFormalTypes(derived, overrideInterfaceTy); - - // Build the SILFunctionType for the class method call. - CanSILFunctionType fnTy = getNativeSILFunctionType( - *this, context, basePattern, bridgedTypes.Uncurried, base, derived, - /*reqt subs*/ None, ProtocolConformanceRef()); - - // Build the SILConstantInfo and cache it. - auto resultBuf = Context.Allocate(sizeof(SILConstantInfo), - alignof(SILConstantInfo)); - auto result = ::new (resultBuf) SILConstantInfo{ - overrideInterfaceTy, - basePattern, - bridgedTypes.Uncurried, - fnTy}; - - auto inserted = ConstantOverrideTypes.insert({{derived, base}, result}); - assert(inserted.second); - (void)inserted; - return *result; -} - -namespace { - -/// Given a lowered SIL type, apply a substitution to it to produce another -/// lowered SIL type which uses the same abstraction conventions. -class SILTypeSubstituter : - public CanTypeVisitor { - TypeConverter &TC; - TypeSubstitutionFn Subst; - LookupConformanceFn Conformances; - // The signature for the original type. - // - // Replacement types are lowered with respect to the current - // context signature. - CanGenericSignature Sig; - - TypeExpansionContext typeExpansionContext; - - bool shouldSubstituteOpaqueArchetypes; - -public: - SILTypeSubstituter(TypeConverter &TC, - TypeExpansionContext context, - TypeSubstitutionFn Subst, - LookupConformanceFn Conformances, - CanGenericSignature Sig, - bool shouldSubstituteOpaqueArchetypes) - : TC(TC), - Subst(Subst), - Conformances(Conformances), - Sig(Sig), - typeExpansionContext(context), - shouldSubstituteOpaqueArchetypes(shouldSubstituteOpaqueArchetypes) - {} - - // SIL type lowering only does special things to tuples and functions. - - // When a function appears inside of another type, we only perform - // substitutions if it is not polymorphic. - CanSILFunctionType visitSILFunctionType(CanSILFunctionType origType) { - return substSILFunctionType(origType, false); - } - - SubstitutionMap substSubstitutions(SubstitutionMap subs) { - // Substitute the substitutions. - SubstOptions options = None; - if (shouldSubstituteOpaqueArchetypes) - options |= SubstFlags::SubstituteOpaqueArchetypes; - - // Expand substituted type according to the expansion context. - auto newSubs = subs.subst(Subst, Conformances, options); - - // If we need to look through opaque types in this context, re-substitute - // according to the expansion context. - newSubs = substOpaqueTypes(newSubs); - - return newSubs; - } - - SubstitutionMap substOpaqueTypes(SubstitutionMap subs) { - if (!typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) - return subs; - - return subs.subst([&](SubstitutableType *s) -> Type { - return substOpaqueTypesWithUnderlyingTypes(s->getCanonicalType(), - typeExpansionContext); - }, [&](CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) -> ProtocolConformanceRef { - return substOpaqueTypesWithUnderlyingTypes( - ProtocolConformanceRef(conformedProtocol), - conformingReplacementType->getCanonicalType(), - typeExpansionContext); - }, SubstFlags::SubstituteOpaqueArchetypes); - } - - // Substitute a function type. - CanSILFunctionType substSILFunctionType(CanSILFunctionType origType, - bool isGenericApplication) { - assert((!isGenericApplication || origType->isPolymorphic()) && - "generic application without invocation signature or with " - "existing arguments"); - assert((!isGenericApplication || !shouldSubstituteOpaqueArchetypes) && - "generic application while substituting opaque archetypes"); - - // The general substitution rule is that we should only substitute - // into the free components of the type, i.e. the components that - // aren't inside a generic signature. That rule would say: - // - // - If there are invocation substitutions, just substitute those; - // the other components are necessarily inside the invocation - // generic signature. - // - // - Otherwise, if there's an invocation generic signature, - // substitute nothing. If we are applying generic arguments, - // add the appropriate invocation substitutions. - // - // - Otherwise, if there are pattern substitutions, just substitute - // those; the other components are inside the patttern generic - // signature. - // - // - Otherwise, substitute the basic components. - // - // There are two caveats here. The first is that we haven't yet - // written all the code that would be necessary in order to handle - // invocation substitutions everywhere, and so we never build those. - // Instead, we substitute into the pattern substitutions if present, - // or the components if not, and build a type with no invocation - // signature. As a special case, when substituting a coroutine type, - // we build pattern substitutions instead of substituting the - // component types in order to preserve the original yield structure, - // which factors into the continuation function ABI. - // - // The second is that this function is also used when substituting - // opaque archetypes. In this case, we may need to substitute - // into component types even within generic signatures. This is - // safe because the substitutions used in this case don't change - // generics, they just narrowly look through certain opaque archetypes. - // If substitutions are present, we still don't substitute into - // the basic components, in order to maintain the information about - // what was abstracted there. - - auto patternSubs = origType->getPatternSubstitutions(); - - // If we have an invocation signatture, we generally shouldn't - // substitute into the pattern substitutions and component types. - if (auto sig = origType->getInvocationGenericSignature()) { - // Substitute the invocation substitutions if present. - if (auto invocationSubs = origType->getInvocationSubstitutions()) { - assert(!isGenericApplication); - invocationSubs = substSubstitutions(invocationSubs); - auto substType = - origType->withInvocationSubstitutions(invocationSubs); - - // Also do opaque-type substitutions on the pattern substitutions - // if requested and applicable. - if (patternSubs) { - patternSubs = substOpaqueTypes(patternSubs); - substType = substType->withPatternSubstitutions(patternSubs); - } - - return substType; - } - - // Otherwise, we shouldn't substitute any components except - // when substituting opaque archetypes. - - // If we're doing a generic application, and there are pattern - // substitutions, substitute into the pattern substitutions; or if - // it's a coroutine, build pattern substitutions; or else, fall - // through to substitute the component types as discussed above. - if (isGenericApplication) { - if (patternSubs || origType->isCoroutine()) { - CanSILFunctionType substType = origType; - if (typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) { - substType = - origType->substituteOpaqueArchetypes(TC, typeExpansionContext); - } - - SubstitutionMap subs; - if (patternSubs) { - subs = substSubstitutions(patternSubs); - } else { - subs = SubstitutionMap::get(sig, Subst, Conformances); - } - auto witnessConformance = substWitnessConformance(origType); - substType = substType->withPatternSpecialization(nullptr, subs, - witnessConformance); - - return substType; - } - // else fall down to component substitution - - // If we're substituting opaque archetypes, and there are pattern - // substitutions present, just substitute those and preserve the - // basic structure in the component types. Otherwise, fall through - // to substitute the component types. - } else if (shouldSubstituteOpaqueArchetypes) { - if (patternSubs) { - patternSubs = substOpaqueTypes(patternSubs); - auto witnessConformance = substWitnessConformance(origType); - return origType->withPatternSpecialization(sig, patternSubs, - witnessConformance); - } - // else fall down to component substitution - - // Otherwise, don't try to substitute bound components. - } else { - auto substType = origType; - if (patternSubs) { - patternSubs = substOpaqueTypes(patternSubs); - auto witnessConformance = substWitnessConformance(origType); - substType = substType->withPatternSpecialization(sig, patternSubs, - witnessConformance); - } - return substType; - } - - // Otherwise, if there are pattern substitutions, just substitute - // into those and don't touch the component types. - } else if (patternSubs) { - patternSubs = substSubstitutions(patternSubs); - auto witnessConformance = substWitnessConformance(origType); - return origType->withPatternSpecialization(nullptr, patternSubs, - witnessConformance); - } - - // Otherwise, we need to substitute component types. - - SmallVector substResults; - substResults.reserve(origType->getNumResults()); - for (auto origResult : origType->getResults()) { - substResults.push_back(substInterface(origResult)); - } - - auto substErrorResult = origType->getOptionalErrorResult(); - assert(!substErrorResult || - (!substErrorResult->getInterfaceType()->hasTypeParameter() && - !substErrorResult->getInterfaceType()->hasArchetype())); - - SmallVector substParams; - substParams.reserve(origType->getParameters().size()); - for (auto &origParam : origType->getParameters()) { - substParams.push_back(substInterface(origParam)); - } - - SmallVector substYields; - substYields.reserve(origType->getYields().size()); - for (auto &origYield : origType->getYields()) { - substYields.push_back(substInterface(origYield)); - } - - auto witnessMethodConformance = substWitnessConformance(origType); - - // The substituted type is no longer generic, so it'd never be - // pseudogeneric. - auto extInfo = origType->getExtInfo(); - if (!shouldSubstituteOpaqueArchetypes) - extInfo = extInfo.withIsPseudogeneric(false); - - auto genericSig = shouldSubstituteOpaqueArchetypes - ? origType->getInvocationGenericSignature() - : nullptr; - - return SILFunctionType::get(genericSig, extInfo, - origType->getCoroutineKind(), - origType->getCalleeConvention(), substParams, - substYields, substResults, substErrorResult, - SubstitutionMap(), SubstitutionMap(), - TC.Context, witnessMethodConformance); - } - - ProtocolConformanceRef substWitnessConformance(CanSILFunctionType origType) { - auto conformance = origType->getWitnessMethodConformanceOrInvalid(); - if (!conformance) return conformance; - - assert(origType->getExtInfo().hasSelfParam()); - auto selfType = origType->getSelfParameter().getInterfaceType(); - - // The Self type can be nested in a few layers of metatypes (etc.). - while (auto metatypeType = dyn_cast(selfType)) { - auto next = metatypeType.getInstanceType(); - if (next == selfType) - break; - selfType = next; - } - - auto substConformance = - conformance.subst(selfType, Subst, Conformances); - - // Substitute the underlying conformance of opaque type archetypes if we - // should look through opaque archetypes. - if (typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) { - SubstOptions substOptions(None); - auto substType = selfType.subst(Subst, Conformances, substOptions) - ->getCanonicalType(); - if (substType->hasOpaqueArchetype()) { - substConformance = substOpaqueTypesWithUnderlyingTypes( - substConformance, substType, typeExpansionContext); - } - } - - return substConformance; - } - - SILType subst(SILType type) { - return SILType::getPrimitiveType(visit(type.getASTType()), - type.getCategory()); - } - - SILResultInfo substInterface(SILResultInfo orig) { - return SILResultInfo(visit(orig.getInterfaceType()), orig.getConvention()); - } - - SILYieldInfo substInterface(SILYieldInfo orig) { - return SILYieldInfo(visit(orig.getInterfaceType()), orig.getConvention()); - } - - SILParameterInfo substInterface(SILParameterInfo orig) { - return SILParameterInfo(visit(orig.getInterfaceType()), - orig.getConvention(), orig.getDifferentiability()); - } - - /// Tuples need to have their component types substituted by these - /// same rules. - CanType visitTupleType(CanTupleType origType) { - // Fast-path the empty tuple. - if (origType->getNumElements() == 0) return origType; - - SmallVector substElts; - substElts.reserve(origType->getNumElements()); - for (auto &origElt : origType->getElements()) { - auto substEltType = visit(CanType(origElt.getType())); - substElts.push_back(origElt.getWithType(substEltType)); - } - return CanType(TupleType::get(substElts, TC.Context)); - } - // Block storage types need to substitute their capture type by these same - // rules. - CanType visitSILBlockStorageType(CanSILBlockStorageType origType) { - auto substCaptureType = visit(origType->getCaptureType()); - return SILBlockStorageType::get(substCaptureType); - } - - /// Optionals need to have their object types substituted by these rules. - CanType visitBoundGenericEnumType(CanBoundGenericEnumType origType) { - // Only use a special rule if it's Optional. - if (!origType->getDecl()->isOptionalDecl()) { - return visitType(origType); - } - - CanType origObjectType = origType.getGenericArgs()[0]; - CanType substObjectType = visit(origObjectType); - return CanType(BoundGenericType::get(origType->getDecl(), Type(), - substObjectType)); - } - - /// Any other type would be a valid type in the AST. Just apply the - /// substitution on the AST level and then lower that. - CanType visitType(CanType origType) { - assert(!isa(origType)); - assert(!isa(origType) && !isa(origType)); - - SubstOptions substOptions(None); - if (shouldSubstituteOpaqueArchetypes) - substOptions = SubstFlags::SubstituteOpaqueArchetypes | - SubstFlags::AllowLoweredTypes; - auto substType = - origType.subst(Subst, Conformances, substOptions)->getCanonicalType(); - - // If the substitution didn't change anything, we know that the - // original type was a lowered type, so we're good. - if (origType == substType) { - return origType; - } - - AbstractionPattern abstraction(Sig, origType); - return TC.getLoweredRValueType(typeExpansionContext, abstraction, - substType); - } -}; - -} // end anonymous namespace - -SILType SILType::subst(TypeConverter &tc, TypeSubstitutionFn subs, - LookupConformanceFn conformances, - CanGenericSignature genericSig, - bool shouldSubstituteOpaqueArchetypes) const { - if (!hasArchetype() && !hasTypeParameter() && - (!shouldSubstituteOpaqueArchetypes || - !getASTType()->hasOpaqueArchetype())) - return *this; - - SILTypeSubstituter STST(tc, TypeExpansionContext::minimal(), subs, - conformances, genericSig, - shouldSubstituteOpaqueArchetypes); - return STST.subst(*this); -} - -SILType SILType::subst(SILModule &M, TypeSubstitutionFn subs, - LookupConformanceFn conformances, - CanGenericSignature genericSig, - bool shouldSubstituteOpaqueArchetypes) const { - return subst(M.Types, subs, conformances, genericSig, - shouldSubstituteOpaqueArchetypes); -} - -SILType SILType::subst(TypeConverter &tc, SubstitutionMap subs) const { - auto sig = subs.getGenericSignature(); - return subst(tc, QuerySubstitutionMap{subs}, - LookUpConformanceInSubstitutionMap(subs), - sig.getCanonicalSignature()); -} -SILType SILType::subst(SILModule &M, SubstitutionMap subs) const{ - return subst(M.Types, subs); -} - -/// Apply a substitution to this polymorphic SILFunctionType so that -/// it has the form of the normal SILFunctionType for the substituted -/// type, except using the original conventions. -CanSILFunctionType -SILFunctionType::substGenericArgs(SILModule &silModule, SubstitutionMap subs, - TypeExpansionContext context) { - if (!isPolymorphic()) { - return CanSILFunctionType(this); - } - - if (subs.empty()) { - return CanSILFunctionType(this); - } - - return substGenericArgs(silModule, - QuerySubstitutionMap{subs}, - LookUpConformanceInSubstitutionMap(subs), - context); -} - -CanSILFunctionType -SILFunctionType::substGenericArgs(SILModule &silModule, - TypeSubstitutionFn subs, - LookupConformanceFn conformances, - TypeExpansionContext context) { - if (!isPolymorphic()) return CanSILFunctionType(this); - SILTypeSubstituter substituter(silModule.Types, context, subs, conformances, - getSubstGenericSignature(), - /*shouldSubstituteOpaqueTypes*/ false); - return substituter.substSILFunctionType(CanSILFunctionType(this), true); -} - -CanSILFunctionType -SILFunctionType::substituteOpaqueArchetypes(TypeConverter &TC, - TypeExpansionContext context) { - if (!hasOpaqueArchetype() || - !context.shouldLookThroughOpaqueTypeArchetypes()) - return CanSILFunctionType(this); - - ReplaceOpaqueTypesWithUnderlyingTypes replacer( - context.getContext(), context.getResilienceExpansion(), - context.isWholeModuleContext()); - - SILTypeSubstituter substituter(TC, context, replacer, replacer, - getSubstGenericSignature(), - /*shouldSubstituteOpaqueTypes*/ true); - auto resTy = - substituter.substSILFunctionType(CanSILFunctionType(this), false); - - return resTy; -} - -/// Fast path for bridging types in a function type without uncurrying. -CanAnyFunctionType -TypeConverter::getBridgedFunctionType(AbstractionPattern pattern, - CanAnyFunctionType t, - AnyFunctionType::ExtInfo extInfo, - Bridgeability bridging) { - // Pull out the generic signature. - CanGenericSignature genericSig = t.getOptGenericSignature(); - - switch (auto rep = t->getExtInfo().getSILRepresentation()) { - case SILFunctionTypeRepresentation::Thick: - case SILFunctionTypeRepresentation::Thin: - case SILFunctionTypeRepresentation::Method: - case SILFunctionTypeRepresentation::Closure: - case SILFunctionTypeRepresentation::WitnessMethod: { - // No bridging needed for native functions. - if (t->getExtInfo() == extInfo) - return t; - return CanAnyFunctionType::get(genericSig, t.getParams(), t.getResult(), - extInfo); - } - - case SILFunctionTypeRepresentation::CFunctionPointer: - case SILFunctionTypeRepresentation::Block: - case SILFunctionTypeRepresentation::ObjCMethod: { - SmallVector params; - getBridgedParams(rep, pattern, t->getParams(), params, bridging); - - bool suppressOptional = pattern.hasForeignErrorStrippingResultOptionality(); - auto result = getBridgedResultType(rep, - pattern.getFunctionResultType(), - t.getResult(), - bridging, - suppressOptional); - - return CanAnyFunctionType::get(genericSig, llvm::makeArrayRef(params), - result, extInfo); - } - } - llvm_unreachable("bad calling convention"); -} - -static AbstractFunctionDecl *getBridgedFunction(SILDeclRef declRef) { - switch (declRef.kind) { - case SILDeclRef::Kind::Func: - case SILDeclRef::Kind::Allocator: - case SILDeclRef::Kind::Initializer: - return (declRef.hasDecl() - ? cast(declRef.getDecl()) - : nullptr); - - case SILDeclRef::Kind::EnumElement: - case SILDeclRef::Kind::Destroyer: - case SILDeclRef::Kind::Deallocator: - case SILDeclRef::Kind::GlobalAccessor: - case SILDeclRef::Kind::DefaultArgGenerator: - case SILDeclRef::Kind::StoredPropertyInitializer: - case SILDeclRef::Kind::PropertyWrapperBackingInitializer: - case SILDeclRef::Kind::IVarInitializer: - case SILDeclRef::Kind::IVarDestroyer: - return nullptr; - } - llvm_unreachable("bad SILDeclRef kind"); -} - -static AbstractionPattern -getAbstractionPatternForConstant(ASTContext &ctx, SILDeclRef constant, - CanAnyFunctionType fnType, - unsigned numParameterLists) { - if (!constant.isForeign) - return AbstractionPattern(fnType); - - auto bridgedFn = getBridgedFunction(constant); - if (!bridgedFn) - return AbstractionPattern(fnType); - const clang::Decl *clangDecl = bridgedFn->getClangDecl(); - if (!clangDecl) - return AbstractionPattern(fnType); - - // Don't implicitly turn non-optional results to optional if - // we're going to apply a foreign error convention that checks - // for nil results. - if (auto method = dyn_cast(clangDecl)) { - assert(numParameterLists == 2 && "getting curried ObjC method type?"); - auto foreignError = bridgedFn->getForeignErrorConvention(); - return AbstractionPattern::getCurriedObjCMethod(fnType, method, - foreignError); - } else if (auto value = dyn_cast(clangDecl)) { - if (numParameterLists == 1) { - // C function imported as a function. - return AbstractionPattern(fnType, value->getType().getTypePtr()); - } else { - assert(numParameterLists == 2); - if (auto method = dyn_cast(clangDecl)) { - // C++ method. - return AbstractionPattern::getCurriedCXXMethod(fnType, bridgedFn); - } else { - // C function imported as a method. - return AbstractionPattern::getCurriedCFunctionAsMethod(fnType, - bridgedFn); - } - } - } - - return AbstractionPattern(fnType); -} - -TypeConverter::LoweredFormalTypes -TypeConverter::getLoweredFormalTypes(SILDeclRef constant, - CanAnyFunctionType fnType) { - // We always use full bridging when importing a constant because we can - // directly bridge its arguments and results when calling it. - auto bridging = Bridgeability::Full; - - unsigned numParameterLists = constant.getParameterListCount(); - auto extInfo = fnType->getExtInfo(); - - // Form an abstraction pattern for bridging purposes. - AbstractionPattern bridgingFnPattern = - getAbstractionPatternForConstant(Context, constant, fnType, - numParameterLists); - - // Fast path: no uncurrying required. - if (numParameterLists == 1) { - auto bridgedFnType = - getBridgedFunctionType(bridgingFnPattern, fnType, extInfo, bridging); - bridgingFnPattern.rewriteType(bridgingFnPattern.getGenericSignature(), - bridgedFnType); - return { bridgingFnPattern, bridgedFnType }; - } - - SILFunctionTypeRepresentation rep = extInfo.getSILRepresentation(); - assert(rep != SILFunctionType::Representation::Block - && "objc blocks cannot be curried"); - - // The dependent generic signature. - CanGenericSignature genericSig = fnType.getOptGenericSignature(); - - // The 'self' parameter. - assert(fnType.getParams().size() == 1); - AnyFunctionType::Param selfParam = fnType.getParams()[0]; - - // The formal method parameters. - // If we actually partially-apply this, assume we'll need a thick function. - fnType = cast(fnType.getResult()); - auto innerExtInfo = - fnType->getExtInfo().withRepresentation(FunctionTypeRepresentation::Swift); - auto methodParams = fnType->getParams(); - - auto resultType = fnType.getResult(); - bool suppressOptionalResult = - bridgingFnPattern.hasForeignErrorStrippingResultOptionality(); - - // Bridge input and result types. - SmallVector bridgedParams; - CanType bridgedResultType; - - switch (rep) { - case SILFunctionTypeRepresentation::Thin: - case SILFunctionTypeRepresentation::Thick: - case SILFunctionTypeRepresentation::Method: - case SILFunctionTypeRepresentation::Closure: - case SILFunctionTypeRepresentation::WitnessMethod: - // Native functions don't need bridging. - bridgedParams.append(methodParams.begin(), methodParams.end()); - bridgedResultType = resultType; - break; - - case SILFunctionTypeRepresentation::ObjCMethod: - case SILFunctionTypeRepresentation::CFunctionPointer: { - if (rep == SILFunctionTypeRepresentation::ObjCMethod) { - // The "self" parameter should not get bridged unless it's a metatype. - if (selfParam.getPlainType()->is()) { - auto selfPattern = bridgingFnPattern.getFunctionParamType(0); - selfParam = getBridgedParam(rep, selfPattern, selfParam, bridging); - } - } - - auto partialFnPattern = bridgingFnPattern.getFunctionResultType(); - getBridgedParams(rep, partialFnPattern, methodParams, bridgedParams, - bridging); - - bridgedResultType = - getBridgedResultType(rep, - partialFnPattern.getFunctionResultType(), - resultType, bridging, suppressOptionalResult); - break; - } - - case SILFunctionTypeRepresentation::Block: - llvm_unreachable("Cannot uncurry native representation"); - } - - // Build the curried function type. - auto inner = - CanFunctionType::get(llvm::makeArrayRef(bridgedParams), - bridgedResultType, innerExtInfo); - - auto curried = - CanAnyFunctionType::get(genericSig, {selfParam}, inner, extInfo); - - // Replace the type in the abstraction pattern with the curried type. - bridgingFnPattern.rewriteType(genericSig, curried); - - // Build the uncurried function type. - if (innerExtInfo.throws()) - extInfo = extInfo.withThrows(true); - - bridgedParams.push_back(selfParam); - - auto uncurried = - CanAnyFunctionType::get(genericSig, - llvm::makeArrayRef(bridgedParams), - bridgedResultType, - extInfo); - - return { bridgingFnPattern, uncurried }; -} - -// TODO: We should compare generic signatures. Class and witness methods -// allow variance in "self"-fulfilled parameters; other functions must -// match exactly. -// TODO: More sophisticated param and return ABI compatibility rules could -// diverge. -static bool areABICompatibleParamsOrReturns(SILType a, SILType b, - SILFunction *inFunction) { - // Address parameters are all ABI-compatible, though the referenced - // values may not be. Assume whoever's doing this knows what they're - // doing. - if (a.isAddress() && b.isAddress()) - return true; - - // Addresses aren't compatible with values. - // TODO: An exception for pointerish types? - if (a.isAddress() || b.isAddress()) - return false; - - // Tuples are ABI compatible if their elements are. - // TODO: Should destructure recursively. - SmallVector aElements, bElements; - if (auto tup = a.getAs()) { - auto types = tup.getElementTypes(); - aElements.append(types.begin(), types.end()); - } else { - aElements.push_back(a.getASTType()); - } - if (auto tup = b.getAs()) { - auto types = tup.getElementTypes(); - bElements.append(types.begin(), types.end()); - } else { - bElements.push_back(b.getASTType()); - } - - if (aElements.size() != bElements.size()) - return false; - - for (unsigned i : indices(aElements)) { - auto aa = SILType::getPrimitiveObjectType(aElements[i]); - auto bb = SILType::getPrimitiveObjectType(bElements[i]); - // Equivalent types are always ABI-compatible. - if (aa == bb) - continue; - - // Opaque types are compatible with their substitution. - if (inFunction) { - auto opaqueTypesSubsituted = aa; - auto *dc = inFunction->getDeclContext(); - auto *currentModule = inFunction->getModule().getSwiftModule(); - if (!dc || !dc->isChildContextOf(currentModule)) - dc = currentModule; - ReplaceOpaqueTypesWithUnderlyingTypes replacer( - dc, inFunction->getResilienceExpansion(), - inFunction->getModule().isWholeModule()); - if (aa.getASTType()->hasOpaqueArchetype()) - opaqueTypesSubsituted = aa.subst(inFunction->getModule(), replacer, - replacer, CanGenericSignature(), true); - - auto opaqueTypesSubsituted2 = bb; - if (bb.getASTType()->hasOpaqueArchetype()) - opaqueTypesSubsituted2 = - bb.subst(inFunction->getModule(), replacer, replacer, - CanGenericSignature(), true); - if (opaqueTypesSubsituted == opaqueTypesSubsituted2) - continue; - } - - // FIXME: If one or both types are dependent, we can't accurately assess - // whether they're ABI-compatible without a generic context. We can - // do a better job here when dependent types are related to their - // generic signatures. - if (aa.hasTypeParameter() || bb.hasTypeParameter()) - continue; - - // Bridgeable object types are interchangeable. - if (aa.isBridgeableObjectType() && bb.isBridgeableObjectType()) - continue; - - // Optional and IUO are interchangeable if their elements are. - auto aObject = aa.getOptionalObjectType(); - auto bObject = bb.getOptionalObjectType(); - if (aObject && bObject && - areABICompatibleParamsOrReturns(aObject, bObject, inFunction)) - continue; - // Optional objects are ABI-interchangeable with non-optionals; - // None is represented by a null pointer. - if (aObject && aObject.isBridgeableObjectType() && - bb.isBridgeableObjectType()) - continue; - if (bObject && bObject.isBridgeableObjectType() && - aa.isBridgeableObjectType()) - continue; - - // Optional thick metatypes are ABI-interchangeable with non-optionals - // too. - if (aObject) - if (auto aObjMeta = aObject.getAs()) - if (auto bMeta = bb.getAs()) - if (aObjMeta->getRepresentation() == bMeta->getRepresentation() && - bMeta->getRepresentation() != MetatypeRepresentation::Thin) - continue; - if (bObject) - if (auto aMeta = aa.getAs()) - if (auto bObjMeta = bObject.getAs()) - if (aMeta->getRepresentation() == bObjMeta->getRepresentation() && - aMeta->getRepresentation() != MetatypeRepresentation::Thin) - continue; - - // Function types are interchangeable if they're also ABI-compatible. - if (auto aFunc = aa.getAs()) { - if (auto bFunc = bb.getAs()) { - // *NOTE* We swallow the specific error here for now. We will still get - // that the function types are incompatible though, just not more - // specific information. - return aFunc->isABICompatibleWith(bFunc, *inFunction).isCompatible(); - } - } - - // Metatypes are interchangeable with metatypes with the same - // representation. - if (auto aMeta = aa.getAs()) { - if (auto bMeta = bb.getAs()) { - if (aMeta->getRepresentation() == bMeta->getRepresentation()) - continue; - } - } - // Other types must match exactly. - return false; - } - - return true; -} - -namespace { -using ABICompatibilityCheckResult = - SILFunctionType::ABICompatibilityCheckResult; -} // end anonymous namespace - -ABICompatibilityCheckResult -SILFunctionType::isABICompatibleWith(CanSILFunctionType other, - SILFunction &context) const { - // The calling convention and function representation can't be changed. - if (getRepresentation() != other->getRepresentation()) - return ABICompatibilityCheckResult::DifferentFunctionRepresentations; - - // Check the results. - if (getNumResults() != other->getNumResults()) - return ABICompatibilityCheckResult::DifferentNumberOfResults; - - for (unsigned i : indices(getResults())) { - auto result1 = getResults()[i]; - auto result2 = other->getResults()[i]; - - if (result1.getConvention() != result2.getConvention()) - return ABICompatibilityCheckResult::DifferentReturnValueConventions; - - if (!areABICompatibleParamsOrReturns( - result1.getSILStorageType(context.getModule(), this), - result2.getSILStorageType(context.getModule(), other), - &context)) { - return ABICompatibilityCheckResult::ABIIncompatibleReturnValues; - } - } - - // Our error result conventions are designed to be ABI compatible - // with functions lacking error results. Just make sure that the - // actual conventions match up. - if (hasErrorResult() && other->hasErrorResult()) { - auto error1 = getErrorResult(); - auto error2 = other->getErrorResult(); - if (error1.getConvention() != error2.getConvention()) - return ABICompatibilityCheckResult::DifferentErrorResultConventions; - - if (!areABICompatibleParamsOrReturns( - error1.getSILStorageType(context.getModule(), this), - error2.getSILStorageType(context.getModule(), other), - &context)) - return ABICompatibilityCheckResult::ABIIncompatibleErrorResults; - } - - // Check the parameters. - // TODO: Could allow known-empty types to be inserted or removed, but SIL - // doesn't know what empty types are yet. - if (getParameters().size() != other->getParameters().size()) - return ABICompatibilityCheckResult::DifferentNumberOfParameters; - - for (unsigned i : indices(getParameters())) { - auto param1 = getParameters()[i]; - auto param2 = other->getParameters()[i]; - - if (param1.getConvention() != param2.getConvention()) - return {ABICompatibilityCheckResult::DifferingParameterConvention, i}; - if (!areABICompatibleParamsOrReturns( - param1.getSILStorageType(context.getModule(), this), - param2.getSILStorageType(context.getModule(), other), - &context)) - return {ABICompatibilityCheckResult::ABIIncompatibleParameterType, i}; - } - - // This needs to be checked last because the result implies everying else has - // already been checked and this is the only difference. - if (isNoEscape() != other->isNoEscape() && - (getRepresentation() == SILFunctionType::Representation::Thick)) - return ABICompatibilityCheckResult::ABIEscapeToNoEscapeConversion; - - return ABICompatibilityCheckResult::None; -} - -StringRef SILFunctionType::ABICompatibilityCheckResult::getMessage() const { - switch (kind) { - case innerty::None: - return "None"; - case innerty::DifferentFunctionRepresentations: - return "Different function representations"; - case innerty::DifferentNumberOfResults: - return "Different number of results"; - case innerty::DifferentReturnValueConventions: - return "Different return value conventions"; - case innerty::ABIIncompatibleReturnValues: - return "ABI incompatible return values"; - case innerty::DifferentErrorResultConventions: - return "Different error result conventions"; - case innerty::ABIIncompatibleErrorResults: - return "ABI incompatible error results"; - case innerty::DifferentNumberOfParameters: - return "Different number of parameters"; - - // These two have to do with specific parameters, so keep the error message - // non-plural. - case innerty::DifferingParameterConvention: - return "Differing parameter convention"; - case innerty::ABIIncompatibleParameterType: - return "ABI incompatible parameter type."; - case innerty::ABIEscapeToNoEscapeConversion: - return "Escape to no escape conversion"; - } - llvm_unreachable("Covered switch isn't completely covered?!"); -} - -static DeclContext *getDeclContextForExpansion(const SILFunction &f) { - auto *dc = f.getDeclContext(); - if (!dc) - dc = f.getModule().getSwiftModule(); - auto *currentModule = f.getModule().getSwiftModule(); - if (!dc || !dc->isChildContextOf(currentModule)) - dc = currentModule; - return dc; -} - -TypeExpansionContext::TypeExpansionContext(const SILFunction &f) - : expansion(f.getResilienceExpansion()), - inContext(getDeclContextForExpansion(f)), - isContextWholeModule(f.getModule().isWholeModule()) {} - -CanSILFunctionType SILFunction::getLoweredFunctionTypeInContext( - TypeExpansionContext context) const { - auto origFunTy = getLoweredFunctionType(); - auto &M = getModule(); - auto funTy = M.Types.getLoweredType(origFunTy , context); - return cast(funTy.getASTType()); -} diff --git a/test/Interop/Cxx/class/Inputs/type-classification.h b/test/Interop/Cxx/class/Inputs/type-classification.h index 09322bb5d0d1e..176e22e1701af 100644 --- a/test/Interop/Cxx/class/Inputs/type-classification.h +++ b/test/Interop/Cxx/class/Inputs/type-classification.h @@ -157,6 +157,8 @@ struct StructDeletedDestructor { struct StructWithCopyConstructorAndValue { int value; + StructWithCopyConstructorAndValue() : value(0) {} + StructWithCopyConstructorAndValue(int value) : value(value) {} StructWithCopyConstructorAndValue( const StructWithCopyConstructorAndValue &other) : value(other.value) {} @@ -168,6 +170,9 @@ struct StructWithSubobjectCopyConstructorAndValue { struct StructWithCopyConstructorAndSubobjectCopyConstructorAndValue { StructWithCopyConstructorAndValue member; + StructWithCopyConstructorAndSubobjectCopyConstructorAndValue( + StructWithCopyConstructorAndValue member) + : member(member) {} StructWithCopyConstructorAndSubobjectCopyConstructorAndValue( const StructWithCopyConstructorAndSubobjectCopyConstructorAndValue &other) : member(other.member) {} diff --git a/test/Interop/Cxx/class/constructors-irgen.swift b/test/Interop/Cxx/class/constructors-irgen.swift index cb98844dbf3a2..75b1c015c403a 100644 --- a/test/Interop/Cxx/class/constructors-irgen.swift +++ b/test/Interop/Cxx/class/constructors-irgen.swift @@ -5,8 +5,11 @@ // RUN: %swift -module-name Swift -target x86_64-unknown-windows-msvc -dump-clang-diagnostics -I %S/Inputs -enable-cxx-interop -emit-ir %s -parse-stdlib -parse-as-library -disable-legacy-type-info | %FileCheck %s -check-prefix=MICROSOFT_X64 import Constructors +import TypeClassification typealias Void = () +struct UnsafePointer { } +struct UnsafeMutablePointer { } public func createHasVirtualBase() -> HasVirtualBase { // - The `this` parameter should carry a `noalias` attribute, as it is @@ -58,3 +61,34 @@ public func createImplicitDefaultConstructor() -> ImplicitDefaultConstructor { // MICROSOFT_X64: call %struct.ImplicitDefaultConstructor* @"??0ImplicitDefaultConstructor@@QEAA@XZ"(%struct.ImplicitDefaultConstructor* %{{[0-9]+}}) return ImplicitDefaultConstructor() } + +public func createStructWithSubobjectCopyConstructorAndValue() { + // ITANIUM_X64-LABEL: define swiftcc void @"$ss48createStructWithSubobjectCopyConstructorAndValueyyF"() + // ITANIUM_X64: [[MEMBER:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV + // ITANIUM_X64: [[MEMBER_AS_STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* %member to %struct.StructWithCopyConstructorAndValue* + // ITANIUM_X64: void @_ZN33StructWithCopyConstructorAndValueC1Ev(%struct.StructWithCopyConstructorAndValue* noalias [[MEMBER_AS_STRUCT]]) + // ITANIUM_X64: call %TSo33StructWithCopyConstructorAndValueV* @"$sSo33StructWithCopyConstructorAndValueVWOc"(%TSo33StructWithCopyConstructorAndValueV* [[MEMBER]], %TSo33StructWithCopyConstructorAndValueV* [[TMP:%.*]]) + // ITANIUM_X64: [[OBJ_MEMBER:%.*]] = getelementptr inbounds %TSo42StructWithSubobjectCopyConstructorAndValueV, %TSo42StructWithSubobjectCopyConstructorAndValueV* %obj, i32 0, i32 0 + // ITANIUM_X64: call %TSo33StructWithCopyConstructorAndValueV* @"$sSo33StructWithCopyConstructorAndValueVWOb"(%TSo33StructWithCopyConstructorAndValueV* [[TMP]], %TSo33StructWithCopyConstructorAndValueV* [[OBJ_MEMBER]]) + // ITANIUM_X64: ret void + + // ITANIUM_ARM-LABEL: define protected swiftcc void @"$ss48createStructWithSubobjectCopyConstructorAndValueyyF"() + // ITANIUM_ARM: [[MEMBER:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV + // ITANIUM_ARM: [[MEMBER_AS_STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* %member to %struct.StructWithCopyConstructorAndValue* + // ITANIUM_ARM: call %struct.StructWithCopyConstructorAndValue* @_ZN33StructWithCopyConstructorAndValueC2Ev(%struct.StructWithCopyConstructorAndValue* [[MEMBER_AS_STRUCT]]) + // ITANIUM_ARM: call %TSo33StructWithCopyConstructorAndValueV* @"$sSo33StructWithCopyConstructorAndValueVWOc"(%TSo33StructWithCopyConstructorAndValueV* [[MEMBER]], %TSo33StructWithCopyConstructorAndValueV* [[TMP:%.*]]) + // ITANIUM_ARM: [[OBJ_MEMBER:%.*]] = getelementptr inbounds %TSo42StructWithSubobjectCopyConstructorAndValueV, %TSo42StructWithSubobjectCopyConstructorAndValueV* %obj, i32 0, i32 0 + // ITANIUM_ARM: call %TSo33StructWithCopyConstructorAndValueV* @"$sSo33StructWithCopyConstructorAndValueVWOb"(%TSo33StructWithCopyConstructorAndValueV* [[TMP]], %TSo33StructWithCopyConstructorAndValueV* [[OBJ_MEMBER]]) + // ITANIUM_ARM: ret void + + // MICROSOFT_X64-LABEL: define dllexport swiftcc void @"$ss48createStructWithSubobjectCopyConstructorAndValueyyF"() + // MICROSOFT_X64: [[MEMBER:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV + // MICROSOFT_X64: [[MEMBER_AS_STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* %member to %struct.StructWithCopyConstructorAndValue* + // MICROSOFT_X64: call %struct.StructWithCopyConstructorAndValue* @"??0StructWithCopyConstructorAndValue@@QEAA@XZ"(%struct.StructWithCopyConstructorAndValue* [[MEMBER_AS_STRUCT]]) + // MICROSOFT_X64: call %TSo33StructWithCopyConstructorAndValueV* @"$sSo33StructWithCopyConstructorAndValueVWOc"(%TSo33StructWithCopyConstructorAndValueV* [[MEMBER]], %TSo33StructWithCopyConstructorAndValueV* [[TMP:%.*]]) + // MICROSOFT_X64: [[OBJ_MEMBER:%.*]] = getelementptr inbounds %TSo42StructWithSubobjectCopyConstructorAndValueV, %TSo42StructWithSubobjectCopyConstructorAndValueV* %obj, i32 0, i32 0 + // MICROSOFT_X64: call %TSo33StructWithCopyConstructorAndValueV* @"$sSo33StructWithCopyConstructorAndValueVWOb"(%TSo33StructWithCopyConstructorAndValueV* [[TMP]], %TSo33StructWithCopyConstructorAndValueV* [[OBJ_MEMBER]]) + // MICROSOFT_X64: ret void + let member = StructWithCopyConstructorAndValue() + let obj = StructWithSubobjectCopyConstructorAndValue(member: member) +} diff --git a/test/Interop/Cxx/class/synthesized-initializers-silgen.swift b/test/Interop/Cxx/class/synthesized-initializers-silgen.swift index ea74f8ab4eecb..8b84f18bb1569 100644 --- a/test/Interop/Cxx/class/synthesized-initializers-silgen.swift +++ b/test/Interop/Cxx/class/synthesized-initializers-silgen.swift @@ -2,36 +2,26 @@ import SynthesizedInitializers -// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo11EmptyStructVABycfC : $@convention(method) (@thin EmptyStruct.Type) -> EmptyStruct -// CHECK: bb0(%{{[0-9]+}} : $@thin EmptyStruct.Type): -// CHECK-NEXT: [[BOX:%.*]] = alloc_box ${ var EmptyStruct } -// CHECK-NEXT: [[UNINIT:%.*]] = mark_uninitialized [rootself] [[BOX]] : ${ var EmptyStruct } -// CHECK-NEXT: [[PTR:%.*]] = project_box [[UNINIT]] : ${ var EmptyStruct }, 0 -// CHECK-NEXT: [[OBJ:%.*]] = builtin "zeroInitializer"() : $EmptyStruct -// CHECK-NEXT: [[PA:%.*]] = begin_access [modify] [unknown] [[PTR]] : $*EmptyStruct -// CHECK-NEXT: assign [[OBJ]] to [[PA]] -// CHECK-NEXT: end_access [[PA]] -// CHECK-NEXT: [[OUT:%.*]] = load [trivial] [[PTR]] -// CHECK-NEXT: destroy_value [[UNINIT]] -// CHECK-NEXT: return [[OUT]] -// CHECK-LABEL: end sil function '$sSo11EmptyStructVABycfC' +// CHECK-LABEL: sil [ossa] @$s4main18emptyTypeNoArgInityyF : $@convention(thin) () -> () +// CHECK: [[AS:%.*]] = alloc_stack $EmptyStruct +// CHECK: [[META:%.*]] = metatype $@thin EmptyStruct.Type +// CHECK: [[FN:%.*]] = function_ref @{{_ZN11EmptyStructC1Ev|\?\?0EmptyStruct@@QEAA@XZ}} : $@convention(c) (@thin EmptyStruct.Type) -> @out EmptyStruct +// CHECK: apply [[FN]]([[AS]], [[META]]) : $@convention(c) (@thin EmptyStruct.Type) -> @out EmptyStruct +// CHECK-LABEL: end sil function '$s4main18emptyTypeNoArgInityyF' + +// CHECL-LABEL: sil [clang EmptyStruct.init] @{{_ZN11EmptyStructC1Ev|\?\?0EmptyStruct@@QEAA@XZ}} : $@convention(c) (@thin EmptyStruct.Type) -> @out EmptyStruct public func emptyTypeNoArgInit() { let e = EmptyStruct() } -// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo6IntBoxVABycfC : $@convention(method) (@thin IntBox.Type) -> IntBox -// CHECK: bb0(%{{[0-9]+}} : $@thin IntBox.Type): -// CHECK-NEXT: [[BOX:%.*]] = alloc_box ${ var IntBox } -// CHECK-NEXT: [[UNINIT:%.*]] = mark_uninitialized [rootself] [[BOX]] : ${ var IntBox } -// CHECK-NEXT: [[PTR:%.*]] = project_box [[UNINIT]] : ${ var IntBox }, 0 -// CHECK-NEXT: [[OBJ:%.*]] = builtin "zeroInitializer"() : $IntBox -// CHECK-NEXT: [[PA:%.*]] = begin_access [modify] [unknown] [[PTR]] : $*IntBox -// CHECK-NEXT: assign [[OBJ]] to [[PA]] -// CHECK-NEXT: end_access [[PA]] -// CHECK-NEXT: [[OUT:%.*]] = load [trivial] [[PTR]] -// CHECK-NEXT: destroy_value [[UNINIT]] -// CHECK-NEXT: return [[OUT]] -// CHECK-LABEL: end sil function '$sSo6IntBoxVABycfC' +// CHECK-LABEL: sil [ossa] @$s4main25singleMemberTypeNoArgInityyF : $@convention(thin) () -> () +// CHECK: [[AS:%.*]] = alloc_stack $IntBox +// CHECK: [[META:%.*]] = metatype $@thin IntBox.Type +// CHECK: [[FN:%.*]] = function_ref @{{_ZN6IntBoxC1Ev|\?\?0IntBox@@QEAA@XZ}} : $@convention(c) (@thin IntBox.Type) -> @out IntBox +// CHECK: apply [[FN]]([[AS]], [[META]]) : $@convention(c) (@thin IntBox.Type) -> @out IntBox +// CHECK-LABEL: end sil function '$s4main25singleMemberTypeNoArgInityyF' + +//CHECK-LABEL: sil [clang IntBox.init] @{{_ZN6IntBoxC1Ev|\?\?0IntBox@@QEAA@XZ}} : $@convention(c) (@thin IntBox.Type) -> @out IntBox public func singleMemberTypeNoArgInit() { let i = IntBox() } diff --git a/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift b/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift index c0a48574a1493..501b444fa4774 100644 --- a/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift +++ b/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift @@ -12,12 +12,15 @@ import TypeClassification // CHECK-LABEL: define {{.*}}i1 @"$s4main37testStructWithCopyConstructorAndValueSbyF" // CHECK: [[OBJ:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV -// CHECK: [[VAL_ELEMENT:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[OBJ]], i32 0, i32 0 -// CHECK: [[VAL_INT:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[VAL_ELEMENT]], i32 0, i32 0 -// CHECK: store i32 42, i32* [[VAL_INT]] -// CHECK: ret i1 true +// CHECK: [[STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* [[OBJ]] to %struct.StructWithCopyConstructorAndValue* +// CHECK: call void @{{_ZN33StructWithCopyConstructorAndValueC(1|2)Ei|"\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z"}}(%struct.StructWithCopyConstructorAndValue* noalias [[STRUCT]], i32 42) +// CHECK: [[OBJ_VAL:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[OBJ]], i32 0, i32 0 +// CHECK: [[I_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[OBJ_VAL]], i32 0, i32 0 +// CHECK: [[I_VAL_VAL:%.*]] = load i32, i32* [[OBJ_VAL]] +// CHECK: [[OUT:%.*]] = icmp eq i32 [[I_VAL_VAL]], 42 +// CHECK: ret i1 [[OUT]] public func testStructWithCopyConstructorAndValue() -> Bool { - let obj = StructWithCopyConstructorAndValue(value: 42) + let obj = StructWithCopyConstructorAndValue(42) return obj.value == 42 } @@ -26,38 +29,39 @@ public func testStructWithCopyConstructorAndValue() -> Bool { // CHECK: [[OBJ:%.*]] = alloca %TSo42StructWithSubobjectCopyConstructorAndValueV // CHECK: alloca %TSo33StructWithCopyConstructorAndValueV // CHECK: [[TMP:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV -// CHECK: [[MEMBER_ELEMENT:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[MEMBER]], i32 0, i32 0 -// CHECK: [[MEMBER_VALUE:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[MEMBER_ELEMENT]], i32 0, i32 0 -// CHECK: store i32 42, i32* [[MEMBER_VALUE]] +// CHECK: [[MEMBER_STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* %member to %struct.StructWithCopyConstructorAndValue* +// CHECK: call void @{{_ZN33StructWithCopyConstructorAndValueC(1|2)Ei|"\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z"}}(%struct.StructWithCopyConstructorAndValue* noalias [[MEMBER_STRUCT]], i32 42) // CHECK: [[TEMP_MEMBER:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[TMP]], i32 0, i32 0 // CHECK: [[TEMP_MEMBER_VALUE:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[TEMP_MEMBER]], i32 0, i32 0 // CHECK: [[LHS:%.*]] = load i32, i32* [[TEMP_MEMBER_VALUE]] // CHECK: [[OUT:%.*]] = icmp eq i32 [[LHS]], 42 // CHECK: ret i1 [[OUT]] public func testStructWithSubobjectCopyConstructorAndValue() -> Bool { - let member = StructWithCopyConstructorAndValue(value: 42) + let member = StructWithCopyConstructorAndValue(42) let obj = StructWithSubobjectCopyConstructorAndValue(member: member) return obj.member.value == 42 } // CHECK-LABEL: define {{.*}}i1 @"$s4main041testStructWithCopyConstructorAndSubobjectefG5ValueSbyF"() // CHECK: [[MEMBER:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV -// CHECK: alloca %TSo037StructWithCopyConstructorAndSubobjectcdE5ValueV -// CHECK: alloca %TSo33StructWithCopyConstructorAndValueV +// CHECK: [[OBJ:%.*]] = alloca %TSo037StructWithCopyConstructorAndSubobjectcdE5ValueV // CHECK: [[TEMP:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV -// CHECK: [[MEMBER_VAL:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[MEMBER]], i32 0, i32 0 -// CHECK: [[MEMBER_VAL_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[MEMBER_VAL]], i32 0, i32 0 -// CHECK: store i32 42, i32* [[MEMBER_VAL_VAL]] -// CHECK: [[TEMP_MEMBER:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[TEMP]], i32 0, i32 0 +// CHECK: [[TEMP2:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV +// CHECK: [[MEMBER_STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* %member to %struct.StructWithCopyConstructorAndValue* +// CHECK: call void @{{_ZN33StructWithCopyConstructorAndValueC(1|2)Ei|"\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z"}}(%struct.StructWithCopyConstructorAndValue* noalias [[MEMBER_STRUCT]], i32 42) +// CHECK: [[TEMP_AS_STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* [[TEMP]] to %struct.StructWithCopyConstructorAndValue* +// CHECK: [[OBJ_AS_STRUCT:%.*]] = bitcast %TSo037StructWithCopyConstructorAndSubobjectcdE5ValueV* [[OBJ]] to %struct.StructWithCopyConstructorAndSubobjectCopyConstructorAndValue* +// CHECK: call void @{{_ZN60StructWithCopyConstructorAndSubobjectCopyConstructorAndValueC(1|2)E33StructWithCopyConstructorAndValue|"\?\?0StructWithCopyConstructorAndSubobjectCopyConstructorAndValue@@QEAA@UStructWithCopyConstructorAndValue@@@Z"}}(%struct.StructWithCopyConstructorAndSubobjectCopyConstructorAndValue* noalias [[OBJ_AS_STRUCT]], %struct.StructWithCopyConstructorAndValue* [[TEMP_AS_STRUCT]]) +// CHECK: [[TEMP_MEMBER:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[TEMP2]], i32 0, i32 0 // CHECK: [[TEMP_MEMBER_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[TEMP_MEMBER]], i32 0, i32 0 // CHECK: [[LHS:%.*]] = load i32, i32* [[TEMP_MEMBER_VAL]] // CHECK: [[OUT:%.*]] = icmp eq i32 [[LHS]], 42 // CHECK: ret i1 [[OUT]] public func testStructWithCopyConstructorAndSubobjectCopyConstructorAndValue() -> Bool { - let member = StructWithCopyConstructorAndValue(value: 42) + let member = StructWithCopyConstructorAndValue(42) let obj = StructWithCopyConstructorAndSubobjectCopyConstructorAndValue( - member: member + member ) return obj.member.value == 42 } diff --git a/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift b/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift index 87766b5a01bdb..88a7253e17f19 100644 --- a/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift +++ b/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift @@ -7,59 +7,37 @@ import TypeClassification // CHECK-LABEL: sil [ossa] @$s4main24testStructWithDestructoryyF // CHECK: [[AS:%.*]] = alloc_stack $StructWithDestructor // CHECK: [[META:%.*]] = metatype $@thin StructWithDestructor.Type -// CHECK: [[FN:%.*]] = function_ref @$sSo20StructWithDestructorVABycfC : $@convention(method) (@thin StructWithDestructor.Type) -> @out StructWithDestructor -// CHECK: apply [[FN]]([[AS]], [[META]]) : $@convention(method) (@thin StructWithDestructor.Type) -> @out StructWithDestructor +// CHECK: [[FN:%.*]] = function_ref @{{_ZN20StructWithDestructorC1Ev|\?\?0StructWithDestructor@@QEAA@XZ}} : $@convention(c) (@thin StructWithDestructor.Type) -> @out StructWithDestructor +// CHECK: apply [[FN]]([[AS]], [[META]]) : $@convention(c) (@thin StructWithDestructor.Type) -> @out StructWithDestructor // CHECK: destroy_addr [[AS]] // CHECK: dealloc_stack %0 : $*StructWithDestructor // CHECK-LABEL: end sil function '$s4main24testStructWithDestructoryyF' + +// CHECK-LABEL: sil [clang StructWithDestructor.init] @{{_ZN20StructWithDestructorC1Ev|\?\?0StructWithDestructor@@QEAA@XZ}} : $@convention(c) (@thin StructWithDestructor.Type) -> @out StructWithDestructor public func testStructWithDestructor() { let d = StructWithDestructor() } -// StructWithDestructor.init() -// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo20StructWithDestructorVABycfC : $@convention(method) (@thin StructWithDestructor.Type) -> @out StructWithDestructor -// CHECK: [[BOX:%.*]] = alloc_box ${ var StructWithDestructor } -// CHECK: [[UBOX:%.*]] = mark_uninitialized [rootself] [[BOX]] : ${ var StructWithDestructor } -// CHECK: [[BOX_ADDR:%.*]] = project_box [[UBOX]] : ${ var StructWithDestructor }, 0 -// CHECK: [[SA:%.*]] = alloc_stack $StructWithDestructor -// CHECK: builtin "zeroInitializer"([[SA]] : $*StructWithDestructor) : $() -// CHECK: [[BA:%.*]] = begin_access [modify] [unknown] [[BOX_ADDR]] : $*StructWithDestructor -// CHECK: copy_addr [take] [[SA]] to [[BA]] : $*StructWithDestructor -// CHECK: copy_addr [[BOX_ADDR]] to [initialization] %0 : $*StructWithDestructor -// CHECK: destroy_value [[UBOX]] : ${ var StructWithDestructor } -// CHECK-LABEL: end sil function '$sSo20StructWithDestructorVABycfC' - // Make sure that "HasMemberWithDestructor" is marked as non-trivial by checking // for a "destroy_addr". -// CHECK-LABEL: sil [ossa] @$s4main33testStructWithSubobjectDestructoryyF +// CHECK-LABEL: sil [ossa] @$s4main33testStructWithSubobjectDestructoryyF : $@convention(thin) () -> () // CHECK: [[AS:%.*]] = alloc_stack $StructWithSubobjectDestructor // CHECK: [[META:%.*]] = metatype $@thin StructWithSubobjectDestructor.Type -// CHECK: [[FN:%.*]] = function_ref @$sSo29StructWithSubobjectDestructorVABycfC : $@convention(method) (@thin StructWithSubobjectDestructor.Type) -> @out StructWithSubobjectDestructor -// CHECK: apply [[FN]]([[AS]], [[META]]) : $@convention(method) (@thin StructWithSubobjectDestructor.Type) -> @out StructWithSubobjectDestructor +// CHECK: [[FN:%.*]] = function_ref @{{_ZN29StructWithSubobjectDestructorC1Ev|\?\?1StructWithSubobjectDestructor@@QEAA@XZ}} : $@convention(c) (@thin StructWithSubobjectDestructor.Type) -> @out StructWithSubobjectDestructor +// CHECK: apply [[FN]]([[AS]], [[META]]) : $@convention(c) (@thin StructWithSubobjectDestructor.Type) -> @out StructWithSubobjectDestructor // CHECK: destroy_addr [[AS]] // CHECK-LABEL: end sil function '$s4main33testStructWithSubobjectDestructoryyF' + +// CHECK-LABEL: sil [clang StructWithSubobjectDestructor.init] @{{_ZN29StructWithSubobjectDestructorC1Ev|\?\?1StructWithSubobjectDestructor@@QEAA@XZ}} : $@convention(c) (@thin StructWithSubobjectDestructor.Type) -> @out StructWithSubobjectDestructor public func testStructWithSubobjectDestructor() { let d = StructWithSubobjectDestructor() } -// StructWithSubobjectDestructor.init() -// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo29StructWithSubobjectDestructorVABycfC : $@convention(method) (@thin StructWithSubobjectDestructor.Type) -> @out StructWithSubobjectDestructor -// CHECK: [[BOX:%.*]] = alloc_box ${ var StructWithSubobjectDestructor } -// CHECK: [[UBOX:%.*]] = mark_uninitialized [rootself] [[BOX]] : ${ var StructWithSubobjectDestructor } -// CHECK: [[ADDR:%.*]] = project_box [[UBOX]] : ${ var StructWithSubobjectDestructor }, 0 -// CHECK: [[AS:%.*]] = alloc_stack $StructWithSubobjectDestructor -// CHECK: builtin "zeroInitializer"([[AS]] : $*StructWithSubobjectDestructor) : $() -// CHECK: [[BA:%.*]] = begin_access [modify] [unknown] [[ADDR]] : $*StructWithSubobjectDestructor -// CHECK: copy_addr [take] [[AS]] to [[BA]] : $*StructWithSubobjectDestructor -// CHECK: copy_addr [[ADDR]] to [initialization] %0 : $*StructWithSubobjectDestructor -// CHECK: destroy_value [[UBOX]] : ${ var StructWithSubobjectDestructor } -// CHECK-LABEL: end sil function '$sSo29StructWithSubobjectDestructorVABycfC' - // CHECK-LABLE: sil [ossa] @$s4main37testStructWithCopyConstructorAndValueSbyF // CHECK: [[AS:%.*]] = alloc_stack $StructWithCopyConstructorAndValue // CHECK: [[META:%.*]] = metatype $@thin StructWithCopyConstructorAndValue.Type -// CHECK: [[FN:%.*]] = function_ref @$sSo33StructWithCopyConstructorAndValueV5valueABs5Int32V_tcfC : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue -// CHECK: apply [[FN]]([[AS]], %{{.*}}, [[META]]) : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: [[FN:%.*]] = function_ref @{{_ZN33StructWithCopyConstructorAndValueC1Ei|\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z}} : $@convention(c) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: apply [[FN]]([[AS]], %{{.*}}, [[META]]) : $@convention(c) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue // CHECK: [[OBJ_VAL_ADDR:%.*]] = struct_element_addr [[AS]] : $*StructWithCopyConstructorAndValue, #StructWithCopyConstructorAndValue.value // CHECK: [[OBJ_VAL:%.*]] = load [trivial] [[OBJ_VAL_ADDR]] : $*Int32 // CHECK: [[IL_42:%.*]] = integer_literal $Builtin.IntLiteral, 42 @@ -70,22 +48,18 @@ public func testStructWithSubobjectDestructor() { // CHECK: destroy_addr [[AS]] : $*StructWithCopyConstructorAndValue // CHECK: return [[OUT]] : $Bool // CHECK-LABLE: end sil function '$s4main37testStructWithCopyConstructorAndValueSbyF' + +// CHECK-LABEL: sil [clang StructWithCopyConstructorAndValue.init] @{{_ZN33StructWithCopyConstructorAndValueC1Ei|\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z}} : $@convention(c) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue public func testStructWithCopyConstructorAndValue() -> Bool { - let obj = StructWithCopyConstructorAndValue(value: 42) + let obj = StructWithCopyConstructorAndValue(42) return obj.value == 42 } -// StructWithCopyConstructorAndValue.init(value:) -// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo33StructWithCopyConstructorAndValueV5valueABs5Int32V_tcfC : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue -// CHECK: [[VAL:%.*]] = struct_element_addr %0 : $*StructWithCopyConstructorAndValue, #StructWithCopyConstructorAndValue.value -// CHECK: store %1 to [trivial] [[VAL]] : $*Int32 -// CHECK-LABEL: end sil function '$sSo33StructWithCopyConstructorAndValueV5valueABs5Int32V_tcfC' - // CHECK-LABEL: sil [ossa] @$s4main46testStructWithSubobjectCopyConstructorAndValueSbyF : $@convention(thin) () -> Bool // CHECK: [[MEMBER_0:%.*]] = alloc_stack $StructWithCopyConstructorAndValue // CHECK: [[MEMBER_META:%.*]] = metatype $@thin StructWithCopyConstructorAndValue.Type -// CHECK: [[MAKE_MEMBER_FN:%.*]] = function_ref @$sSo33StructWithCopyConstructorAndValueV5valueABs5Int32V_tcfC : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue -// CHECK: apply [[MAKE_MEMBER_FN]]([[MEMBER_0]], %{{.*}}, [[MEMBER_META]]) : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: [[MAKE_MEMBER_FN:%.*]] = function_ref @{{_ZN33StructWithCopyConstructorAndValueC1Ei|\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z}} : $@convention(c) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: apply [[MAKE_MEMBER_FN]]([[MEMBER_0]], %{{.*}}, [[MEMBER_META]]) : $@convention(c) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue // CHECK: [[AS:%.*]] = alloc_stack $StructWithSubobjectCopyConstructorAndValue // CHECK: [[META:%.*]] = metatype $@thin StructWithSubobjectCopyConstructorAndValue.Type // CHECK: [[MEMBER_1:%.*]] = alloc_stack $StructWithCopyConstructorAndValue @@ -104,7 +78,7 @@ public func testStructWithCopyConstructorAndValue() -> Bool { // CHECK: return [[OUT]] : $Bool // CHECK-LABEL: end sil function '$s4main46testStructWithSubobjectCopyConstructorAndValueSbyF' public func testStructWithSubobjectCopyConstructorAndValue() -> Bool { - let member = StructWithCopyConstructorAndValue(value: 42) + let member = StructWithCopyConstructorAndValue(42) let obj = StructWithSubobjectCopyConstructorAndValue(member: member) return obj.member.value == 42 } @@ -119,14 +93,14 @@ public func testStructWithSubobjectCopyConstructorAndValue() -> Bool { // CHECK-LABEL: sil [ossa] @$s4main041testStructWithCopyConstructorAndSubobjectefG5ValueSbyF : $@convention(thin) () -> Bool // CHECK: [[MEMBER_0:%.*]] = alloc_stack $StructWithCopyConstructorAndValue // CHECK: [[META_MEMBER:%.*]] = metatype $@thin StructWithCopyConstructorAndValue.Type -// CHECK: [[CREATE_MEMBER_FN:%.*]] = function_ref @$sSo33StructWithCopyConstructorAndValueV5valueABs5Int32V_tcfC : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue -// CHECK: apply [[CREATE_MEMBER_FN]]([[MEMBER_0]], %{{.*}}, [[META_MEMBER]]) : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: [[CREATE_MEMBER_FN:%.*]] = function_ref @{{_ZN33StructWithCopyConstructorAndValueC1Ei|\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z}} : $@convention(c) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: apply [[CREATE_MEMBER_FN]]([[MEMBER_0]], %{{.*}}, [[META_MEMBER]]) : $@convention(c) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue // CHECK: [[AS:%.*]] = alloc_stack $StructWithCopyConstructorAndSubobjectCopyConstructorAndValue // CHECK: [[META:%.*]] = metatype $@thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type // CHECK: [[MEMBER_1:%.*]] = alloc_stack $StructWithCopyConstructorAndValue // CHECK: copy_addr [[MEMBER_0]] to [initialization] [[MEMBER_1]] : $*StructWithCopyConstructorAndValue -// CHECK: [[FN:%.*]] = function_ref @$sSo037StructWithCopyConstructorAndSubobjectcdE5ValueV6memberABSo0abcdeG0V_tcfC : $@convention(method) (@in StructWithCopyConstructorAndValue, @thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue -// CHECK: apply [[FN]]([[AS]], [[MEMBER_1]], [[META]]) : $@convention(method) (@in StructWithCopyConstructorAndValue, @thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue +// CHECK: [[FN:%.*]] = function_ref @{{_ZN60StructWithCopyConstructorAndSubobjectCopyConstructorAndValueC1E33StructWithCopyConstructorAndValue|\?\?0StructWithCopyConstructorAndSubobjectCopyConstructorAndValue@@QEAA@UStructWithCopyConstructorAndValue@@@Z}} : $@convention(c) (@in StructWithCopyConstructorAndValue, @thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue +// CHECK: apply [[FN]]([[AS]], [[MEMBER_1]], [[META]]) : $@convention(c) (@in StructWithCopyConstructorAndValue, @thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue // CHECK: [[OBJ_MEMBER_ADDR:%.*]] = struct_element_addr [[AS]] : $*StructWithCopyConstructorAndSubobjectCopyConstructorAndValue, #StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.member // CHECK: [[MEMBER_2:%.*]] = alloc_stack $StructWithCopyConstructorAndValue // CHECK: copy_addr [[OBJ_MEMBER_ADDR]] to [initialization] [[MEMBER_2]] : $*StructWithCopyConstructorAndValue @@ -138,19 +112,13 @@ public func testStructWithSubobjectCopyConstructorAndValue() -> Bool { // CHECK-LABEL: end sil function '$s4main041testStructWithCopyConstructorAndSubobjectefG5ValueSbyF' public func testStructWithCopyConstructorAndSubobjectCopyConstructorAndValue() -> Bool { - let member = StructWithCopyConstructorAndValue(value: 42) + let member = StructWithCopyConstructorAndValue(42) let obj = StructWithCopyConstructorAndSubobjectCopyConstructorAndValue( - member: member + member ) return obj.member.value == 42 } -// StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.init(member:) -// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo037StructWithCopyConstructorAndSubobjectcdE5ValueV6memberABSo0abcdeG0V_tcfC : $@convention(method) (@in StructWithCopyConstructorAndValue, @thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue -// CHECK: [[MEMBER:%.*]] = struct_element_addr %0 : $*StructWithCopyConstructorAndSubobjectCopyConstructorAndValue, #StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.member -// CHECK: copy_addr [take] %1 to [initialization] [[MEMBER]] : $*StructWithCopyConstructorAndValue -// CHECK-LABEL: end sil function '$sSo037StructWithCopyConstructorAndSubobjectcdE5ValueV6memberABSo0abcdeG0V_tcfC' - // CHECK-LABEL: sil [ossa] @$s4main4test3objSbSo33StructWithCopyConstructorAndValueV_tF : $@convention(thin) (@in_guaranteed StructWithCopyConstructorAndValue) -> Bool // CHECK: [[META_1:%.*]] = metatype $@thin Int32.Type // CHECK: [[OBJ_VAL_ADDR:%.*]] = struct_element_addr %0 : $*StructWithCopyConstructorAndValue, #StructWithCopyConstructorAndValue.value diff --git a/test/Interop/Cxx/class/type-classification-non-trivial.swift b/test/Interop/Cxx/class/type-classification-non-trivial.swift index 489e6f71c7a40..910651f7b5cb5 100644 --- a/test/Interop/Cxx/class/type-classification-non-trivial.swift +++ b/test/Interop/Cxx/class/type-classification-non-trivial.swift @@ -11,12 +11,12 @@ import StdlibUnittest var AddressOnlyTestSuite = TestSuite("Address Only Types") AddressOnlyTestSuite.test("Test struct with copy constructor") { - let obj = StructWithCopyConstructorAndValue(value: 42) + let obj = StructWithCopyConstructorAndValue(42) expectEqual(obj.value, 42) } AddressOnlyTestSuite.test("Test struct with member with copy constructor") { - let member = StructWithCopyConstructorAndValue(value: 42) + let member = StructWithCopyConstructorAndValue(42) let obj = StructWithSubobjectCopyConstructorAndValue(member: member) expectEqual(obj.member.value, 42) } @@ -24,9 +24,9 @@ AddressOnlyTestSuite.test("Test struct with member with copy constructor") { AddressOnlyTestSuite.test( "Test struct with copy constructor and member with copy constructor" ) { - let member = StructWithCopyConstructorAndValue(value: 42) + let member = StructWithCopyConstructorAndValue(42) let obj = StructWithCopyConstructorAndSubobjectCopyConstructorAndValue( - member: member + member ) expectEqual(obj.member.value, 42) } diff --git a/test/Interop/Cxx/templates/canonical-types-module-interface.swift b/test/Interop/Cxx/templates/canonical-types-module-interface.swift index 3a90d1d44311a..735e2dafe5257 100644 --- a/test/Interop/Cxx/templates/canonical-types-module-interface.swift +++ b/test/Interop/Cxx/templates/canonical-types-module-interface.swift @@ -1,15 +1,16 @@ // RUN: %target-swift-ide-test -print-module -module-to-print=CanonicalTypes -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s // CHECK: struct __CxxTemplateInst12MagicWrapperI10IntWrapperE { -// CHECK: var t: IntWrapper -// CHECK: init() -// CHECK: init(t: IntWrapper) -// CHECK: mutating func getValuePlusArg(_ arg: Int32) -> Int32 -// CHECK: } -// CHECK: struct IntWrapper { -// CHECK: var value: Int32 -// CHECK: init() -// CHECK: mutating func getValue() -> Int32 -// CHECK: } -// CHECK: typealias WrappedMagicNumberA = __CxxTemplateInst12MagicWrapperI10IntWrapperE -// CHECK: typealias WrappedMagicNumberB = __CxxTemplateInst12MagicWrapperI10IntWrapperE +// CHECK-NEXT: var t: IntWrapper +// CHECK-NEXT: init() +// CHECK-NEXT: init(t: IntWrapper) +// CHECK-NEXT: mutating func getValuePlusArg(_ arg: Int32) -> Int32 +// CHECK-NEXT: } +// CHECK-NEXT: struct IntWrapper { +// CHECK-NEXT: var value: Int32 +// CHECK-NEXT: init() +// CHECK-NEXT: init(value: Int32) +// CHECK-NEXT: mutating func getValue() -> Int32 +// CHECK-NEXT: } +// CHECK-NEXT: typealias WrappedMagicNumberA = __CxxTemplateInst12MagicWrapperI10IntWrapperE +// CHECK-NEXT: typealias WrappedMagicNumberB = __CxxTemplateInst12MagicWrapperI10IntWrapperE diff --git a/test/Interop/Cxx/templates/not-pre-defined-class-template-module-interface.swift b/test/Interop/Cxx/templates/not-pre-defined-class-template-module-interface.swift index 7fd6b7cc0efc1..0f2b7f7b57c6e 100644 --- a/test/Interop/Cxx/templates/not-pre-defined-class-template-module-interface.swift +++ b/test/Interop/Cxx/templates/not-pre-defined-class-template-module-interface.swift @@ -1,15 +1,15 @@ // RUN: %target-swift-ide-test -print-module -module-to-print=NotPreDefinedClassTemplate -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s // CHECK: struct __CxxTemplateInst12MagicWrapperI10IntWrapperE { -// CHECK: var t: IntWrapper -// CHECK: init() -// CHECK: init(t: IntWrapper) -// CHECK: mutating func getValuePlusArg(_ arg: Int32) -> Int32 -// CHECK: } -// CHECK: struct IntWrapper { -// CHECK: var value: Int32 -// CHECK: init() -// CHECK: init(value: Int32) -// CHECK: mutating func getValue() -> Int32 -// CHECK: } -// CHECK: typealias MagicallyWrappedIntWithoutDefinition = __CxxTemplateInst12MagicWrapperI10IntWrapperE +// CHECK-NEXT: var t: IntWrapper +// CHECK-NEXT: init() +// CHECK-NEXT: init(t: IntWrapper) +// CHECK-NEXT: mutating func getValuePlusArg(_ arg: Int32) -> Int32 +// CHECK-NEXT: } +// CHECK-NEXT: struct IntWrapper { +// CHECK-NEXT: var value: Int32 +// CHECK-NEXT: init() +// CHECK-NEXT: init(value: Int32) +// CHECK-NEXT: mutating func getValue() -> Int32 +// CHECK-NEXT: } +// CHECK-NEXT: typealias MagicallyWrappedIntWithoutDefinition = __CxxTemplateInst12MagicWrapperI10IntWrapperE diff --git a/test/Interop/Cxx/templates/using-directive-module-interface.swift b/test/Interop/Cxx/templates/using-directive-module-interface.swift index 81b445d21e3d7..cee692e4dc45c 100644 --- a/test/Interop/Cxx/templates/using-directive-module-interface.swift +++ b/test/Interop/Cxx/templates/using-directive-module-interface.swift @@ -1,15 +1,15 @@ // RUN: %target-swift-ide-test -print-module -module-to-print=UsingDirective -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s // CHECK: struct __CxxTemplateInst12MagicWrapperI10IntWrapperE { -// CHECK: var t: IntWrapper -// CHECK: init() -// CHECK: init(t: IntWrapper) -// CHECK: mutating func getValuePlusArg(_ arg: Int32) -> Int32 -// CHECK: } -// CHECK: struct IntWrapper { -// CHECK: var value: Int32 -// CHECK: init() -// CHECK: init(value: Int32) -// CHECK: mutating func getValue() -> Int32 -// CHECK: } -// CHECK: typealias UsingWrappedMagicNumber = __CxxTemplateInst12MagicWrapperI10IntWrapperE +// CHECK-NEXT: var t: IntWrapper +// CHECK-NEXT: init() +// CHECK-NEXT: init(t: IntWrapper) +// CHECK-NEXT: mutating func getValuePlusArg(_ arg: Int32) -> Int32 +// CHECK-NEXT: } +// CHECK-NEXT: struct IntWrapper { +// CHECK-NEXT: var value: Int32 +// CHECK-NEXT: init() +// CHECK-NEXT: init(value: Int32) +// CHECK-NEXT: mutating func getValue() -> Int32 +// CHECK-NEXT: } +// CHECK-NEXT: typealias UsingWrappedMagicNumber = __CxxTemplateInst12MagicWrapperI10IntWrapperE From 7399c20fbf7809a274d9b6819bfd6fe708d73582 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Thu, 24 Sep 2020 11:48:46 -0700 Subject: [PATCH 344/745] [cxx-interop] Revert changes to ClangTypeConverter::visitMetatypeType. The old implementation should be sufficient. The C++ constructors we import should get their types from the imported decl, not a double conversion to a SILFunction type and then back again. --- lib/AST/ClangTypeConverter.cpp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index d2316dd7415ea..9fd13f797798d 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -461,19 +461,7 @@ clang::QualType ClangTypeConverter::visitProtocolType(ProtocolType *type) { // Metatypes can be converted to Class when they are metatypes for concrete // classes. https://github.com/apple/swift/pull/27479#discussion_r344418131 clang::QualType ClangTypeConverter::visitMetatypeType(MetatypeType *type) { - assert(type->hasRepresentation() && - "metatype should have been assigned a representation"); - switch (type->getRepresentation()) { - case MetatypeRepresentation::Thin: - return ClangASTContext.VoidTy; - - case MetatypeRepresentation::Thick: - llvm_unreachable("thick metatypes don't have a corresponding Clang type"); - - case MetatypeRepresentation::ObjC: - return getClangMetatypeType(ClangASTContext); - } - llvm_unreachable("bad representation"); + return getClangMetatypeType(ClangASTContext); } // TODO: [stronger-checking-in-clang-type-conversion] From 9e2d03e0a8a32f55c0c956e56ce63ae5d4a2bfba Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 29 Sep 2020 16:44:00 -0700 Subject: [PATCH 345/745] [cxx-interop] Skip void types in emitCXXConstructorThunkIfNeeded. emitCXXConstructorThunkIfNeeded receives a SIL function that may contain metatypes. If so, they will be converted to void and we need to skip them when emitting the thunk's args. --- lib/IRGen/GenDecl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index e8aff92311183..a59533d2d4c6a 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -2812,6 +2812,10 @@ emitCXXConstructorThunkIfNeeded(IRGenModule &IGM, SILFunction *initializer, for (auto i = thunk->arg_begin(), e = thunk->arg_end(); i != e; ++i) { auto *argTy = i->getType(); auto *paramTy = ctorFnType->getParamType(i - thunk->arg_begin()); + // Thin metatypes are represented as "void". If we run across one of + // thesse, skip it. + if (paramTy == IGM.VoidTy) + continue; if (paramTy != argTy) Args.push_back(subIGF.coerceValue(i, paramTy, IGM.DataLayout)); else From 7635443915bbedec5adc55dd3325e5a223cc31a4 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sat, 3 Oct 2020 13:44:07 -0700 Subject: [PATCH 346/745] [cxx-interop] Update ABIArgInfo::Indirect case in expandExternalSignatureTypes to handle thin metatype parameters. Add second list of SILParameterInfos that excludes thin metatypes so that the "Indirect" argument path below will select the correct parameter info. --- lib/IRGen/GenCall.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 0166efb11799b..677e6de51d8bd 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -1315,6 +1315,17 @@ void SignatureExpansion::expandExternalSignatureTypes() { // Swift parameters list? size_t clangToSwiftParamOffset = paramTys.size(); + // This is exactly the same as "params" but without thin metatypes. This must + // exist so that when the path for "Indirect" arguments looks for the + // SILParameterInfo for a given index, we get the parameter info for the + // coorisponding clang type's index and not a "random" parameter's info. + // + // This is only an issue in very rare cases when we have a constructor that + // looks like this: (T, @thin U.Type) -> @out U. In this case "params" will + // contain two elements and "paramTys" will contain two elements so the + // "Indirect" argument path gets confused and selects the second parameter + // (the thin metatype) instead of the first one. + SmallVector adjustedSILParams; // Convert each parameter to a Clang type. for (auto param : params) { auto clangTy = IGM.getClangType(param, FnType); @@ -1324,6 +1335,7 @@ void SignatureExpansion::expandExternalSignatureTypes() { continue; } paramTys.push_back(clangTy); + adjustedSILParams.push_back(param); } // Generate function info for this signature. @@ -1413,7 +1425,7 @@ void SignatureExpansion::expandExternalSignatureTypes() { case clang::CodeGen::ABIArgInfo::Indirect: { assert(i >= clangToSwiftParamOffset && "Unexpected index for indirect byval argument"); - auto ¶m = params[i - clangToSwiftParamOffset]; + auto ¶m = adjustedSILParams[i - clangToSwiftParamOffset]; auto paramTy = getSILFuncConventions().getSILType( param, IGM.getMaximalTypeExpansionContext()); auto ¶mTI = cast(IGM.getTypeInfo(paramTy)); From f04de9f12867bc388411cd8a4abf4028bf77db01 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 4 Oct 2020 14:38:04 -0700 Subject: [PATCH 347/745] [cxx-interop] Skip metatypes when lowering C++ constructor SIL function type. When lowering a C++ constructor's function type to a SIL function type, skip over the "self" metatype parameter. --- lib/IRGen/GenCall.cpp | 19 +--------- lib/IRGen/GenClangType.cpp | 14 +------- lib/IRGen/GenDecl.cpp | 4 --- lib/SIL/IR/SILFunctionType.cpp | 12 ++++++- lib/SILGen/SILGenApply.cpp | 6 ++++ .../Cxx/class/constructors-objc-silgen.swift | 5 ++- .../Cxx/class/constructors-silgen.swift | 5 ++- .../synthesized-initializers-silgen.swift | 14 ++++---- ...pe-classification-non-trivial-silgen.swift | 36 ++++++++----------- 9 files changed, 44 insertions(+), 71 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 677e6de51d8bd..dd8b6bab50a7a 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -1315,27 +1315,10 @@ void SignatureExpansion::expandExternalSignatureTypes() { // Swift parameters list? size_t clangToSwiftParamOffset = paramTys.size(); - // This is exactly the same as "params" but without thin metatypes. This must - // exist so that when the path for "Indirect" arguments looks for the - // SILParameterInfo for a given index, we get the parameter info for the - // coorisponding clang type's index and not a "random" parameter's info. - // - // This is only an issue in very rare cases when we have a constructor that - // looks like this: (T, @thin U.Type) -> @out U. In this case "params" will - // contain two elements and "paramTys" will contain two elements so the - // "Indirect" argument path gets confused and selects the second parameter - // (the thin metatype) instead of the first one. - SmallVector adjustedSILParams; // Convert each parameter to a Clang type. for (auto param : params) { auto clangTy = IGM.getClangType(param, FnType); - // If a parameter type is lowered to void, this means it should be ignored. - // For example, this happens for thin metatypes. - if (clangTy->isVoidType()) { - continue; - } paramTys.push_back(clangTy); - adjustedSILParams.push_back(param); } // Generate function info for this signature. @@ -1425,7 +1408,7 @@ void SignatureExpansion::expandExternalSignatureTypes() { case clang::CodeGen::ABIArgInfo::Indirect: { assert(i >= clangToSwiftParamOffset && "Unexpected index for indirect byval argument"); - auto ¶m = adjustedSILParams[i - clangToSwiftParamOffset]; + auto ¶m = params[i - clangToSwiftParamOffset]; auto paramTy = getSILFuncConventions().getSILType( param, IGM.getMaximalTypeExpansionContext()); auto ¶mTI = cast(IGM.getTypeInfo(paramTy)); diff --git a/lib/IRGen/GenClangType.cpp b/lib/IRGen/GenClangType.cpp index c8f38d6038a65..8bc35dd99c134 100644 --- a/lib/IRGen/GenClangType.cpp +++ b/lib/IRGen/GenClangType.cpp @@ -403,19 +403,7 @@ clang::CanQualType GenClangType::visitProtocolType(CanProtocolType type) { } clang::CanQualType GenClangType::visitMetatypeType(CanMetatypeType type) { - assert(type->hasRepresentation() && - "metatype should have been assigned a representation by SIL"); - switch (type->getRepresentation()) { - case MetatypeRepresentation::Thin: - return getClangASTContext().VoidTy; - - case MetatypeRepresentation::Thick: - llvm_unreachable("thick metatypes don't have a corresponding Clang type"); - - case MetatypeRepresentation::ObjC: - return getClangMetatypeType(getClangASTContext()); - } - llvm_unreachable("bad representation"); + return getClangMetatypeType(getClangASTContext()); } clang::CanQualType diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index a59533d2d4c6a..e8aff92311183 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -2812,10 +2812,6 @@ emitCXXConstructorThunkIfNeeded(IRGenModule &IGM, SILFunction *initializer, for (auto i = thunk->arg_begin(), e = thunk->arg_end(); i != e; ++i) { auto *argTy = i->getType(); auto *paramTy = ctorFnType->getParamType(i - thunk->arg_begin()); - // Thin metatypes are represented as "void". If we run across one of - // thesse, skip it. - if (paramTy == IGM.VoidTy) - continue; if (paramTy != argTy) Args.push_back(subIGF.coerceValue(i, paramTy, IGM.DataLayout)); else diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index ff07a080d7c4f..b0d52ef84d46f 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -4222,7 +4222,17 @@ TypeConverter::getLoweredFormalTypes(SILDeclRef constant, if (innerExtInfo.isAsync()) extInfo = extInfo.withAsync(true); - bridgedParams.push_back(selfParam); + // If this is a C++ constructor, don't add the metatype "self" parameter + // because we'll never use it and it will cause problems in IRGen. + if (constant.getDecl()->getClangDecl() && + isa(constant.getDecl()->getClangDecl())) { + // But, make sure it is actually a metatype that we're not adding. If + // changes to the self parameter are made in the future, this logic may + // need to be updated. + assert(selfParam.getParameterType()->is()); + } else { + bridgedParams.push_back(selfParam); + } auto uncurried = CanAnyFunctionType::get(genericSig, diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 65c44598af668..bb4a47b7d1f95 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -38,6 +38,7 @@ #include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/SILArgument.h" #include "llvm/Support/Compiler.h" +#include "clang/AST/DeclCXX.h" using namespace swift; using namespace Lowering; @@ -559,6 +560,11 @@ class Callee { result.foreignError = func->getForeignErrorConvention(); result.foreignSelf = func->getImportAsMemberStatus(); + // Remove the metatype "self" parameter by making this a static member. + if (constant->getDecl()->getClangDecl() && + isa(constant->getDecl()->getClangDecl())) + result.foreignSelf.setStatic(); + return result; } diff --git a/test/Interop/Cxx/class/constructors-objc-silgen.swift b/test/Interop/Cxx/class/constructors-objc-silgen.swift index c7199ee889811..6e1b907d2b472 100644 --- a/test/Interop/Cxx/class/constructors-objc-silgen.swift +++ b/test/Interop/Cxx/class/constructors-objc-silgen.swift @@ -5,8 +5,7 @@ import ConstructorsObjC // CHECK: [[VAR:%[0-9]+]] = alloc_stack $ConstructorWithNSArrayParam -// CHECK: [[TYPE:%[0-9]+]] = metatype $@thin ConstructorWithNSArrayParam.Type // CHECK: [[OPT_ARRAY:%[0-9]+]] = enum $Optional, #Optional.some!enumelt, %{{[0-9]+}} : $NSArray -// CHECK: [[FUNC:%[0-9]+]] = function_ref @_ZN27ConstructorWithNSArrayParamC1EP7NSArray : $@convention(c) (Optional, @thin ConstructorWithNSArrayParam.Type) -> @out ConstructorWithNSArrayParam -// CHECK: %{{[0-9]+}} = apply [[FUNC]]([[VAR]], [[OPT_ARRAY]], [[TYPE]]) : $@convention(c) (Optional, @thin ConstructorWithNSArrayParam.Type) -> @out ConstructorWithNSArrayParam +// CHECK: [[FUNC:%[0-9]+]] = function_ref @_ZN27ConstructorWithNSArrayParamC1EP7NSArray : $@convention(c) (Optional) -> @out ConstructorWithNSArrayParam +// CHECK: %{{[0-9]+}} = apply [[FUNC]]([[VAR]], [[OPT_ARRAY]]) : $@convention(c) (Optional) -> @out ConstructorWithNSArrayParam let _ = ConstructorWithNSArrayParam([]) diff --git a/test/Interop/Cxx/class/constructors-silgen.swift b/test/Interop/Cxx/class/constructors-silgen.swift index def974a1e9a3d..9472ff3cc08aa 100644 --- a/test/Interop/Cxx/class/constructors-silgen.swift +++ b/test/Interop/Cxx/class/constructors-silgen.swift @@ -5,9 +5,8 @@ import Constructors // The most important thing to test here is that the constructor result is // returned with an @out attribute. // CHECK: [[VAR:%[0-9]+]] = alloc_stack $ConstructorWithParam -// CHECK: [[TYPE:%[0-9]+]] = metatype $@thin ConstructorWithParam.Type // CHECK: [[LITERAL:%[0-9]+]] = integer_literal $Builtin.Int32, 42 // CHECK: [[INT:%[0-9]+]] = struct $Int32 ([[LITERAL]] : $Builtin.Int32) -// CHECK: [[FUNC:%[0-9]+]] = function_ref @{{_ZN20ConstructorWithParamC1Ei|\?\?0ConstructorWithParam@@QEAA@H@Z}} : $@convention(c) (Int32, @thin ConstructorWithParam.Type) -> @out ConstructorWithParam -// CHECK: %{{[0-9]+}} = apply [[FUNC]]([[VAR]], [[INT]], [[TYPE]]) : $@convention(c) (Int32, @thin ConstructorWithParam.Type) -> @out ConstructorWithParam +// CHECK: [[FUNC:%[0-9]+]] = function_ref @{{_ZN20ConstructorWithParamC1Ei|\?\?0ConstructorWithParam@@QEAA@H@Z}} : $@convention(c) (Int32) -> @out ConstructorWithParam +// CHECK: %{{[0-9]+}} = apply [[FUNC]]([[VAR]], [[INT]]) : $@convention(c) (Int32) -> @out ConstructorWithParam let _ = ConstructorWithParam(42) diff --git a/test/Interop/Cxx/class/synthesized-initializers-silgen.swift b/test/Interop/Cxx/class/synthesized-initializers-silgen.swift index 8b84f18bb1569..379873c341222 100644 --- a/test/Interop/Cxx/class/synthesized-initializers-silgen.swift +++ b/test/Interop/Cxx/class/synthesized-initializers-silgen.swift @@ -4,24 +4,22 @@ import SynthesizedInitializers // CHECK-LABEL: sil [ossa] @$s4main18emptyTypeNoArgInityyF : $@convention(thin) () -> () // CHECK: [[AS:%.*]] = alloc_stack $EmptyStruct -// CHECK: [[META:%.*]] = metatype $@thin EmptyStruct.Type -// CHECK: [[FN:%.*]] = function_ref @{{_ZN11EmptyStructC1Ev|\?\?0EmptyStruct@@QEAA@XZ}} : $@convention(c) (@thin EmptyStruct.Type) -> @out EmptyStruct -// CHECK: apply [[FN]]([[AS]], [[META]]) : $@convention(c) (@thin EmptyStruct.Type) -> @out EmptyStruct +// CHECK: [[FN:%.*]] = function_ref @{{_ZN11EmptyStructC1Ev|\?\?0EmptyStruct@@QEAA@XZ}} : $@convention(c) () -> @out EmptyStruct +// CHECK: apply [[FN]]([[AS]]) : $@convention(c) () -> @out EmptyStruct // CHECK-LABEL: end sil function '$s4main18emptyTypeNoArgInityyF' -// CHECL-LABEL: sil [clang EmptyStruct.init] @{{_ZN11EmptyStructC1Ev|\?\?0EmptyStruct@@QEAA@XZ}} : $@convention(c) (@thin EmptyStruct.Type) -> @out EmptyStruct +// CHECL-LABEL: sil [clang EmptyStruct.init] @{{_ZN11EmptyStructC1Ev|\?\?0EmptyStruct@@QEAA@XZ}} : $@convention(c) () -> @out EmptyStruct public func emptyTypeNoArgInit() { let e = EmptyStruct() } // CHECK-LABEL: sil [ossa] @$s4main25singleMemberTypeNoArgInityyF : $@convention(thin) () -> () // CHECK: [[AS:%.*]] = alloc_stack $IntBox -// CHECK: [[META:%.*]] = metatype $@thin IntBox.Type -// CHECK: [[FN:%.*]] = function_ref @{{_ZN6IntBoxC1Ev|\?\?0IntBox@@QEAA@XZ}} : $@convention(c) (@thin IntBox.Type) -> @out IntBox -// CHECK: apply [[FN]]([[AS]], [[META]]) : $@convention(c) (@thin IntBox.Type) -> @out IntBox +// CHECK: [[FN:%.*]] = function_ref @{{_ZN6IntBoxC1Ev|\?\?0IntBox@@QEAA@XZ}} : $@convention(c) () -> @out IntBox +// CHECK: apply [[FN]]([[AS]]) : $@convention(c) () -> @out IntBox // CHECK-LABEL: end sil function '$s4main25singleMemberTypeNoArgInityyF' -//CHECK-LABEL: sil [clang IntBox.init] @{{_ZN6IntBoxC1Ev|\?\?0IntBox@@QEAA@XZ}} : $@convention(c) (@thin IntBox.Type) -> @out IntBox +//CHECK-LABEL: sil [clang IntBox.init] @{{_ZN6IntBoxC1Ev|\?\?0IntBox@@QEAA@XZ}} : $@convention(c) () -> @out IntBox public func singleMemberTypeNoArgInit() { let i = IntBox() } diff --git a/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift b/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift index 88a7253e17f19..51f00d5268432 100644 --- a/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift +++ b/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift @@ -6,14 +6,13 @@ import TypeClassification // "destroy_addr". // CHECK-LABEL: sil [ossa] @$s4main24testStructWithDestructoryyF // CHECK: [[AS:%.*]] = alloc_stack $StructWithDestructor -// CHECK: [[META:%.*]] = metatype $@thin StructWithDestructor.Type -// CHECK: [[FN:%.*]] = function_ref @{{_ZN20StructWithDestructorC1Ev|\?\?0StructWithDestructor@@QEAA@XZ}} : $@convention(c) (@thin StructWithDestructor.Type) -> @out StructWithDestructor -// CHECK: apply [[FN]]([[AS]], [[META]]) : $@convention(c) (@thin StructWithDestructor.Type) -> @out StructWithDestructor +// CHECK: [[FN:%.*]] = function_ref @{{_ZN20StructWithDestructorC1Ev|\?\?0StructWithDestructor@@QEAA@XZ}} : $@convention(c) () -> @out StructWithDestructor +// CHECK: apply [[FN]]([[AS]]) : $@convention(c) () -> @out StructWithDestructor // CHECK: destroy_addr [[AS]] // CHECK: dealloc_stack %0 : $*StructWithDestructor // CHECK-LABEL: end sil function '$s4main24testStructWithDestructoryyF' -// CHECK-LABEL: sil [clang StructWithDestructor.init] @{{_ZN20StructWithDestructorC1Ev|\?\?0StructWithDestructor@@QEAA@XZ}} : $@convention(c) (@thin StructWithDestructor.Type) -> @out StructWithDestructor +// CHECK-LABEL: sil [clang StructWithDestructor.init] @{{_ZN20StructWithDestructorC1Ev|\?\?0StructWithDestructor@@QEAA@XZ}} : $@convention(c) () -> @out StructWithDestructor public func testStructWithDestructor() { let d = StructWithDestructor() } @@ -22,22 +21,20 @@ public func testStructWithDestructor() { // for a "destroy_addr". // CHECK-LABEL: sil [ossa] @$s4main33testStructWithSubobjectDestructoryyF : $@convention(thin) () -> () // CHECK: [[AS:%.*]] = alloc_stack $StructWithSubobjectDestructor -// CHECK: [[META:%.*]] = metatype $@thin StructWithSubobjectDestructor.Type -// CHECK: [[FN:%.*]] = function_ref @{{_ZN29StructWithSubobjectDestructorC1Ev|\?\?1StructWithSubobjectDestructor@@QEAA@XZ}} : $@convention(c) (@thin StructWithSubobjectDestructor.Type) -> @out StructWithSubobjectDestructor -// CHECK: apply [[FN]]([[AS]], [[META]]) : $@convention(c) (@thin StructWithSubobjectDestructor.Type) -> @out StructWithSubobjectDestructor +// CHECK: [[FN:%.*]] = function_ref @{{_ZN29StructWithSubobjectDestructorC1Ev|\?\?1StructWithSubobjectDestructor@@QEAA@XZ}} : $@convention(c) () -> @out StructWithSubobjectDestructor +// CHECK: apply [[FN]]([[AS]]) : $@convention(c) () -> @out StructWithSubobjectDestructor // CHECK: destroy_addr [[AS]] // CHECK-LABEL: end sil function '$s4main33testStructWithSubobjectDestructoryyF' -// CHECK-LABEL: sil [clang StructWithSubobjectDestructor.init] @{{_ZN29StructWithSubobjectDestructorC1Ev|\?\?1StructWithSubobjectDestructor@@QEAA@XZ}} : $@convention(c) (@thin StructWithSubobjectDestructor.Type) -> @out StructWithSubobjectDestructor +// CHECK-LABEL: sil [clang StructWithSubobjectDestructor.init] @{{_ZN29StructWithSubobjectDestructorC1Ev|\?\?1StructWithSubobjectDestructor@@QEAA@XZ}} : $@convention(c) () -> @out StructWithSubobjectDestructor public func testStructWithSubobjectDestructor() { let d = StructWithSubobjectDestructor() } // CHECK-LABLE: sil [ossa] @$s4main37testStructWithCopyConstructorAndValueSbyF // CHECK: [[AS:%.*]] = alloc_stack $StructWithCopyConstructorAndValue -// CHECK: [[META:%.*]] = metatype $@thin StructWithCopyConstructorAndValue.Type -// CHECK: [[FN:%.*]] = function_ref @{{_ZN33StructWithCopyConstructorAndValueC1Ei|\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z}} : $@convention(c) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue -// CHECK: apply [[FN]]([[AS]], %{{.*}}, [[META]]) : $@convention(c) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: [[FN:%.*]] = function_ref @{{_ZN33StructWithCopyConstructorAndValueC1Ei|\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z}} : $@convention(c) (Int32) -> @out StructWithCopyConstructorAndValue +// CHECK: apply [[FN]]([[AS]], %{{.*}}) : $@convention(c) (Int32) -> @out StructWithCopyConstructorAndValue // CHECK: [[OBJ_VAL_ADDR:%.*]] = struct_element_addr [[AS]] : $*StructWithCopyConstructorAndValue, #StructWithCopyConstructorAndValue.value // CHECK: [[OBJ_VAL:%.*]] = load [trivial] [[OBJ_VAL_ADDR]] : $*Int32 // CHECK: [[IL_42:%.*]] = integer_literal $Builtin.IntLiteral, 42 @@ -49,7 +46,7 @@ public func testStructWithSubobjectDestructor() { // CHECK: return [[OUT]] : $Bool // CHECK-LABLE: end sil function '$s4main37testStructWithCopyConstructorAndValueSbyF' -// CHECK-LABEL: sil [clang StructWithCopyConstructorAndValue.init] @{{_ZN33StructWithCopyConstructorAndValueC1Ei|\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z}} : $@convention(c) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK-LABEL: sil [clang StructWithCopyConstructorAndValue.init] @{{_ZN33StructWithCopyConstructorAndValueC1Ei|\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z}} : $@convention(c) (Int32) -> @out StructWithCopyConstructorAndValue public func testStructWithCopyConstructorAndValue() -> Bool { let obj = StructWithCopyConstructorAndValue(42) return obj.value == 42 @@ -57,9 +54,8 @@ public func testStructWithCopyConstructorAndValue() -> Bool { // CHECK-LABEL: sil [ossa] @$s4main46testStructWithSubobjectCopyConstructorAndValueSbyF : $@convention(thin) () -> Bool // CHECK: [[MEMBER_0:%.*]] = alloc_stack $StructWithCopyConstructorAndValue -// CHECK: [[MEMBER_META:%.*]] = metatype $@thin StructWithCopyConstructorAndValue.Type -// CHECK: [[MAKE_MEMBER_FN:%.*]] = function_ref @{{_ZN33StructWithCopyConstructorAndValueC1Ei|\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z}} : $@convention(c) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue -// CHECK: apply [[MAKE_MEMBER_FN]]([[MEMBER_0]], %{{.*}}, [[MEMBER_META]]) : $@convention(c) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: [[MAKE_MEMBER_FN:%.*]] = function_ref @{{_ZN33StructWithCopyConstructorAndValueC1Ei|\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z}} : $@convention(c) (Int32) -> @out StructWithCopyConstructorAndValue +// CHECK: apply [[MAKE_MEMBER_FN]]([[MEMBER_0]], %{{.*}}) : $@convention(c) (Int32) -> @out StructWithCopyConstructorAndValue // CHECK: [[AS:%.*]] = alloc_stack $StructWithSubobjectCopyConstructorAndValue // CHECK: [[META:%.*]] = metatype $@thin StructWithSubobjectCopyConstructorAndValue.Type // CHECK: [[MEMBER_1:%.*]] = alloc_stack $StructWithCopyConstructorAndValue @@ -92,15 +88,13 @@ public func testStructWithSubobjectCopyConstructorAndValue() -> Bool { // testStructWithCopyConstructorAndSubobjectCopyConstructorAndValue() // CHECK-LABEL: sil [ossa] @$s4main041testStructWithCopyConstructorAndSubobjectefG5ValueSbyF : $@convention(thin) () -> Bool // CHECK: [[MEMBER_0:%.*]] = alloc_stack $StructWithCopyConstructorAndValue -// CHECK: [[META_MEMBER:%.*]] = metatype $@thin StructWithCopyConstructorAndValue.Type -// CHECK: [[CREATE_MEMBER_FN:%.*]] = function_ref @{{_ZN33StructWithCopyConstructorAndValueC1Ei|\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z}} : $@convention(c) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue -// CHECK: apply [[CREATE_MEMBER_FN]]([[MEMBER_0]], %{{.*}}, [[META_MEMBER]]) : $@convention(c) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: [[CREATE_MEMBER_FN:%.*]] = function_ref @{{_ZN33StructWithCopyConstructorAndValueC1Ei|\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z}} : $@convention(c) (Int32) -> @out StructWithCopyConstructorAndValue +// CHECK: apply [[CREATE_MEMBER_FN]]([[MEMBER_0]], %{{.*}}) : $@convention(c) (Int32) -> @out StructWithCopyConstructorAndValue // CHECK: [[AS:%.*]] = alloc_stack $StructWithCopyConstructorAndSubobjectCopyConstructorAndValue -// CHECK: [[META:%.*]] = metatype $@thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type // CHECK: [[MEMBER_1:%.*]] = alloc_stack $StructWithCopyConstructorAndValue // CHECK: copy_addr [[MEMBER_0]] to [initialization] [[MEMBER_1]] : $*StructWithCopyConstructorAndValue -// CHECK: [[FN:%.*]] = function_ref @{{_ZN60StructWithCopyConstructorAndSubobjectCopyConstructorAndValueC1E33StructWithCopyConstructorAndValue|\?\?0StructWithCopyConstructorAndSubobjectCopyConstructorAndValue@@QEAA@UStructWithCopyConstructorAndValue@@@Z}} : $@convention(c) (@in StructWithCopyConstructorAndValue, @thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue -// CHECK: apply [[FN]]([[AS]], [[MEMBER_1]], [[META]]) : $@convention(c) (@in StructWithCopyConstructorAndValue, @thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue +// CHECK: [[FN:%.*]] = function_ref @{{_ZN60StructWithCopyConstructorAndSubobjectCopyConstructorAndValueC1E33StructWithCopyConstructorAndValue|\?\?0StructWithCopyConstructorAndSubobjectCopyConstructorAndValue@@QEAA@UStructWithCopyConstructorAndValue@@@Z}} : $@convention(c) (@in StructWithCopyConstructorAndValue) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue +// CHECK: apply [[FN]]([[AS]], [[MEMBER_1]]) : $@convention(c) (@in StructWithCopyConstructorAndValue) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue // CHECK: [[OBJ_MEMBER_ADDR:%.*]] = struct_element_addr [[AS]] : $*StructWithCopyConstructorAndSubobjectCopyConstructorAndValue, #StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.member // CHECK: [[MEMBER_2:%.*]] = alloc_stack $StructWithCopyConstructorAndValue // CHECK: copy_addr [[OBJ_MEMBER_ADDR]] to [initialization] [[MEMBER_2]] : $*StructWithCopyConstructorAndValue From 4364af39ee107bca52aefb07ef461d6d2d7e246d Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 4 Oct 2020 15:15:08 -0700 Subject: [PATCH 348/745] [cxx-interop] Small fixes and cleanup based on review. * Check isAggregate instead of hasUserDeclaredConstructor. * Rename addEmptyArgNamesForCxxFunc -> addEmptyArgNamesForClangFunction. * Other minor fixes and cleanups. --- lib/ClangImporter/ImportDecl.cpp | 6 ++---- lib/ClangImporter/ImportName.cpp | 14 ++++++-------- .../Cxx/class/constructors-module-interface.swift | 1 + .../Cxx/class/constructors-typechecker.swift | 2 +- .../class/synthesized-initializers-silgen.swift | 10 ++++++++++ 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 8b5fb67fab9da..e1712aa991ada 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3452,10 +3452,8 @@ namespace { ctors.push_back(createDefaultConstructor(Impl, result)); } - bool hasUserDeclaredConstructor = - cxxRecordDecl && cxxRecordDecl->hasUserDeclaredConstructor(); - if (hasReferenceableFields && hasMemberwiseInitializer && - !hasUserDeclaredConstructor) { + bool isAggregate = cxxRecordDecl && cxxRecordDecl->isAggregate(); + if (hasReferenceableFields && hasMemberwiseInitializer && isAggregate) { // The default zero initializer suppresses the implicit value // constructor that would normally be formed, so we have to add that // explicitly as well. diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 1a02a454ede0d..79fcd57ccc141 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -1367,11 +1367,10 @@ static bool suppressFactoryMethodAsInit(const clang::ObjCMethodDecl *method, } static void -addEmptyArgNamesForCxxFunc(const clang::FunctionDecl *funcDecl, - SmallVectorImpl &argumentNames) { - for (size_t i = 0; i < funcDecl->param_size(); ++i) { +addEmptyArgNamesForClangFunction(const clang::FunctionDecl *funcDecl, + SmallVectorImpl &argumentNames) { + for (size_t i = 0; i < funcDecl->param_size(); ++i) argumentNames.push_back(StringRef()); - } if (funcDecl->isVariadic()) argumentNames.push_back(StringRef()); } @@ -1614,9 +1613,8 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, isFunction = true; result.info.initKind = CtorInitializerKind::Designated; baseName = "init"; - if (auto ctor = dyn_cast(D)) { - addEmptyArgNamesForCxxFunc(ctor, argumentNames); - } + addEmptyArgNamesForClangFunction(cast(D), + argumentNames); break; case clang::DeclarationName::CXXConversionFunctionName: @@ -1691,7 +1689,7 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, if (auto function = dyn_cast(D)) { isFunction = true; - addEmptyArgNamesForCxxFunc(function, argumentNames); + addEmptyArgNamesForClangFunction(function, argumentNames); } break; diff --git a/test/Interop/Cxx/class/constructors-module-interface.swift b/test/Interop/Cxx/class/constructors-module-interface.swift index acc321c4bca4e..672c11692641c 100644 --- a/test/Interop/Cxx/class/constructors-module-interface.swift +++ b/test/Interop/Cxx/class/constructors-module-interface.swift @@ -16,6 +16,7 @@ // CHECK-NEXT: } // CHECK-NEXT: struct DefaultConstructorDeleted { // CHECK-NEXT: var a: UnsafeMutablePointer +// CHECK-NEXT: init(a: UnsafeMutablePointer) // CHECK-NEXT: } // CHECK-NEXT: struct ConstructorWithParam { // CHECK-NEXT: var x: Int32 diff --git a/test/Interop/Cxx/class/constructors-typechecker.swift b/test/Interop/Cxx/class/constructors-typechecker.swift index 3cc008807c6a1..c78328c2b39c3 100644 --- a/test/Interop/Cxx/class/constructors-typechecker.swift +++ b/test/Interop/Cxx/class/constructors-typechecker.swift @@ -8,6 +8,6 @@ let implicit = ImplicitDefaultConstructor() let deletedImplicitly = ConstructorWithParam() // expected-error {{missing argument for parameter #1 in call}} -let deletedExplicitly = DefaultConstructorDeleted() // expected-error {{cannot be constructed because it has no accessible initializers}} +let deletedExplicitly = DefaultConstructorDeleted() // expected-error {{missing argument for parameter 'a' in call}} let withArg = ConstructorWithParam(42) diff --git a/test/Interop/Cxx/class/synthesized-initializers-silgen.swift b/test/Interop/Cxx/class/synthesized-initializers-silgen.swift index 379873c341222..dac14a687a652 100644 --- a/test/Interop/Cxx/class/synthesized-initializers-silgen.swift +++ b/test/Interop/Cxx/class/synthesized-initializers-silgen.swift @@ -1,6 +1,7 @@ // RUN: %target-swift-frontend -I %S/Inputs -enable-cxx-interop -emit-silgen %s | %FileCheck %s import SynthesizedInitializers +import Constructors // CHECK-LABEL: sil [ossa] @$s4main18emptyTypeNoArgInityyF : $@convention(thin) () -> () // CHECK: [[AS:%.*]] = alloc_stack $EmptyStruct @@ -32,3 +33,12 @@ public func singleMemberTypeNoArgInit() { public func singleMemberTypeValueInit() { let i = IntBox(x: 42) } + +// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo25DefaultConstructorDeletedV1aABSpys5Int32VG_tcfC : $@convention(method) (UnsafeMutablePointer, @thin DefaultConstructorDeleted.Type) -> DefaultConstructorDeleted +// CHECK: bb0([[A:%.*]] : $UnsafeMutablePointer +// CHECK-NEXT: [[OUT:%.*]] = struct $DefaultConstructorDeleted ([[A]] : $UnsafeMutablePointer) +// CHECK-NEXT: return [[OUT]] : $DefaultConstructorDeleted +// CHECK-LABEL: end sil function '$sSo25DefaultConstructorDeletedV1aABSpys5Int32VG_tcfC' +public func deletedConstructor(a: UnsafeMutablePointer) { + let deletedExplicitly = DefaultConstructorDeleted(a: a) +} From 5774610eafd460023da4f0fac66442a4deceb023 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 4 Oct 2020 15:21:00 -0700 Subject: [PATCH 349/745] [cxx-interop] Fix patch formatting with clang-format. Fix all formatting of the changes made by this patch. --- lib/ClangImporter/ClangImporter.cpp | 4 ++-- lib/ClangImporter/ImportDecl.cpp | 11 +++++------ lib/IRGen/GenCall.cpp | 2 +- lib/SIL/IR/SILFunctionType.cpp | 6 +++--- lib/SILGen/SILGenApply.cpp | 4 ++-- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 370a3cdd1f91d..5628cfe170c5b 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3590,8 +3590,8 @@ void ClangImporter::getMangledName(raw_ostream &os, Impl.Mangler.reset(Impl.getClangASTContext().createMangleContext()); if (auto ctor = dyn_cast(clangDecl)) { - auto ctorGlobalDecl = clang::GlobalDecl(ctor, - clang::CXXCtorType::Ctor_Complete); + auto ctorGlobalDecl = + clang::GlobalDecl(ctor, clang::CXXCtorType::Ctor_Complete); Impl.Mangler->mangleCXXName(ctorGlobalDecl, os); } else { Impl.Mangler->mangleName(clangDecl, os); diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index e1712aa991ada..3e82783ef4b02 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3485,8 +3485,7 @@ namespace { result->setHasUnreferenceableStorage(hasUnreferenceableStorage); if (cxxRecordDecl) { - result->setIsCxxNonTrivial( - !cxxRecordDecl->isTriviallyCopyable()); + result->setIsCxxNonTrivial(!cxxRecordDecl->isTriviallyCopyable()); for (auto ctor : cxxRecordDecl->ctors()) { if (ctor->isCopyConstructor() && @@ -3925,10 +3924,10 @@ namespace { } else { auto resultTy = importedType.getType(); - FuncDecl *func = createFuncOrAccessor( - Impl.SwiftContext, loc, accessorInfo, name, - nameLoc, bodyParams, resultTy, - /*async*/ false, /*throws*/ false, dc, decl); + FuncDecl *func = + createFuncOrAccessor(Impl.SwiftContext, loc, accessorInfo, name, + nameLoc, bodyParams, resultTy, + /*async*/ false, /*throws*/ false, dc, decl); result = func; if (!dc->isModuleScopeContext()) { diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index dd8b6bab50a7a..60f4de3fccac7 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -2821,7 +2821,7 @@ static void externalizeArguments(IRGenFunction &IGF, const Callee &callee, == SILFunctionTypeRepresentation::Block) { // Ignore the physical block-object parameter. firstParam += 1; - // Or the indirect result parameter. + // Or the indirect result parameter. } else if (fnType->getNumResults() > 0 && fnType->getSingleResult().isFormalIndirect()) { // Ignore the indirect result parameter. diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index b0d52ef84d46f..f1037df2b1550 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2806,9 +2806,9 @@ class CXXMethodConventions : public CFunctionTypeConventions { // actual ABI doesn't match the assumed ABI, we try to get as close as // possible to make it easy for LLVM to optimize away the thunk. return ResultConvention::Indirect; - } - return CFunctionTypeConventions::getResult(resultTL); - } + } + return CFunctionTypeConventions::getResult(resultTL); + } static bool classof(const Conventions *C) { return C->getKind() == ConventionsKind::CXXMethod; } diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index bb4a47b7d1f95..70a2611f44e37 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -27,9 +27,9 @@ #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" -#include "swift/AST/ParameterList.h" #include "swift/AST/Module.h" #include "swift/AST/ModuleLoader.h" +#include "swift/AST/ParameterList.h" #include "swift/AST/SubstitutionMap.h" #include "swift/Basic/ExternalUnion.h" #include "swift/Basic/Range.h" @@ -37,8 +37,8 @@ #include "swift/Basic/Unicode.h" #include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/SILArgument.h" -#include "llvm/Support/Compiler.h" #include "clang/AST/DeclCXX.h" +#include "llvm/Support/Compiler.h" using namespace swift; using namespace Lowering; From 4099ddfdeb9e61a4d0f0a1c737f6f4c899a9bd53 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 9 Oct 2020 10:42:32 -0700 Subject: [PATCH 350/745] Ensure Changes to External Dependencies Cascade We cannot know a-priori how a change to an external dependency will affect the resulting structure of every file in the module that depends upon it. Therefore, we must be conservative and ensure that these dependent files always rebuild. Commit a test to this effect. --- .../Inputs/external-cascade/A.json | 11 ++++ .../Inputs/external-cascade/A.swift | 3 + .../Inputs/external-cascade/B.json | 11 ++++ .../Inputs/external-cascade/B.swift | 3 + .../Inputs/external-cascade/C.json | 11 ++++ .../Inputs/external-cascade/C.swift | 1 + .../Inputs/external-cascade/another-header.h | 1 + .../Inputs/external-cascade/bridging-header.h | 2 + .../CrossModule/external-cascade.swift | 65 +++++++++++++++++++ 9 files changed, 108 insertions(+) create mode 100644 test/Incremental/CrossModule/Inputs/external-cascade/A.json create mode 100644 test/Incremental/CrossModule/Inputs/external-cascade/A.swift create mode 100644 test/Incremental/CrossModule/Inputs/external-cascade/B.json create mode 100644 test/Incremental/CrossModule/Inputs/external-cascade/B.swift create mode 100644 test/Incremental/CrossModule/Inputs/external-cascade/C.json create mode 100644 test/Incremental/CrossModule/Inputs/external-cascade/C.swift create mode 100644 test/Incremental/CrossModule/Inputs/external-cascade/another-header.h create mode 100644 test/Incremental/CrossModule/Inputs/external-cascade/bridging-header.h create mode 100644 test/Incremental/CrossModule/external-cascade.swift diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/A.json b/test/Incremental/CrossModule/Inputs/external-cascade/A.json new file mode 100644 index 0000000000000..50005aae33f05 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/external-cascade/A.json @@ -0,0 +1,11 @@ +{ + "A.swift": { + "object": "./A.o", + "swift-dependencies": "./A.swiftdeps", + "swiftmodule": "./A~partial.swiftmodule", + "swiftdoc": "./A.swiftdoc", + }, + "": { + "swift-dependencies": "./A~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/A.swift b/test/Incremental/CrossModule/Inputs/external-cascade/A.swift new file mode 100644 index 0000000000000..24372998c4adf --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/external-cascade/A.swift @@ -0,0 +1,3 @@ +import B + +public let fromA = fromB diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/B.json b/test/Incremental/CrossModule/Inputs/external-cascade/B.json new file mode 100644 index 0000000000000..30c08e2ae8a00 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/external-cascade/B.json @@ -0,0 +1,11 @@ +{ + "B.swift": { + "object": "./B.o", + "swift-dependencies": "./B.swiftdeps", + "swiftmodule": "./B~partial.swiftmodule", + "swiftdoc": "./B.swiftdoc", + }, + "": { + "swift-dependencies": "./B~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/B.swift b/test/Incremental/CrossModule/Inputs/external-cascade/B.swift new file mode 100644 index 0000000000000..12f93a40df9a3 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/external-cascade/B.swift @@ -0,0 +1,3 @@ +import C + +public let fromB = fromC diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/C.json b/test/Incremental/CrossModule/Inputs/external-cascade/C.json new file mode 100644 index 0000000000000..34775828548e8 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/external-cascade/C.json @@ -0,0 +1,11 @@ +{ + "C.swift": { + "object": "./C.o", + "swift-dependencies": "./C.swiftdeps", + "swiftmodule": "./C~partial.swiftmodule", + "swiftdoc": "./C.swiftdoc", + }, + "": { + "swift-dependencies": "./C~buildrecord.swiftdeps" + } +} diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/C.swift b/test/Incremental/CrossModule/Inputs/external-cascade/C.swift new file mode 100644 index 0000000000000..e9ee5ef0dceef --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/external-cascade/C.swift @@ -0,0 +1 @@ +public let fromC = ASymbolFromAHeader + ASymbolFromAnotherHeader diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/another-header.h b/test/Incremental/CrossModule/Inputs/external-cascade/another-header.h new file mode 100644 index 0000000000000..b69f5a05a1182 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/external-cascade/another-header.h @@ -0,0 +1 @@ +int ASymbolFromAnotherHeader = 43; diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/bridging-header.h b/test/Incremental/CrossModule/Inputs/external-cascade/bridging-header.h new file mode 100644 index 0000000000000..94fc89f7c37b4 --- /dev/null +++ b/test/Incremental/CrossModule/Inputs/external-cascade/bridging-header.h @@ -0,0 +1,2 @@ +#include "another-header.h" +int ASymbolFromAHeader = 42; diff --git a/test/Incremental/CrossModule/external-cascade.swift b/test/Incremental/CrossModule/external-cascade.swift new file mode 100644 index 0000000000000..83407e1573124 --- /dev/null +++ b/test/Incremental/CrossModule/external-cascade.swift @@ -0,0 +1,65 @@ +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/external-cascade/* %t + +// rdar://problem/70012853 +// XFAIL: OS=windows-msvc + +// +// This test establishes a chain of modules that all depend on a set of +// bridging headers. This test ensures that changes to external dependencies - +// especially those that aren't directly imported - cause incremental rebuilds. +// +// |bridging-header.h| - Module C Module B Module A +// ^ -------- -> -------- -> -------- +// | | ^ +// | | | +// |another-header.h| ------------------------ + +// +// Set up a clean incremental build of all three modules +// + +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle %t/C.swift +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift + +// +// Now change a header and ensure that the rebuild cascades outwards +// + +// RUN: rm %t/another-header.h +// RUN: cp %S/Inputs/external-cascade/another-header.h %t/another-header.h +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s + +// MODULE-C: Job finished: {generate-pch: bridging-header-[[BRIDGING_HEADER:.*]].pch <= bridging-header.h} +// MODULE-C: Job finished: {compile: C.o <= C.swift bridging-header-[[BRIDGING_HEADER]].pch} +// MODULE-C: Job finished: {merge-module: C.swiftmodule <= C.o} + +// MODULE-B: Queuing because of external dependencies: {compile: B.o <= B.swift} +// MODULE-B: Job finished: {compile: B.o <= B.swift} +// MODULE-B: Job finished: {merge-module: B.swiftmodule <= B.o} + + +// MODULE-A: Queuing because of external dependencies: {compile: A.o <= A.swift} +// MODULE-A: Job finished: {compile: A.o <= A.swift} +// MODULE-A: Job finished: {merge-module: A.swiftmodule <= A.o} + +// +// And ensure that the null build really is null. +// + +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s +// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s + +// MODULE-C-NULL: Job finished: {generate-pch: bridging-header-[[BRIDGING_HEADER:.*]].pch <= bridging-header.h} +// MODULE-C-NULL: Job skipped: {compile: C.o <= C.swift bridging-header-[[BRIDGING_HEADER]].pch} +// MODULE-C-NULL: Job skipped: {merge-module: C.swiftmodule <= C.o} + +// MODULE-B-NULL: Job skipped: {compile: B.o <= B.swift} +// MODULE-B-NULL: Job skipped: {merge-module: B.swiftmodule <= B.o} + +// MODULE-A-NULL: Job skipped: {compile: A.o <= A.swift} +// MODULE-A-NULL: Job skipped: {merge-module: A.swiftmodule <= A.o} From c79d5cfbd6952c5dc141a24db10a87d548bc5f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Thu, 10 Sep 2020 20:52:03 -0700 Subject: [PATCH 351/745] [Sema] Still use the current minimum deployment target as fallback --- lib/Sema/TypeCheckAvailability.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 1f102fa89bb52..e0bd189ef7077 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -720,6 +720,14 @@ TypeChecker::overApproximateAvailabilityAtLocation(SourceLoc loc, } } + // If we still don't have an introduction version, use the current deployment + // target. This covers cases where an inlinable function and its parent + // contexts don't have explicit availability attributes. + if (!OverApproximateContext.getOSVersion().hasLowerEndpoint()) { + auto currentOS = AvailabilityContext::forDeploymentTarget(Context); + OverApproximateContext.constrainWith(currentOS); + } + return OverApproximateContext; } From 25c9ddd7c817d4dee5a1643cc0fc32f3cf833af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Thu, 10 Sep 2020 20:54:09 -0700 Subject: [PATCH 352/745] [Sema] Prioritize unavailability check for protocol witnesses The errors on explicit unavailability are more precise than those on availablity version. Make sure we prioritize the unavailability ones first. --- lib/Sema/TypeCheckProtocol.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index dc0c1c182f14a..72d0faaf2227c 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -1469,6 +1469,11 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement, return CheckKind::UsableFromInline; } + if (match.Witness->getAttrs().isUnavailable(getASTContext()) && + !requirement->getAttrs().isUnavailable(getASTContext())) { + return CheckKind::WitnessUnavailable; + } + auto requiredAvailability = AvailabilityContext::alwaysAvailable(); if (checkWitnessAvailability(requirement, match.Witness, &requiredAvailability)) { @@ -1498,11 +1503,6 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement, } } - if (match.Witness->getAttrs().isUnavailable(getASTContext()) && - !requirement->getAttrs().isUnavailable(getASTContext())) { - return CheckKind::WitnessUnavailable; - } - return CheckKind::Success; } From f536a581082633e33ea8849b7f1d2f9b0d816c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Wed, 16 Sep 2020 16:20:26 -0700 Subject: [PATCH 353/745] [Sema] Silence an inconsistent availability error for the stdlib Don't raise the error availability_decl_more_than_enclosing in the context is unavailable and if read in a swiftinterface file. This error was present in the stdlib in 5.3 without side effects so we still want to accept it. --- lib/Sema/TypeCheckAttr.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index ff1db56c28832..4342772a50201 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -1530,9 +1530,16 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *attr) { VersionRange::allGTE(attr->Introduced.getValue())}; if (!AttrRange.isContainedIn(EnclosingAnnotatedRange.getValue())) { - diagnose(attr->getLocation(), diag::availability_decl_more_than_enclosing); - diagnose(EnclosingDecl->getLoc(), - diag::availability_decl_more_than_enclosing_enclosing_here); + // Don't show this error in swiftinterfaces if it is about a context that + // is unavailable, this was in the stdlib at Swift 5.3. + SourceFile *Parent = D->getDeclContext()->getParentSourceFile(); + if (!Parent || Parent->Kind != SourceFileKind::Interface || + !EnclosingAnnotatedRange.getValue().isKnownUnreachable()) { + diagnose(attr->getLocation(), + diag::availability_decl_more_than_enclosing); + diagnose(EnclosingDecl->getLoc(), + diag::availability_decl_more_than_enclosing_enclosing_here); + } } } From 429017fc7acc303d70c6251eec7c59c97255fa96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Thu, 17 Sep 2020 12:30:53 -0700 Subject: [PATCH 354/745] [ClangImporter] Import unavailable decls as unavailable --- lib/ClangImporter/ImportDecl.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 079ded2e02094..81cb3ee453050 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -1817,18 +1817,27 @@ static void applyAvailableAttribute(Decl *decl, AvailabilityContext &info, if (info.isAlwaysAvailable()) return; + PlatformAgnosticAvailabilityKind platformAgnosticAvailability; + llvm::VersionTuple introducedVersion; + if (info.isKnownUnreachable()) { + platformAgnosticAvailability = PlatformAgnosticAvailabilityKind::Unavailable; + } else { + platformAgnosticAvailability = PlatformAgnosticAvailabilityKind::None; + introducedVersion = info.getOSVersion().getLowerEndpoint(); + } + llvm::VersionTuple noVersion; auto AvAttr = new (C) AvailableAttr(SourceLoc(), SourceRange(), targetPlatform(C.LangOpts), /*Message=*/StringRef(), /*Rename=*/StringRef(), - info.getOSVersion().getLowerEndpoint(), + introducedVersion, /*IntroducedRange*/SourceRange(), /*Deprecated=*/noVersion, /*DeprecatedRange*/SourceRange(), /*Obsoleted=*/noVersion, /*ObsoletedRange*/SourceRange(), - PlatformAgnosticAvailabilityKind::None, + platformAgnosticAvailability, /*Implicit=*/false); decl->getAttrs().add(AvAttr); From d2343f23a4d14b37f1f603ae13e3421fb5455df4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Mon, 21 Sep 2020 15:03:45 -0700 Subject: [PATCH 355/745] [Sema] Create a new TypeRefinementContext only for explicit inlinables --- lib/Sema/TypeCheckAvailability.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index e0bd189ef7077..890a036b9cc47 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -203,21 +203,27 @@ class TypeRefinementContextBuilder : private ASTWalker { return NewTRC; } - + /// Returns true if the declaration should introduce a new refinement context. bool declarationIntroducesNewContext(Decl *D) { if (!isa(D) && !isa(D)) { return false; } - + + // Explicit inlinability may to the decl being used on an earlier OS + // version when inlined on the client side. This check assumes that + // implicit decls are handled elsewhere. + bool isExplicitlyInlinable = !D->isImplicit() && + (D->getAttrs().hasAttribute() || + D->getAttrs().hasAttribute()); + // No need to introduce a context if the declaration does not have an - // availability or inlinable attribute. + // availability or non-implicit inlinable attribute. if (!hasActiveAvailableAttribute(D, Context) && - !D->getAttrs().hasAttribute() && - !D->getAttrs().hasAttribute()) { + !isExplicitlyInlinable) { return false; } - + // Only introduce for an AbstractStorageDecl if it is not local. // We introduce for the non-local case because these may // have getters and setters (and these may be synthesized, so they might @@ -228,7 +234,7 @@ class TypeRefinementContextBuilder : private ASTWalker { return false; } } - + return true; } From 1ca852e77a00bf8323494d7445d054ea28466b99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Thu, 17 Sep 2020 12:30:10 -0700 Subject: [PATCH 356/745] [Sema] Accept unavailable constructors override in unavailable types Preserve the old behavior of accepted unavailable override of constructors as this change would be source breaking. --- lib/Sema/TypeCheckDeclOverride.cpp | 12 +++++++++++- test/Sema/availability_versions.swift | 11 +++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index ca957db149544..d7b79aa88a07c 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1687,7 +1687,8 @@ static bool diagnoseOverrideForAvailability(ValueDecl *override, if (isRedundantAccessorOverrideAvailabilityDiagnostic(override, base)) return false; - auto &diags = override->getASTContext().Diags; + auto &ctx = override->getASTContext(); + auto &diags = ctx.Diags; if (auto *accessor = dyn_cast(override)) { diags.diagnose(override, diag::override_accessor_less_available, accessor->getDescriptiveKind(), @@ -1696,6 +1697,15 @@ static bool diagnoseOverrideForAvailability(ValueDecl *override, return true; } + // Don't report constructors that are marked unavailable as being less + // available than their introduction. This was previously allowed and + // can be used to forbid the direct use of a constructor in a subclass. + // Note that even when marked unavailable the constructor could be called + // by other inherited constructors. + if (isa(override) && + override->getAttrs().isUnavailable(ctx)) + return false; + diags.diagnose(override, diag::override_less_available, override->getBaseName()); diags.diagnose(base, diag::overridden_here); diff --git a/test/Sema/availability_versions.swift b/test/Sema/availability_versions.swift index ff86f938446ba..6ca4dada0de69 100644 --- a/test/Sema/availability_versions.swift +++ b/test/Sema/availability_versions.swift @@ -788,6 +788,9 @@ class UnavailableClassExtendingUnavailableClass : ClassAvailableOn10_51 { // Method availability is contravariant class SuperWithAlwaysAvailableMembers { + + required init() {} // expected-note {{overridden declaration is here}} + func shouldAlwaysBeAvailableMethod() { // expected-note 2 {{overridden declaration is here}} } @@ -808,6 +811,10 @@ class SuperWithAlwaysAvailableMembers { } class SubWithLimitedMemberAvailability : SuperWithAlwaysAvailableMembers { + + @available(OSX, introduced: 10.51) + required init() {} // expected-error {{overriding 'init' must be as available as declaration it overrides}} + @available(OSX, introduced: 10.51) override func shouldAlwaysBeAvailableMethod() { // expected-error {{overriding 'shouldAlwaysBeAvailableMethod' must be as available as declaration it overrides}} } @@ -833,6 +840,10 @@ class SubWithLimitedMemberAvailability : SuperWithAlwaysAvailableMembers { } class SubWithUnavailableMembers : SuperWithAlwaysAvailableMembers { + + @available(OSX, unavailable) + required init() {} + @available(OSX, unavailable) override func shouldAlwaysBeAvailableMethod() { // expected-error {{overriding 'shouldAlwaysBeAvailableMethod' must be as available as declaration it overrides}} } From fca7d36cb42b044b1179254d6e38f2b8a263eeed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Tue, 6 Oct 2020 14:15:32 -0700 Subject: [PATCH 357/745] [Sema] Use the deployment target as minimum OS version in swiftinterfaces Preserve the previous behavior when parsing swiftinterfaces as the Swift stdlib for Swift 5.3 and a few other swiftinterfaces in Xcode 12.0 had issues that would now be reported as errors. --- lib/Sema/TypeCheckAvailability.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 890a036b9cc47..e072bf594ba84 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -179,9 +179,13 @@ class TypeRefinementContextBuilder : private ASTWalker { // When the body is inlinable consider only the explicitly declared range // for checking availability. Otherwise, use the parent range which may // begin at the minimum deployment target. + // + // Also use the parent range when reading swiftinterfaces for + // retrocompatibility. bool isInlinable = D->getAttrs().hasAttribute() || D->getAttrs().hasAttribute(); - if (!isInlinable)) { + SourceFile *SF = D->getDeclContext()->getParentSourceFile(); + if (!isInlinable || (SF && SF->Kind == SourceFileKind::Interface)) { DeclInfo.intersectWith( getCurrentTRC()->getAvailabilityInfo()); } From aced5c74df5804b7c567a1ae9cf68b7e9cb336ee Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 6 Oct 2020 19:20:58 +0200 Subject: [PATCH 358/745] SILOptimizer: Remove InspectionMode from MemBehehaviorVisitor The InspectionMode was never set to anything else than "IgnoreRetains" --- .../SILOptimizer/Analysis/AliasAnalysis.h | 47 +++------------- lib/SILOptimizer/Analysis/MemoryBehavior.cpp | 40 +++++-------- .../UtilityPasses/MemBehaviorDumper.cpp | 4 +- test/SILOptimizer/mem-behavior-all.sil | 56 +++++++++---------- test/SILOptimizer/mem-behavior.sil | 24 ++++---- 5 files changed, 63 insertions(+), 108 deletions(-) diff --git a/include/swift/SILOptimizer/Analysis/AliasAnalysis.h b/include/swift/SILOptimizer/Analysis/AliasAnalysis.h index 6a1a38c70a77f..a1794a3f01ca5 100644 --- a/include/swift/SILOptimizer/Analysis/AliasAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/AliasAnalysis.h @@ -47,7 +47,6 @@ namespace { struct MemBehaviorKeyTy { // The SILValue pair: size_t V1, V2; - RetainObserveKind InspectionMode; }; } @@ -201,24 +200,16 @@ class AliasAnalysis : public SILAnalysis { /// Use the alias analysis to determine the memory behavior of Inst with /// respect to V. - /// - /// TODO: When ref count behavior is separated from generic memory behavior, - /// the InspectionMode flag will be unnecessary. - MemoryBehavior computeMemoryBehavior(SILInstruction *Inst, SILValue V, - RetainObserveKind); + MemoryBehavior computeMemoryBehavior(SILInstruction *Inst, SILValue V); /// Use the alias analysis to determine the memory behavior of Inst with /// respect to V. - /// - /// TODO: When ref count behavior is separated from generic memory behavior, - /// the InspectionMode flag will be unnecessary. - MemoryBehavior computeMemoryBehaviorInner(SILInstruction *Inst, SILValue V, - RetainObserveKind); + MemoryBehavior computeMemoryBehaviorInner(SILInstruction *Inst, SILValue V); /// Returns true if \p Inst may read from memory in a manner that /// affects V. bool mayReadFromMemory(SILInstruction *Inst, SILValue V) { - auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::IgnoreRetains); + auto B = computeMemoryBehavior(Inst, V); return B == MemoryBehavior::MayRead || B == MemoryBehavior::MayReadWrite || B == MemoryBehavior::MayHaveSideEffects; @@ -227,7 +218,7 @@ class AliasAnalysis : public SILAnalysis { /// Returns true if \p Inst may write to memory in a manner that /// affects V. bool mayWriteToMemory(SILInstruction *Inst, SILValue V) { - auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::IgnoreRetains); + auto B = computeMemoryBehavior(Inst, V); return B == MemoryBehavior::MayWrite || B == MemoryBehavior::MayReadWrite || B == MemoryBehavior::MayHaveSideEffects; @@ -236,26 +227,10 @@ class AliasAnalysis : public SILAnalysis { /// Returns true if \p Inst may read or write to memory in a manner that /// affects V. bool mayReadOrWriteMemory(SILInstruction *Inst, SILValue V) { - auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::IgnoreRetains); + auto B = computeMemoryBehavior(Inst, V); return MemoryBehavior::None != B; } - /// Returns true if Inst may have side effects in a manner that affects V. - bool mayHaveSideEffects(SILInstruction *Inst, SILValue V) { - auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::ObserveRetains); - return B == MemoryBehavior::MayWrite || - B == MemoryBehavior::MayReadWrite || - B == MemoryBehavior::MayHaveSideEffects; - } - - /// Returns true if Inst may have side effects in a manner that affects - /// V. This is independent of whether or not Inst may write to V and is meant - /// to encode notions such as ref count modifications. - bool mayHavePureSideEffects(SILInstruction *Inst, SILValue V) { - auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::ObserveRetains); - return MemoryBehavior::MayHaveSideEffects == B; - } - /// Returns true if \p Ptr may be released in the function call \p FAS. bool canApplyDecrementRefCount(FullApplySite FAS, SILValue Ptr); @@ -268,8 +243,7 @@ class AliasAnalysis : public SILAnalysis { AliasKeyTy toAliasKey(SILValue V1, SILValue V2, SILType Type1, SILType Type2); /// Encodes the memory behavior query as a MemBehaviorKeyTy. - MemBehaviorKeyTy toMemoryBehaviorKey(SILInstruction *V1, SILValue V2, - RetainObserveKind K); + MemBehaviorKeyTy toMemoryBehaviorKey(SILInstruction *V1, SILValue V2); virtual void invalidate() override { AliasCache.clear(); @@ -330,24 +304,21 @@ namespace llvm { template <> struct DenseMapInfo { static inline MemBehaviorKeyTy getEmptyKey() { auto Allone = std::numeric_limits::max(); - return {0, Allone, RetainObserveKind::RetainObserveKindEnd}; + return {0, Allone}; } static inline MemBehaviorKeyTy getTombstoneKey() { auto Allone = std::numeric_limits::max(); - return {Allone, 0, RetainObserveKind::RetainObserveKindEnd}; + return {Allone, 0}; } static unsigned getHashValue(const MemBehaviorKeyTy V) { unsigned H = 0; H ^= DenseMapInfo::getHashValue(V.V1); H ^= DenseMapInfo::getHashValue(V.V2); - H ^= DenseMapInfo::getHashValue(static_cast(V.InspectionMode)); return H; } static bool isEqual(const MemBehaviorKeyTy LHS, const MemBehaviorKeyTy RHS) { - return LHS.V1 == RHS.V1 && - LHS.V2 == RHS.V2 && - LHS.InspectionMode == RHS.InspectionMode; + return LHS.V1 == RHS.V1 && LHS.V2 == RHS.V2; } }; } diff --git a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp index 62b15e917b1d3..fa7a1929da056 100644 --- a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp +++ b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp @@ -66,15 +66,10 @@ class MemoryBehaviorVisitor /// The SILType of the value. Optional TypedAccessTy; - /// Should we treat instructions that increment ref counts as None instead of - /// MayHaveSideEffects. - RetainObserveKind InspectionMode; - public: MemoryBehaviorVisitor(AliasAnalysis *AA, SideEffectAnalysis *SEA, - EscapeAnalysis *EA, SILValue V, - RetainObserveKind IgnoreRefCountIncs) - : AA(AA), SEA(SEA), EA(EA), V(V), InspectionMode(IgnoreRefCountIncs) {} + EscapeAnalysis *EA, SILValue V) + : AA(AA), SEA(SEA), EA(EA), V(V) {} SILType getValueTBAAType() { if (!TypedAccessTy) @@ -223,9 +218,7 @@ class MemoryBehaviorVisitor // memory this will be unnecessary. #define REFCOUNTINC_MEMBEHAVIOR_INST(Name) \ MemBehavior visit##Name(Name *I) { \ - if (InspectionMode == RetainObserveKind::IgnoreRetains) \ - return MemBehavior::None; \ - return I->getMemoryBehavior(); \ + return MemBehavior::None; \ } REFCOUNTINC_MEMBEHAVIOR_INST(StrongRetainInst) REFCOUNTINC_MEMBEHAVIOR_INST(RetainValueInst) @@ -364,19 +357,15 @@ MemBehavior MemoryBehaviorVisitor::visitApplyInst(ApplyInst *AI) { // one the parameters in the function call is @in_guaranteed of V, ie. the // callee isn't allowed to modify it. Behavior = MemBehavior::MayRead; - } else if (ApplyEffects.mayReadRC() || - (InspectionMode == RetainObserveKind::ObserveRetains && - ApplyEffects.mayAllocObjects())) { - Behavior = MemBehavior::MayHaveSideEffects; } else { auto &GlobalEffects = ApplyEffects.getGlobalEffects(); - Behavior = GlobalEffects.getMemBehavior(InspectionMode); + Behavior = GlobalEffects.getMemBehavior(RetainObserveKind::IgnoreRetains); // Check all parameter effects. for (unsigned Idx = 0, End = AI->getNumArguments(); Idx < End && Behavior < MemBehavior::MayHaveSideEffects; ++Idx) { auto &ArgEffect = ApplyEffects.getParameterEffects()[Idx]; - auto ArgBehavior = ArgEffect.getMemBehavior(InspectionMode); + auto ArgBehavior = ArgEffect.getMemBehavior(RetainObserveKind::IgnoreRetains); if (ArgEffect.mayRelease()) { Behavior = MemBehavior::MayHaveSideEffects; break; @@ -442,9 +431,8 @@ visitBeginCOWMutationInst(BeginCOWMutationInst *BCMI) { //===----------------------------------------------------------------------===// MemBehavior -AliasAnalysis::computeMemoryBehavior(SILInstruction *Inst, SILValue V, - RetainObserveKind InspectionMode) { - MemBehaviorKeyTy Key = toMemoryBehaviorKey(Inst, V, InspectionMode); +AliasAnalysis::computeMemoryBehavior(SILInstruction *Inst, SILValue V) { + MemBehaviorKeyTy Key = toMemoryBehaviorKey(Inst, V); // Check if we've already computed this result. auto It = MemoryBehaviorCache.find(Key); if (It != MemoryBehaviorCache.end()) { @@ -457,27 +445,25 @@ AliasAnalysis::computeMemoryBehavior(SILInstruction *Inst, SILValue V, MemoryBehaviorNodeToIndex.clear(); // Key is no longer valid as we cleared the MemoryBehaviorNodeToIndex. - Key = toMemoryBehaviorKey(Inst, V, InspectionMode); + Key = toMemoryBehaviorKey(Inst, V); } // Calculate the aliasing result and store it in the cache. - auto Result = computeMemoryBehaviorInner(Inst, V, InspectionMode); + auto Result = computeMemoryBehaviorInner(Inst, V); MemoryBehaviorCache[Key] = Result; return Result; } MemBehavior -AliasAnalysis::computeMemoryBehaviorInner(SILInstruction *Inst, SILValue V, - RetainObserveKind InspectionMode) { +AliasAnalysis::computeMemoryBehaviorInner(SILInstruction *Inst, SILValue V) { LLVM_DEBUG(llvm::dbgs() << "GET MEMORY BEHAVIOR FOR:\n " << *Inst << " " << *V); assert(SEA && "SideEffectsAnalysis must be initialized!"); - return MemoryBehaviorVisitor(this, SEA, EA, V, InspectionMode).visit(Inst); + return MemoryBehaviorVisitor(this, SEA, EA, V).visit(Inst); } MemBehaviorKeyTy AliasAnalysis::toMemoryBehaviorKey(SILInstruction *V1, - SILValue V2, - RetainObserveKind M) { + SILValue V2) { size_t idx1 = MemoryBehaviorNodeToIndex.getIndex(V1->getRepresentativeSILNodeInObject()); assert(idx1 != std::numeric_limits::max() && @@ -486,5 +472,5 @@ MemBehaviorKeyTy AliasAnalysis::toMemoryBehaviorKey(SILInstruction *V1, V2->getRepresentativeSILNodeInObject()); assert(idx2 != std::numeric_limits::max() && "~0 index reserved for empty/tombstone keys"); - return {idx1, idx2, M}; + return {idx1, idx2}; } diff --git a/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp b/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp index a9fa20e226ecc..d3927f0451c45 100644 --- a/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp @@ -88,11 +88,9 @@ class MemBehaviorDumper : public SILModuleTransform { bool Read = AA->mayReadFromMemory(&I, V); bool Write = AA->mayWriteToMemory(&I, V); - bool SideEffects = AA->mayHaveSideEffects(&I, V); llvm::outs() << "PAIR #" << PairCount++ << ".\n" << " " << I << " " << V - << " r=" << Read << ",w=" << Write - << ",se=" << SideEffects << "\n"; + << " r=" << Read << ",w=" << Write << "\n"; } } } diff --git a/test/SILOptimizer/mem-behavior-all.sil b/test/SILOptimizer/mem-behavior-all.sil index fb101d2b3224d..59b5de05d3132 100644 --- a/test/SILOptimizer/mem-behavior-all.sil +++ b/test/SILOptimizer/mem-behavior-all.sil @@ -22,17 +22,17 @@ class Parent { // CHECK: PAIR #25. // CHECK-NEXT: %6 = begin_access [modify] [static] %1 : $*Builtin.Int32 // CHECK-NEXT: %10 = ref_element_addr %9 : $C, #C.prop -// CHECK-NEXT: r=0,w=0,se=0 +// CHECK-NEXT: r=0,w=0 // // Any unknown instructions with side effects does affect the let-load. // CHECK: PAIR #83. // CHECK-NEXT: end_borrow %{{.*}} : $C // CHECK-NEXT: ref_element_addr %{{.*}} : $C, #C.prop -// CHECK-NEXT: r=1,w=1,se=1 +// CHECK-NEXT: r=1,w=1 // CHECK: PAIR #103. // CHECK-NEXT: destroy_value %0 : $Parent // CHECK-NEXT: ref_element_addr %{{.*}} : $C, #C.prop -// CHECK-NEXT: r=1,w=1,se=1 +// CHECK-NEXT: r=1,w=1 sil [ossa] @testLetSideEffects : $@convention(thin) (@owned Parent, @inout Builtin.Int32) -> Builtin.Int32 { bb0(%0 : @owned $Parent, %1 : $*Builtin.Int32): %borrow1 = begin_borrow %0 : $Parent @@ -60,47 +60,47 @@ bb0(%0 : @owned $Parent, %1 : $*Builtin.Int32): // CHECK: PAIR #0. // CHECK-NEXT: %{{.*}} = begin_access [read] [static] %0 : $*Builtin.Int32 // CHECK-NEXT: %0 = argument of bb0 : $*Builtin.Int32 -// CHECK-NEXT: r=1,w=0,se=0 +// CHECK-NEXT: r=1,w=0 // CHECK: PAIR #1. // CHECK-NEXT: %{{.*}} = begin_access [read] [static] %0 : $*Builtin.Int32 // CHECK-NEXT: load [trivial] %{{.*}} : $*Builtin.Int32 -// CHECK-NEXT: r=1,w=0,se=0 +// CHECK-NEXT: r=1,w=0 // CHECK: PAIR #3. // CHECK-NEXT: %{{.*}} = begin_access [read] [static] %0 : $*Builtin.Int32 // CHECK-NEXT: %{{.*}} = begin_access [modify] [static] %0 : $*Builtin.Int32 -// CHECK-NEXT: r=1,w=0,se=0 +// CHECK-NEXT: r=1,w=0 // CHECK: PAIR #8. // CHECK-NEXT: end_access %{{.*}} : $*Builtin.Int32 // CHECK-NEXT: %0 = argument of bb0 : $*Builtin.Int32 -// CHECK-NEXT: r=1,w=0,se=0 +// CHECK-NEXT: r=1,w=0 // CHECK: PAIR #9. // CHECK-NEXT: end_access %{{.*}} : $*Builtin.Int32 // CHECK-NEXT: %{{.*}} = begin_access [read] [static] %0 : $*Builtin.Int32 -// CHECK-NEXT: r=1,w=0,se=0 +// CHECK-NEXT: r=1,w=0 // CHECK: PAIR #12. // CHECK-NEXT: end_access %{{.*}} : $*Builtin.Int32 // CHECK-NEXT: %{{.*}} = begin_access [modify] [static] %0 : $*Builtin.Int32 -// CHECK-NEXT: r=1,w=0,se=0 +// CHECK-NEXT: r=1,w=0 // CHECK: PAIR #13. // CHECK-NEXT: %{{.*}} = begin_access [modify] [static] %0 : $*Builtin.Int32 // CHECK-NEXT: %0 = argument of bb0 : $*Builtin.Int32 -// CHECK-NEXT: r=0,w=1,se=1 +// CHECK-NEXT: r=0,w=1 // CHECK: PAIR #14. // CHECK-NEXT: %{{.*}} = begin_access [modify] [static] %0 : $*Builtin.Int32 // CHECK-NEXT: %{{.*}} = begin_access [read] [static] %0 : $*Builtin.Int32 -// CHECK-NEXT: r=0,w=1,se=1 +// CHECK-NEXT: r=0,w=1 // CHECK: PAIR #22. // CHECK-NEXT: end_access %{{.*}} : $*Builtin.Int32 // CHECK-NEXT: %0 = argument of bb0 : $*Builtin.Int32 -// CHECK-NEXT: r=0,w=1,se=1 +// CHECK-NEXT: r=0,w=1 // CHECK: PAIR #23. // CHECK-NEXT: end_access %{{.*}} : $*Builtin.Int32 // CHECK-NEXT: %{{.*}} = begin_access [read] [static] %0 : $*Builtin.Int32 -// CHECK-NEXT: r=0,w=1,se=1 +// CHECK-NEXT: r=0,w=1 // CHECK: PAIR #26. // CHECK-NEXT: end_access %{{.*}} : $*Builtin.Int32 // CHECK-NEXT: %{{.*}} = begin_access [modify] [static] %0 : $*Builtin.Int32 -// CHECK-NEXT: r=0,w=1,se=1 +// CHECK-NEXT: r=0,w=1 sil [ossa] @testReadWriteAccess : $@convention(thin) (@inout Builtin.Int32) -> Builtin.Int32 { bb0(%0 : $*Builtin.Int32): %read = begin_access [read] [static] %0 : $*Builtin.Int32 @@ -117,11 +117,11 @@ bb0(%0 : $*Builtin.Int32): // CHECK: PAIR #0. // CHECK-NEXT: %2 = load [take] %0 : $*C // CHECK-NEXT: %0 = argument of bb0 : $*C -// CHECK-NEXT: r=1,w=1,se=1 +// CHECK-NEXT: r=1,w=1 // CHECK: PAIR #1. // CHECK-NEXT: %2 = load [take] %0 : $*C // CHECK-NEXT: %1 = argument of bb0 : $*C -// CHECK-NEXT: r=0,w=0,se=0 +// CHECK-NEXT: r=0,w=0 sil [ossa] @testLoadTake : $@convention(thin) (@in C, @in_guaranteed C) -> @owned C { bb0(%0 : $*C, %1 : $*C): %2 = load [take] %0 : $*C @@ -139,11 +139,11 @@ sil_global hidden [let] @globalC : $C // CHECK: PAIR #5. // CHECK-NEXT: load %{{.*}} : $*C // CHECK-NEXT: begin_access [deinit] [static] %{{.*}} : $*Builtin.Int32 -// CHECK-NEXT: r=1,w=0,se=0 +// CHECK-NEXT: r=1,w=0 // CHECK: PAIR #16. // CHECK-NEXT: %6 = begin_access [deinit] [static] %5 : $*Builtin.Int32 // user: %7 // CHECK-NEXT: %2 = load %1 : $*C // user: %3 -// CHECK-NEXT: r=1,w=0,se=0 +// CHECK-NEXT: r=1,w=0 sil hidden @testDeinitInstVsNonAddressValue : $@convention(thin) (@guaranteed C) -> () { bb0(%0 : $C): %1 = global_addr @globalC : $*C @@ -169,7 +169,7 @@ struct Int64Wrapper { // CHECK: PAIR #2. // CHECK: %1 = begin_access [modify] [static] %0 : $*Int64Wrapper // users: %7, %2 // CHECK: %3 = begin_access [read] [static] %2 : $*Int64 // users: %6, %4 -// CHECK: r=0,w=1,se=1 +// CHECK: r=0,w=1 // CHECK: PAIR #3. sil @testNestedAccessWithInterposedProjection : $@convention(thin) (@inout Int64Wrapper) -> () { bb0(%0 : $*Int64Wrapper): @@ -188,15 +188,15 @@ bb0(%0 : $*Int64Wrapper): // CHECK: PAIR #0. // CHECK-NEXT: copy_addr %1 to [initialization] %0 : $*C // CHECK-NEXT: %0 = argument of bb0 : $*C -// CHECK-NEXT: r=0,w=1,se=1 +// CHECK-NEXT: r=0,w=1 // CHECK: PAIR #1. // CHECK-NEXT: copy_addr %1 to [initialization] %0 : $*C // CHECK-NEXT: %1 = argument of bb0 : $*C -// CHECK-NEXT: r=1,w=0,se=0 +// CHECK-NEXT: r=1,w=0 // CHECK: PAIR #2. // CHECK-NEXT: copy_addr %1 to [initialization] %0 : $*C // CHECK-NEXT: %2 = argument of bb0 : $*C -// CHECK-NEXT: r=0,w=0,se=0 +// CHECK-NEXT: r=0,w=0 sil @test_copy_addr_initialize : $@convention(thin) (@inout C, @inout C) -> @out C { bb0(%0 : $*C, %1 : $*C, %2: $*C): copy_addr %1 to [initialization] %0 : $*C @@ -208,15 +208,15 @@ bb0(%0 : $*C, %1 : $*C, %2: $*C): // CHECK: PAIR #0. // CHECK-NEXT: copy_addr %1 to %0 : $*C // CHECK-NEXT: %0 = argument of bb0 : $*C -// CHECK-NEXT: r=1,w=1,se=1 +// CHECK-NEXT: r=1,w=1 // CHECK: PAIR #1. // CHECK-NEXT: copy_addr %1 to %0 : $*C // CHECK-NEXT: %1 = argument of bb0 : $*C -// CHECK-NEXT: r=1,w=1,se=1 +// CHECK-NEXT: r=1,w=1 // CHECK: PAIR #2. // CHECK-NEXT: copy_addr %1 to %0 : $*C // CHECK-NEXT: %2 = argument of bb0 : $*C -// CHECK-NEXT: r=1,w=1,se=1 +// CHECK-NEXT: r=1,w=1 sil @test_copy_addr_assign : $@convention(thin) (@inout C, @inout C, @inout C) -> () { bb0(%0 : $*C, %1 : $*C, %2: $*C): copy_addr %1 to %0 : $*C @@ -228,15 +228,15 @@ bb0(%0 : $*C, %1 : $*C, %2: $*C): // CHECK: PAIR #0. // CHECK-NEXT: copy_addr [take] %1 to [initialization] %0 : $*C // CHECK-NEXT: %0 = argument of bb0 : $*C -// CHECK-NEXT: r=0,w=1,se=1 +// CHECK-NEXT: r=0,w=1 // CHECK: PAIR #1. // CHECK-NEXT: copy_addr [take] %1 to [initialization] %0 : $*C // CHECK-NEXT: %1 = argument of bb0 : $*C -// CHECK-NEXT: r=1,w=1,se=1 +// CHECK-NEXT: r=1,w=1 // CHECK: PAIR #2. // CHECK-NEXT: copy_addr [take] %1 to [initialization] %0 : $*C // CHECK-NEXT: %2 = argument of bb0 : $*C -// CHECK-NEXT: r=0,w=0,se=0 +// CHECK-NEXT: r=0,w=0 sil @test_copy_addr_take : $@convention(thin) (@in C, @inout C) -> @out C { bb0(%0 : $*C, %1 : $*C, %2: $*C): copy_addr [take] %1 to [initialization] %0 : $*C diff --git a/test/SILOptimizer/mem-behavior.sil b/test/SILOptimizer/mem-behavior.sil index 3d101dbbc3c81..13fa7aa1e023d 100644 --- a/test/SILOptimizer/mem-behavior.sil +++ b/test/SILOptimizer/mem-behavior.sil @@ -35,11 +35,11 @@ bb0(%0 : $X): // CHECK: PAIR #1. // CHECK-NEXT: %4 = apply %3(%0, %1) : $@convention(thin) (Int32, @in Int32) -> () // CHECK-NEXT: %1 = argument of bb0 : $*Int32{{.*}} // user: %4 -// CHECK-NEXT: r=1,w=1,se=1 +// CHECK-NEXT: r=1,w=1 // CHECK: PAIR #2. // CHECK-NEXT: %4 = apply %3(%0, %1) : $@convention(thin) (Int32, @in Int32) -> () // CHECK-NEXT: %2 = argument of bb0 : $*Int32 -// CHECK-NEXT: r=0,w=0,se=0 +// CHECK-NEXT: r=0,w=0 sil @call_unknown_func : $@convention(thin) (Int32, @in Int32, @in Int32) -> () { bb0(%0 : $Int32, %1 : $*Int32, %2 : $*Int32): %3 = function_ref @unknown_func : $@convention(thin) (Int32, @in Int32) -> () @@ -53,11 +53,11 @@ bb0(%0 : $Int32, %1 : $*Int32, %2 : $*Int32): // CHECK: PAIR #1. // CHECK-NEXT: %4 = apply %3(%0, %1) : $@convention(thin) (Int32, @inout Int32) -> () // CHECK-NEXT: %1 = argument of bb0 : $*Int32{{.*}} // user: %4 -// CHECK-NEXT: r=0,w=1,se=1 +// CHECK-NEXT: r=0,w=1 // CHECK: PAIR #2. // CHECK-NEXT: %4 = apply %3(%0, %1) : $@convention(thin) (Int32, @inout Int32) -> () // CHECK-NEXT: %2 = argument of bb0 : $*Int32 -// CHECK-NEXT: r=0,w=0,se=0 +// CHECK-NEXT: r=0,w=0 sil @call_store_to_int_not_aliased : $@convention(thin) (Int32, @inout Int32, @in Int32) -> () { bb0(%0 : $Int32, %1 : $*Int32, %2 : $*Int32): %3 = function_ref @store_to_int : $@convention(thin) (Int32, @inout Int32) -> () @@ -71,11 +71,11 @@ bb0(%0 : $Int32, %1 : $*Int32, %2 : $*Int32): // CHECK: PAIR #3. // CHECK-NEXT: %6 = apply %5(%0, %3) : $@convention(thin) (Int32, @inout Int32) -> () // CHECK-NEXT: %3 = ref_element_addr %1 : $X, #X.a{{.*}} // user: %6 -// CHECK-NEXT: r=0,w=1,se=1 +// CHECK-NEXT: r=0,w=1 // CHECK: PAIR #4. // CHECK-NEXT: %6 = apply %5(%0, %3) : $@convention(thin) (Int32, @inout Int32) -> () // CHECK-NEXT: %4 = ref_element_addr %2 : $X, #X.a -// CHECK-NEXT: r=0,w=1,se=1 +// CHECK-NEXT: r=0,w=1 sil @call_store_to_int_aliased : $@convention(thin) (Int32, @guaranteed X, @guaranteed X) -> () { bb0(%0 : $Int32, %1 : $X, %2 : $X): %3 = ref_element_addr %1 : $X, #X.a @@ -100,7 +100,7 @@ bb0(%0 : $X, %1 : $X): // CHECK: PAIR #1 // CHECK-NEXT: %3 = apply %2() : $@convention(thin) () -> () // CHECK-NEXT: %1 = alloc_stack $Int32{{.*}} // user: %5 -// CHECK-NEXT: r=0,w=0,se=0 +// CHECK-NEXT: r=0,w=0 sil @allocstack_apply_no_side_effect : $@convention(thin) (Int32) -> () { bb0(%0 : $Int32): %1 = alloc_stack $Int32 // user: %5 @@ -115,7 +115,7 @@ bb0(%0 : $Int32): // CHECK: PAIR #1. // CHECK-NEXT: %3 = apply %2(%0, %1) : $@convention(thin) (Int32, @inout Int32) -> () // CHECK-NEXT: %1 = alloc_stack $Int32 -// CHECK-NEXT: r=0,w=1,se=1 +// CHECK-NEXT: r=0,w=1 sil @allocstack_apply_side_effect : $@convention(thin) (Int32) -> () { bb0(%0 : $Int32): %1 = alloc_stack $Int32 // users: %3, %5 @@ -131,7 +131,7 @@ bb0(%0 : $Int32): // CHECK: PAIR #1. // CHECK-NEXT: %3 = apply %2() : $@convention(thin) () -> () // CHECK-NEXT: %1 = alloc_stack $Int32{{.*}} // users: %7, %5 -// CHECK-NEXT: r=0,w=0,se=0 +// CHECK-NEXT: r=0,w=0 sil @allocstack_apply_no_escaping : $@convention(thin) (Int32) -> () { bb0(%0 : $Int32): %1 = alloc_stack $Int32 // users: %3, %5 @@ -148,7 +148,7 @@ bb0(%0 : $Int32): // CHECK: PAIR #1. // CHECK-NEXT: %4 = apply %3(%1) : $@convention(thin) (@in X) -> () // CHECK-NEXT: %1 = alloc_stack $X{{.*}} // users: %5, %4, %2 -// CHECK-NEXT: r=1,w=0,se=0 +// CHECK-NEXT: r=1,w=0 sil @allocstack_apply_read_only : $@convention(thin) (X) -> () { bb0(%0 : $X): %1 = alloc_stack $X @@ -176,7 +176,7 @@ struct TwoInts { // CHECK: PAIR #0. // CHECK-NEXT: %4 = apply %3(%1, %2) : $@convention(thin) (@inout Int32, @inout Int32) -> () // CHECK-NEXT: %0 = alloc_stack $TwoInts{{.*}} // users: %5, %2, %1 -// CHECK-NEXT: r=1,w=1,se=1 +// CHECK-NEXT: r=1,w=1 sil @combination_of_read_and_write_effects : $@convention(thin) () -> () { bb0: %0 = alloc_stack $TwoInts @@ -201,7 +201,7 @@ bb0(%0 : $*Int32, %1 : $*Int32): // CHECK: PAIR #0. // CHECK-NEXT: %2 = apply %1(%0) : $@convention(thin) (@in_guaranteed Int) -> () // CHECK-NEXT: %0 = argument of bb0 : $*Int -// CHECK-NEXT: r=1,w=0,se=0 +// CHECK-NEXT: r=1,w=0 sil @test_in_guaranteed_only_fun_is_ro_sink : $@convention(thin) (Int) -> () sil @test_in_guaranteed_only_fun_is_ro_callee : $@convention(thin) (@in_guaranteed Int) -> () From d4a6bd39b686307028b850d25a2a2fbc337160a3 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 9 Oct 2020 18:59:03 +0200 Subject: [PATCH 359/745] SILOptimizer: improve MemBehavior for apply instructions. 1. Do a better alias analysis for "function-local" objects, like alloc_stack and inout parameters 2. Fully support try_apply and begin/end/abort_apply So far we fully relied on escape analysis. But escape analysis has some shortcomings with SIL address-types. Therefore, handle two common cases, alloc_stack and inout parameters, with alias analysis. This gives better results. The biggest change here is to do a quick check if the address escapes via an address_to_pointer instructions. --- .../Analysis/SideEffectAnalysis.h | 7 + lib/SILOptimizer/Analysis/MemoryBehavior.cpp | 169 +++++++++++++----- .../Analysis/SideEffectAnalysis.cpp | 28 +++ .../UtilityPasses/MemBehaviorDumper.cpp | 5 +- test/SILOptimizer/mem-behavior.sil | 166 ++++++++++++++++- .../specialize_opaque_type_archetypes.swift | 1 - 6 files changed, 324 insertions(+), 52 deletions(-) diff --git a/include/swift/SILOptimizer/Analysis/SideEffectAnalysis.h b/include/swift/SILOptimizer/Analysis/SideEffectAnalysis.h index f75e577b41052..db3eecb6c9245 100644 --- a/include/swift/SILOptimizer/Analysis/SideEffectAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/SideEffectAnalysis.h @@ -389,6 +389,13 @@ class FunctionSideEffects { /// instructions are considered as side effects. MemoryBehavior getMemBehavior(RetainObserveKind ScanKind) const; + /// Gets the memory behavior for an argument. + /// + /// This is derived from the combined argument and the global effects. + /// Also the argument type and convention are considered. + MemoryBehavior getArgumentBehavior(FullApplySite applySite, + unsigned argIdx); + /// Get the global effects for the function. These are effects which cannot /// be associated to a specific parameter, e.g. writes to global variables /// or writes to unknown pointers. diff --git a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp index fa7a1929da056..9d3d8548e0c44 100644 --- a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp +++ b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp @@ -172,6 +172,10 @@ class MemoryBehaviorVisitor MemBehavior visitCopyAddrInst(CopyAddrInst *CAI); MemBehavior visitApplyInst(ApplyInst *AI); MemBehavior visitTryApplyInst(TryApplyInst *AI); + MemBehavior visitBeginApplyInst(BeginApplyInst *AI); + MemBehavior visitEndApplyInst(EndApplyInst *EAI); + MemBehavior visitAbortApplyInst(AbortApplyInst *AAI); + MemBehavior getApplyBehavior(FullApplySite AS); MemBehavior visitBuiltinInst(BuiltinInst *BI); MemBehavior visitStrongReleaseInst(StrongReleaseInst *BI); MemBehavior visitReleaseValueInst(ReleaseValueInst *BI); @@ -326,70 +330,137 @@ MemBehavior MemoryBehaviorVisitor::visitBuiltinInst(BuiltinInst *BI) { } MemBehavior MemoryBehaviorVisitor::visitTryApplyInst(TryApplyInst *AI) { - MemBehavior Behavior = MemBehavior::MayHaveSideEffects; - // Ask escape analysis. - if (!EA->canEscapeTo(V, AI)) - Behavior = MemBehavior::None; - - // Otherwise be conservative and return that we may have side effects. - LLVM_DEBUG(llvm::dbgs() << " Found tryapply, returning " << Behavior <<'\n'); - return Behavior; + return getApplyBehavior(AI); } MemBehavior MemoryBehaviorVisitor::visitApplyInst(ApplyInst *AI) { + return getApplyBehavior(AI); +} - FunctionSideEffects ApplyEffects; - SEA->getCalleeEffects(ApplyEffects, AI); +MemBehavior MemoryBehaviorVisitor::visitBeginApplyInst(BeginApplyInst *AI) { + return getApplyBehavior(AI); +} + +MemBehavior MemoryBehaviorVisitor::visitEndApplyInst(EndApplyInst *EAI) { + return getApplyBehavior(EAI->getBeginApply()); +} + +MemBehavior MemoryBehaviorVisitor::visitAbortApplyInst(AbortApplyInst *AAI) { + return getApplyBehavior(AAI->getBeginApply()); +} + +/// Returns true if the \p address may have any users which let the address +/// escape in an unusual way, e.g. with an address_to_pointer instruction. +static bool hasEscapingUses(SILValue address, int &numChecks) { + for (Operand *use : address->getUses()) { + SILInstruction *user = use->getUser(); + + // Avoid quadratic complexity in corner cases. A limit of 24 is more than + // enough in most cases. + if (++numChecks > 24) + return true; + + switch (user->getKind()) { + case SILInstructionKind::DebugValueAddrInst: + case SILInstructionKind::FixLifetimeInst: + case SILInstructionKind::LoadInst: + case SILInstructionKind::StoreInst: + case SILInstructionKind::CopyAddrInst: + case SILInstructionKind::DestroyAddrInst: + case SILInstructionKind::DeallocStackInst: + // Those instructions have no result and cannot escape the address. + break; + case SILInstructionKind::ApplyInst: + case SILInstructionKind::TryApplyInst: + case SILInstructionKind::BeginApplyInst: + // Apply instructions can not let an address escape either. It's not + // possible that an address, passed as an indirect parameter, escapes + // the function in any way (which is not unsafe and undefined behavior). + break; + case SILInstructionKind::OpenExistentialAddrInst: + case SILInstructionKind::UncheckedTakeEnumDataAddrInst: + case SILInstructionKind::StructElementAddrInst: + case SILInstructionKind::TupleElementAddrInst: + case SILInstructionKind::UncheckedAddrCastInst: + // Check the uses of address projections. + if (hasEscapingUses(cast(user), numChecks)) + return true; + break; + case SILInstructionKind::AddressToPointerInst: + // This is _the_ instruction which can let an address escape. + return true; + default: + // To be conservative, also bail for anything we don't handle here. + return true; + } + } + return false; +} - MemBehavior Behavior = MemBehavior::None; +MemBehavior MemoryBehaviorVisitor::getApplyBehavior(FullApplySite AS) { - // We can ignore mayTrap(). - bool any_in_guaranteed_params = false; - for (auto op : enumerate(AI->getArgumentOperands())) { - if (op.value().get() == V && - AI->getSubstCalleeConv().getSILArgumentConvention(op.index()) == swift::SILArgumentConvention::Indirect_In_Guaranteed) { - any_in_guaranteed_params = true; - break; + // Do a quick check first: if V is directly passed to an in_guaranteed + // argument, we know that the function cannot write to it. + for (Operand &argOp : AS.getArgumentOperands()) { + if (argOp.get() == V && + AS.getArgumentConvention(argOp) == + swift::SILArgumentConvention::Indirect_In_Guaranteed) { + return MemBehavior::MayRead; } } - if (any_in_guaranteed_params) { - // one the parameters in the function call is @in_guaranteed of V, ie. the - // callee isn't allowed to modify it. - Behavior = MemBehavior::MayRead; - } else { - auto &GlobalEffects = ApplyEffects.getGlobalEffects(); - Behavior = GlobalEffects.getMemBehavior(RetainObserveKind::IgnoreRetains); - - // Check all parameter effects. - for (unsigned Idx = 0, End = AI->getNumArguments(); - Idx < End && Behavior < MemBehavior::MayHaveSideEffects; ++Idx) { - auto &ArgEffect = ApplyEffects.getParameterEffects()[Idx]; - auto ArgBehavior = ArgEffect.getMemBehavior(RetainObserveKind::IgnoreRetains); - if (ArgEffect.mayRelease()) { - Behavior = MemBehavior::MayHaveSideEffects; - break; - } - auto NewBehavior = combineMemoryBehavior(Behavior, ArgBehavior); - if (NewBehavior != Behavior) { - SILValue Arg = AI->getArgument(Idx); - // We only consider the argument effects if the argument aliases V. - if (!Arg->getType().isAddress() || mayAlias(Arg)) - Behavior = NewBehavior; - } + SILValue object = getUnderlyingObject(V); + int numUsesChecked = 0; + + // For exclusive/local addresses we can do a quick and good check with alias + // analysis. For everything else we use escape analysis (see below). + // TODO: The check for not-escaping can probably done easier with the upcoming + // API of AccessStorage. + bool nonEscapingAddress = + (isa(object) || isExclusiveArgument(object)) && + !hasEscapingUses(object, numUsesChecked); + + FunctionSideEffects applyEffects; + SEA->getCalleeEffects(applyEffects, AS); + + MemBehavior behavior = MemBehavior::None; + MemBehavior globalBehavior = applyEffects.getGlobalEffects().getMemBehavior( + RetainObserveKind::IgnoreRetains); + + // If it's a non-escaping address, we don't care about the "global" effects + // of the called function. + if (!nonEscapingAddress) + behavior = globalBehavior; + + // Check all parameter effects. + for (unsigned argIdx = 0, end = AS.getNumArguments(); + argIdx < end && behavior < MemBehavior::MayHaveSideEffects; + ++argIdx) { + SILValue arg = AS.getArgument(argIdx); + + // In case the argument is not an address, alias analysis will always report + // a no-alias. Therefore we have to treat non-address arguments + // conservatively here. For example V could be a ref_element_addr of a + // reference argument. In this case V clearly "aliases" the argument, but + // this is not reported by alias analysis. + if ((!nonEscapingAddress && !arg->getType().isAddress()) || + mayAlias(arg)) { + MemBehavior argBehavior = applyEffects.getArgumentBehavior(AS, argIdx); + behavior = combineMemoryBehavior(behavior, argBehavior); } } - if (Behavior > MemBehavior::None) { - if (Behavior > MemBehavior::MayRead && isLetValue()) - Behavior = MemBehavior::MayRead; + if (behavior > MemBehavior::None) { + if (behavior > MemBehavior::MayRead && isLetValue()) + behavior = MemBehavior::MayRead; // Ask escape analysis. - if (!EA->canEscapeTo(V, AI)) - Behavior = MemBehavior::None; + if (!nonEscapingAddress && !EA->canEscapeTo(V, AS)) + behavior = MemBehavior::None; } - LLVM_DEBUG(llvm::dbgs() << " Found apply, returning " << Behavior << '\n'); - return Behavior; + LLVM_DEBUG(llvm::dbgs() << " Found apply, returning " << behavior << '\n'); + + return behavior; } MemBehavior diff --git a/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp b/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp index 0712744d5a5a8..3b0096be8e5cb 100644 --- a/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp @@ -219,6 +219,34 @@ FunctionSideEffects::getMemBehavior(RetainObserveKind ScanKind) const { return Behavior; } +MemoryBehavior +FunctionSideEffects::getArgumentBehavior(FullApplySite applySite, + unsigned argIdx) { + // Rule out trivial non-address argument types. + SILType argType = applySite.getArgument(argIdx)->getType(); + if (!argType.isAddress() && argType.isTrivial(*applySite.getFunction())) + return MemoryBehavior::None; + + // The overall argument effect is the combination of the argument and the + // global effects. + MemoryBehavior behavior = + GlobalEffects.getMemBehavior(RetainObserveKind::IgnoreRetains); + MemoryBehavior argBehavior = + ParamEffects[argIdx].getMemBehavior(RetainObserveKind::IgnoreRetains); + + behavior = combineMemoryBehavior(behavior, argBehavior); + + if (behavior > MemoryBehavior::MayRead && + applySite.getArgumentConvention(applySite.getArgumentRef(argIdx)) == + SILArgumentConvention::Indirect_In_Guaranteed) { + // Even if side-effect analysis doesn't know anything about the called + // called function, the in_guaranteed convention guarantees that the + // argument is never written to. + return MemoryBehavior::MayRead; + } + return behavior; +} + bool FunctionSideEffects::mergeFrom(const FunctionSideEffects &RHS) { bool Changed = mergeFlags(RHS); Changed |= GlobalEffects.mergeFrom(RHS.GlobalEffects); diff --git a/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp b/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp index d3927f0451c45..68e568865d37b 100644 --- a/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp @@ -59,7 +59,10 @@ class MemBehaviorDumper : public SILModuleTransform { // selected types of instructions. static bool shouldTestInstruction(SILInstruction *I) { // Only consider function calls. - if ((EnableDumpAll && I->mayReadOrWriteMemory()) || FullApplySite::isa(I)) + if ((EnableDumpAll && I->mayReadOrWriteMemory()) || + FullApplySite::isa(I) || + isa(I) || + isa(I)) return true; return false; diff --git a/test/SILOptimizer/mem-behavior.sil b/test/SILOptimizer/mem-behavior.sil index 13fa7aa1e023d..0da680975137c 100644 --- a/test/SILOptimizer/mem-behavior.sil +++ b/test/SILOptimizer/mem-behavior.sil @@ -13,7 +13,11 @@ class X { } sil @unknown_func : $@convention(thin) (Int32, @in Int32) -> () - +sil @single_indirect_arg : $@convention(thin) (@in Int32) -> Int32 +sil @single_indirect_arg_and_error : $@convention(thin) (@in Int32) -> (Int32, @error Error) +sil @single_indirect_arg_coroutine : $@yield_once @convention(thin) (@in Int32) -> @yields Int32 +sil @indirect_arg_and_ptr : $@convention(thin) (@in Int32, Builtin.RawPointer) -> Int32 +sil @single_reference : $@convention(thin) (@guaranteed X) -> Int32 sil @nouser_func : $@convention(thin) () -> () sil @store_to_int : $@convention(thin) (Int32, @inout Int32) -> () { @@ -160,6 +164,144 @@ bb0(%0 : $X): return %6 : $() } +// CHECK-LABEL: @allocstack_and_copyaddr +// CHECK: PAIR #0. +// CHECK-NEXT: %4 = apply %3(%0) : $@convention(thin) (@in Int32) -> Int32 +// CHECK-NEXT: %0 = argument of bb0 : $*Int32 +// CHECK-NEXT: r=1,w=1 +// CHECK: PAIR #1. +// CHECK-NEXT: %4 = apply %3(%0) : $@convention(thin) (@in Int32) -> Int32 +// CHECK-NEXT: %1 = alloc_stack $Int32 +// CHECK-NEXT: r=0,w=0 +sil @allocstack_and_copyaddr : $@convention(thin) (@in Int32) -> Int32 { +bb0(%0 : $*Int32): + %1 = alloc_stack $Int32 + copy_addr %0 to %1 : $*Int32 + %3 = function_ref @single_indirect_arg : $@convention(thin) (@in Int32) -> Int32 + %4 = apply %3(%0) : $@convention(thin) (@in Int32) -> Int32 + dealloc_stack %1 : $*Int32 + return %4 : $Int32 +} + +// CHECK-LABEL: @inout_and_copyaddr +// CHECK: PAIR #0. +// CHECK-NEXT: %4 = apply %3(%0) : $@convention(thin) (@in Int32) -> Int32 +// CHECK-NEXT: %0 = argument of bb0 : $*Int32 +// CHECK-NEXT: r=1,w=1 +// CHECK: PAIR #1. +// CHECK-NEXT: %4 = apply %3(%0) : $@convention(thin) (@in Int32) -> Int32 +// CHECK-NEXT: %1 = argument of bb0 : $*Int32 +// CHECK-NEXT: r=0,w=0 +sil @inout_and_copyaddr : $@convention(thin) (@in Int32, @inout Int32) -> Int32 { +bb0(%0 : $*Int32, %1 : $*Int32): + copy_addr %0 to %1 : $*Int32 + %3 = function_ref @single_indirect_arg : $@convention(thin) (@in Int32) -> Int32 + %4 = apply %3(%0) : $@convention(thin) (@in Int32) -> Int32 + return %4 : $Int32 +} + +// CHECK-LABEL: @esacping_allocstack_and_copyaddr +// CHECK: PAIR #0. +// CHECK-NEXT: %5 = apply %4(%0, %3) : $@convention(thin) (@in Int32, Builtin.RawPointer) -> Int32 +// CHECK-NEXT: %0 = argument of bb0 : $*Int32 +// CHECK-NEXT: r=1,w=1 +// CHECK: PAIR #1. +// CHECK-NEXT: %5 = apply %4(%0, %3) : $@convention(thin) (@in Int32, Builtin.RawPointer) -> Int32 +// CHECK-NEXT: %1 = alloc_stack $Int32 +// CHECK-NEXT: r=1,w=1 +// CHECK: PAIR #2. +// CHECK-NEXT: %5 = apply %4(%0, %3) : $@convention(thin) (@in Int32, Builtin.RawPointer) -> Int32 +// CHECK-NEXT: %3 = address_to_pointer %1 : $*Int32 to $Builtin.RawPointer +// CHECK-NEXT: r=1,w=1 +sil @esacping_allocstack_and_copyaddr : $@convention(thin) (@in Int32) -> Int32 { +bb0(%0 : $*Int32): + %1 = alloc_stack $Int32 + copy_addr %0 to %1 : $*Int32 + %3 = address_to_pointer %1 : $*Int32 to $Builtin.RawPointer + %4 = function_ref @indirect_arg_and_ptr : $@convention(thin) (@in Int32, Builtin.RawPointer) -> Int32 + %5 = apply %4(%0, %3) : $@convention(thin) (@in Int32, Builtin.RawPointer) -> Int32 + dealloc_stack %1 : $*Int32 + return %5 : $Int32 +} + + +// CHECK-LABEL: @tryapply_allocstack_and_copyaddr +// CHECK: PAIR #0. +// CHECK-NEXT: try_apply %3(%0) : $@convention(thin) (@in Int32) -> (Int32, @error Error), normal bb1, error bb2 +// CHECK-NEXT: %0 = argument of bb0 : $*Int32 +// CHECK-NEXT: r=1,w=1 +// CHECK: PAIR #1. +// CHECK-NEXT: try_apply %3(%0) : $@convention(thin) (@in Int32) -> (Int32, @error Error), normal bb1, error bb2 +// CHECK-NEXT: %1 = alloc_stack $Int32 +// CHECK-NEXT: r=0,w=0 +sil @tryapply_allocstack_and_copyaddr : $@convention(thin) (@in Int32) -> Int32 { +bb0(%0 : $*Int32): + %1 = alloc_stack $Int32 + copy_addr %0 to %1 : $*Int32 + %3 = function_ref @single_indirect_arg_and_error : $@convention(thin) (@in Int32) -> (Int32, @error Error) + try_apply %3(%0) : $@convention(thin) (@in Int32) -> (Int32, @error Error), normal bb1, error bb2 +bb1(%5 : $Int32): + dealloc_stack %1 : $*Int32 + return %5 : $Int32 +bb2(%8 : $Error): + unreachable +} + + +// CHECK-LABEL: @beginapply_allocstack_and_copyaddr +// CHECK: PAIR #0. +// CHECK-NEXT: (%4, %5) = begin_apply %3(%0) : $@yield_once @convention(thin) (@in Int32) -> @yields Int32 +// CHECK-NEXT: %0 = argument of bb0 : $*Int32 +// CHECK-NEXT: r=1,w=1 +// CHECK: PAIR #1. +// CHECK-NEXT: (%4, %5) = begin_apply %3(%0) : $@yield_once @convention(thin) (@in Int32) -> @yields Int32 +// CHECK-NEXT: %1 = alloc_stack $Int32 +// CHECK-NEXT: r=0,w=0 +// CHECK: PAIR #3. +// CHECK-NEXT: end_apply %5 +// CHECK-NEXT: %0 = argument of bb0 : $*Int32 +// CHECK-NEXT: r=1,w=1 +// CHECK: PAIR #4. +// CHECK-NEXT: end_apply %5 +// CHECK-NEXT: %1 = alloc_stack $Int32 +// CHECK-NEXT: r=0,w=0 +// CHECK: PAIR #8. +// CHECK-NEXT: abort_apply %5 +// CHECK-NEXT: %0 = argument of bb0 : $*Int32 +// CHECK-NEXT: r=1,w=1 +// CHECK: PAIR #9. +// CHECK-NEXT: abort_apply %5 +// CHECK-NEXT: %1 = alloc_stack $Int32 +// CHECK-NEXT: r=0,w=0 +sil @beginapply_allocstack_and_copyaddr : $@convention(thin) (@in Int32) -> Int32 { +bb0(%0 : $*Int32): + %1 = alloc_stack $Int32 + copy_addr %0 to %1 : $*Int32 + %3 = function_ref @single_indirect_arg_coroutine : $@yield_once @convention(thin) (@in Int32) -> @yields Int32 + (%4, %5) = begin_apply %3(%0) : $@yield_once @convention(thin) (@in Int32) -> @yields Int32 + cond_br undef, bb1, bb2 +bb1: + end_apply %5 + dealloc_stack %1 : $*Int32 + return %4 : $Int32 +bb2: + abort_apply %5 + unreachable +} + +// CHECK-LABEL: @refelementaddr_and_reference +// CHECK: PAIR #1. +// CHECK-NEXT: %3 = apply %2(%0) : $@convention(thin) (@guaranteed X) -> Int32 +// CHECK-NEXT: %1 = ref_element_addr %0 : $X, #X.a +// CHECK-NEXT: r=1,w=1 +sil @refelementaddr_and_reference : $@convention(thin) (@guaranteed X) -> Int32 { +bb0(%0 : $X): + %1 = ref_element_addr %0 : $X, #X.a + %2 = function_ref @single_reference : $@convention(thin) (@guaranteed X) -> Int32 + %3 = apply %2(%0) : $@convention(thin) (@guaranteed X) -> Int32 + return %3 : $Int32 +} + sil @load_from_in : $@convention(thin) (@in X) -> () { bb0(%0 : $*X): %1 = load %0 : $*X @@ -189,6 +331,28 @@ bb0: return %r : $() } +// CHECK-LABEL: @non_overlapping_struct_fields +// CHECK: PAIR #0. +// CHECK-NEXT: %4 = apply %3(%1) : $@convention(thin) (@in Int32) -> Int32 +// CHECK-NEXT: %0 = argument of bb0 : $*TwoInts +// CHECK-NEXT: r=1,w=1 +// CHECK: PAIR #1. +// CHECK-NEXT: %4 = apply %3(%1) : $@convention(thin) (@in Int32) -> Int32 +// CHECK-NEXT: %1 = struct_element_addr %0 : $*TwoInts, #TwoInts.i1 +// CHECK-NEXT: r=1,w=1 +// CHECK: PAIR #2. +// CHECK-NEXT: %4 = apply %3(%1) : $@convention(thin) (@in Int32) -> Int32 +// CHECK-NEXT: %2 = struct_element_addr %0 : $*TwoInts, #TwoInts.i2 +// CHECK-NEXT: r=0,w=0 +sil @non_overlapping_struct_fields : $@convention(thin) (@in TwoInts) -> Int32 { +bb0(%0 : $*TwoInts): + %1 = struct_element_addr %0 : $*TwoInts, #TwoInts.i1 + %2 = struct_element_addr %0 : $*TwoInts, #TwoInts.i2 + %3 = function_ref @single_indirect_arg : $@convention(thin) (@in Int32) -> Int32 + %4 = apply %3(%1) : $@convention(thin) (@in Int32) -> Int32 + return %4 : $Int32 +} + sil @copy_ints : $@convention(thin) (@inout Int32, @inout Int32) -> () { bb0(%0 : $*Int32, %1 : $*Int32): %2 = load %0 : $*Int32 diff --git a/test/SILOptimizer/specialize_opaque_type_archetypes.swift b/test/SILOptimizer/specialize_opaque_type_archetypes.swift index 0236c0817ccbd..c27966dd7f2d3 100644 --- a/test/SILOptimizer/specialize_opaque_type_archetypes.swift +++ b/test/SILOptimizer/specialize_opaque_type_archetypes.swift @@ -229,7 +229,6 @@ func nonResilient() -> some ExternalP2 { // CHECK-LABEL: sil @$s1A019usePairResilientNonC0yyF : $@convention(thin) () -> () // CHECK: alloc_stack $Pair Date: Fri, 9 Oct 2020 19:07:42 +0200 Subject: [PATCH 360/745] TempRValueElimination: handle potential modifications of the copy-source in a called functions correctly. This fixes a miscompile in case the source of the optimized copy_addr is modified in a called function with to a not visible alias. This can happen with class properties or global variables. This fix removes the special handling of function parameters, which was just wrong. Instead it simply uses the alias analysis API to check for modifications of the source object. The fix makes TempRValueElimination more conservative and this can cause some performance regressions, but this is unavoidable. rdar://problem/69605657 --- .../Transforms/TempRValueElimination.cpp | 88 ++++++++----------- test/SILOptimizer/bridged_casts_folding.swift | 6 +- .../opened_archetype_operands_tracking.sil | 2 - .../sil_combine_protocol_conf.swift | 12 ++- test/SILOptimizer/temp_rvalue_opt.sil | 22 ++++- test/SILOptimizer/temp_rvalue_opt.swift | 24 +++++ 6 files changed, 96 insertions(+), 58 deletions(-) create mode 100644 test/SILOptimizer/temp_rvalue_opt.swift diff --git a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp index 6e4ab1b04bbbc..7c80311fd4fe1 100644 --- a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp +++ b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp @@ -79,9 +79,8 @@ class TempRValueOptPass : public SILFunctionTransform { checkTempObjectDestroy(AllocStackInst *tempObj, CopyAddrInst *copyInst, ValueLifetimeAnalysis::Frontier &tempAddressFrontier); - bool checkNoTempObjectModificationInApply(Operand *tempObjUser, - SILInstruction *inst, - SILValue srcAddr); + bool canApplyBeTreatedAsLoad(Operand *tempObjUser, ApplySite apply, + SILValue srcAddr); bool tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst); std::pair @@ -115,57 +114,39 @@ bool TempRValueOptPass::collectLoadsFromProjection( return true; } -/// Check if 'tempObjUser' passed to the apply instruction can be modified by it -bool TempRValueOptPass::checkNoTempObjectModificationInApply( - Operand *tempObjUser, SILInstruction *applyInst, SILValue srcAddr) { - ApplySite apply(applyInst); - +/// Check if \p tempObjUser, passed to the apply instruction, is only loaded, +/// but not modified and if \p srcAddr is not modified as well. +bool TempRValueOptPass::canApplyBeTreatedAsLoad( + Operand *tempObjUser, ApplySite apply, SILValue srcAddr) { // Check if the function can just read from tempObjUser. auto convention = apply.getArgumentConvention(*tempObjUser); if (!convention.isGuaranteedConvention()) { LLVM_DEBUG(llvm::dbgs() << " Temp consuming use may write/destroy " "its source" - << *applyInst); - return false; - } - - // If we do not have an src address, but are indirect, bail. We would need - // to perform function signature specialization to change the functions - // signature to pass something direct. - if (!srcAddr && convention.isIndirectConvention()) { - LLVM_DEBUG( - llvm::dbgs() - << " Temp used to materialize value for indirect convention?! Can " - "not remove temporary without func sig opts" - << *applyInst); + << *apply.getInstruction()); return false; } - // Check if there is another function argument, which is inout which might - // modify the source address if we have one. - // - // When a use of the temporary is an apply, then we need to prove that the - // function called by the apply cannot modify the temporary's source - // value. By design, this should be handled by - // `checkNoSourceModification`. However, this would be too conservative - // since it's common for the apply to have an @out argument, and alias - // analysis cannot prove that the @out does not alias with `src`. Instead, - // `checkNoSourceModification` always avoids analyzing the current use, so - // applies need to be handled here. We already know that an @out cannot - // alias with `src` because the `src` value must be initialized at the point - // of the call. Hence, it is sufficient to check specifically for another - // @inout that might alias with `src`. if (srcAddr) { - auto calleeConv = apply.getSubstCalleeConv(); - unsigned calleeArgIdx = apply.getCalleeArgIndexOfFirstAppliedArg(); - for (const auto &operand : apply.getArgumentOperands()) { - auto argConv = calleeConv.getSILArgumentConvention(calleeArgIdx); - if (argConv.isInoutConvention()) { - if (!aa->isNoAlias(operand.get(), srcAddr)) { - return false; - } - } - ++calleeArgIdx; + // If the function may write to the source of the copy_addr, the apply + // cannot be treated as a load: all (potential) writes of the source must + // appear _after_ all loads of the temporary. But in case of a function call + // we don't know in which order the writes and loads are executed inside the + // called function. The source may be written before the temporary is + // loaded, which would make the optization invalid. + if (aa->mayWriteToMemory(apply.getInstruction(), srcAddr)) + return false; + } else { + // If we do not have an src address, but are indirect, bail. We would need + // to perform function signature specialization to change the functions + // signature to pass something direct. + if (convention.isIndirectConvention()) { + LLVM_DEBUG( + llvm::dbgs() + << " Temp used to materialize value for indirect convention?! Can " + "not remove temporary without func sig opts" + << *apply.getInstruction()); + return false; } } return true; @@ -189,7 +170,8 @@ bool TempRValueOptPass::collectLoads( SILValue srcAddr, SmallPtrSetImpl &loadInsts) { // All normal uses (loads) must be in the initialization block. // (The destroy and dealloc are commonly in a different block though.) - if (user->getParent() != address->getParent()) + SILBasicBlock *block = address->getParent(); + if (user->getParent() != block) return false; // Only allow uses that cannot destroy their operand. We need to be sure @@ -232,22 +214,25 @@ bool TempRValueOptPass::collectLoads( LLVM_FALLTHROUGH; case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: { - if (!checkNoTempObjectModificationInApply(userOp, user, srcAddr)) + if (!canApplyBeTreatedAsLoad(userOp, ApplySite(user), srcAddr)) return false; // Everything is okay with the function call. Register it as a "load". loadInsts.insert(user); return true; } case SILInstructionKind::BeginApplyInst: { - if (!checkNoTempObjectModificationInApply(userOp, user, srcAddr)) + if (!canApplyBeTreatedAsLoad(userOp, ApplySite(user), srcAddr)) return false; auto beginApply = cast(user); // Register 'end_apply'/'abort_apply' as loads as well // 'checkNoSourceModification' should check instructions until // 'end_apply'/'abort_apply'. - for (auto tokenUses : beginApply->getTokenResult()->getUses()) { - loadInsts.insert(tokenUses->getUser()); + for (auto tokenUse : beginApply->getTokenResult()->getUses()) { + SILInstruction *user = tokenUse->getUser(); + if (user->getParent() != block) + return false; + loadInsts.insert(tokenUse->getUser()); } return true; } @@ -285,7 +270,8 @@ bool TempRValueOptPass::collectLoads( return collectLoadsFromProjection(utedai, srcAddr, loadInsts); } case SILInstructionKind::StructElementAddrInst: - case SILInstructionKind::TupleElementAddrInst: { + case SILInstructionKind::TupleElementAddrInst: + case SILInstructionKind::UncheckedAddrCastInst: { return collectLoadsFromProjection(cast(user), srcAddr, loadInsts); } diff --git a/test/SILOptimizer/bridged_casts_folding.swift b/test/SILOptimizer/bridged_casts_folding.swift index d168cd16c30b7..e02c312a3a976 100644 --- a/test/SILOptimizer/bridged_casts_folding.swift +++ b/test/SILOptimizer/bridged_casts_folding.swift @@ -904,8 +904,12 @@ var anyHashable: AnyHashable = 0 // CHECK-LABEL: $s21bridged_casts_folding29testUncondCastSwiftToSubclassAA08NSObjectI0CyF // CHECK: [[GLOBAL:%[0-9]+]] = global_addr @$s21bridged_casts_folding11anyHashables03AnyE0Vv +// CHECK: [[TMP:%[0-9]+]] = alloc_stack $AnyHashable +// CHECK: [[ACCESS:%[0-9]+]] = begin_access [read] [static] [no_nested_conflict] [[GLOBAL]] +// CHECK: copy_addr [[ACCESS]] to [initialization] [[TMP]] // CHECK: [[FUNC:%.*]] = function_ref @$ss11AnyHashableV10FoundationE19_bridgeToObjectiveCSo8NSObjectCyF -// CHECK-NEXT: apply [[FUNC]]([[GLOBAL]]) +// CHECK-NEXT: apply [[FUNC]]([[TMP]]) +// CHECK-NEXT: destroy_addr [[TMP]] // CHECK-NEXT: unconditional_checked_cast {{%.*}} : $NSObject to NSObjectSubclass // CHECK: } // end sil function '$s21bridged_casts_folding29testUncondCastSwiftToSubclassAA08NSObjectI0CyF' @inline(never) diff --git a/test/SILOptimizer/opened_archetype_operands_tracking.sil b/test/SILOptimizer/opened_archetype_operands_tracking.sil index 1f640f2a80817..a5e43d3978c49 100644 --- a/test/SILOptimizer/opened_archetype_operands_tracking.sil +++ b/test/SILOptimizer/opened_archetype_operands_tracking.sil @@ -210,12 +210,10 @@ sil hidden_external @use : $@convention(thin) <τ_0_0> (@in τ_0_0) -> () // It should contain alloc_ref and alloc_stack instructions using opened archetypes. // CHECK-LABEL: sil @bar // CHECK: open_existential{{.*}}C08045E0-2779-11E7-970E-A45E60E99281 -// CHECK: alloc_stack{{.*}}C08045E0-2779-11E7-970E-A45E60E99281 // CHECK: alloc_ref{{.*}}C08045E0-2779-11E7-970E-A45E60E99281 // CHECK-NOT: function_ref @use // CHECK: function_ref @use // CHECK-NOT: function_ref -// CHECK: dealloc_stack{{.*}}C08045E0-2779-11E7-970E-A45E60E99281 // CHECK: strong_release{{.*}}C08045E0-2779-11E7-970E-A45E60E99281 // CHECK: dealloc_ref{{.*}}C08045E0-2779-11E7-970E-A45E60E99281 // CHECK: end sil function 'bar' diff --git a/test/SILOptimizer/sil_combine_protocol_conf.swift b/test/SILOptimizer/sil_combine_protocol_conf.swift index 0b22b61654704..3779cdd4d0388 100644 --- a/test/SILOptimizer/sil_combine_protocol_conf.swift +++ b/test/SILOptimizer/sil_combine_protocol_conf.swift @@ -245,8 +245,10 @@ internal class OtherClass { // CHECK: load [[S11]] // CHECK: [[R2:%.*]] = ref_element_addr [[ARG]] : $OtherClass, #OtherClass.arg2 // CHECK: [[O2:%.*]] = open_existential_addr immutable_access [[R2]] : $*GenericPropProtocol to $*@opened("{{.*}}") GenericPropProtocol +// CHECK: [[T1:%.*]] = alloc_stack $@opened +// CHECK: copy_addr [[O2]] to [initialization] [[T1]] // CHECK: [[W2:%.*]] = witness_method $@opened("{{.*}}") GenericPropProtocol, #GenericPropProtocol.val!getter : (Self) -> () -> Int, [[O2]] : $*@opened("{{.*}}") GenericPropProtocol : $@convention(witness_method: GenericPropProtocol) <τ_0_0 where τ_0_0 : GenericPropProtocol> (@in_guaranteed τ_0_0) -> Int -// CHECK: apply [[W2]]<@opened("{{.*}}") GenericPropProtocol>([[O2]]) : $@convention(witness_method: GenericPropProtocol) <τ_0_0 where τ_0_0 : GenericPropProtocol> (@in_guaranteed τ_0_0) -> Int +// CHECK: apply [[W2]]<@opened("{{.*}}") GenericPropProtocol>([[T1]]) : $@convention(witness_method: GenericPropProtocol) <τ_0_0 where τ_0_0 : GenericPropProtocol> (@in_guaranteed τ_0_0) -> Int // CHECK: struct_extract // CHECK: integer_literal // CHECK: builtin @@ -265,8 +267,10 @@ internal class OtherClass { // CHECK: cond_fail // CHECK: [[R5:%.*]] = ref_element_addr [[ARG]] : $OtherClass, #OtherClass.arg4 // CHECK: [[O5:%.*]] = open_existential_addr immutable_access [[R5]] : $*GenericNestedPropProtocol to $*@opened("{{.*}}") GenericNestedPropProtocol +// CHECK: [[T2:%.*]] = alloc_stack $@opened +// CHECK: copy_addr [[O5]] to [initialization] [[T2]] // CHECK: [[W5:%.*]] = witness_method $@opened("{{.*}}") GenericNestedPropProtocol, #GenericNestedPropProtocol.val!getter : (Self) -> () -> Int, [[O5:%.*]] : $*@opened("{{.*}}") GenericNestedPropProtocol : $@convention(witness_method: GenericNestedPropProtocol) <τ_0_0 where τ_0_0 : GenericNestedPropProtocol> (@in_guaranteed τ_0_0) -> Int -// CHECK: apply [[W5]]<@opened("{{.*}}") GenericNestedPropProtocol>([[O5]]) : $@convention(witness_method: GenericNestedPropProtocol) <τ_0_0 where τ_0_0 : GenericNestedPropProtocol> (@in_guaranteed τ_0_0) -> Int +// CHECK: apply [[W5]]<@opened("{{.*}}") GenericNestedPropProtocol>([[T2]]) : $@convention(witness_method: GenericNestedPropProtocol) <τ_0_0 where τ_0_0 : GenericNestedPropProtocol> (@in_guaranteed τ_0_0) -> Int // CHECK: struct_extract // CHECK: builtin // CHECK: tuple_extract @@ -333,8 +337,10 @@ internal class OtherKlass { // CHECK: integer_literal // CHECK: [[R1:%.*]] = ref_element_addr [[ARG]] : $OtherKlass, #OtherKlass.arg2 // CHECK: [[O1:%.*]] = open_existential_addr immutable_access [[R1]] : $*AGenericProtocol to $*@opened("{{.*}}") AGenericProtocol +// CHECK: [[T1:%.*]] = alloc_stack $@opened +// CHECK: copy_addr [[O1]] to [initialization] [[T1]] // CHECK: [[W1:%.*]] = witness_method $@opened("{{.*}}") AGenericProtocol, #AGenericProtocol.val!getter : (Self) -> () -> Int, [[O1]] : $*@opened("{{.*}}") AGenericProtocol : $@convention(witness_method: AGenericProtocol) <τ_0_0 where τ_0_0 : AGenericProtocol> (@in_guaranteed τ_0_0) -> Int -// CHECK: apply [[W1]]<@opened("{{.*}}") AGenericProtocol>([[O1]]) : $@convention(witness_method: AGenericProtocol) <τ_0_0 where τ_0_0 : AGenericProtocol> (@in_guaranteed τ_0_0) -> Int +// CHECK: apply [[W1]]<@opened("{{.*}}") AGenericProtocol>([[T1]]) : $@convention(witness_method: AGenericProtocol) <τ_0_0 where τ_0_0 : AGenericProtocol> (@in_guaranteed τ_0_0) -> Int // CHECK: struct_extract // CHECK: integer_literal // CHECK: builtin diff --git a/test/SILOptimizer/temp_rvalue_opt.sil b/test/SILOptimizer/temp_rvalue_opt.sil index 5ca28bac003f9..3e503d5a7ae94 100644 --- a/test/SILOptimizer/temp_rvalue_opt.sil +++ b/test/SILOptimizer/temp_rvalue_opt.sil @@ -14,7 +14,9 @@ struct GS { var _value: Builtin.Int64 } -class Klass {} +class Klass { + @_hasStorage var a: String +} struct Str { var kl: Klass @@ -22,6 +24,7 @@ struct Str { } sil @unknown : $@convention(thin) () -> () +sil @load_string : $@convention(thin) (@in_guaranteed String) -> String sil @inguaranteed_user_without_result : $@convention(thin) (@in_guaranteed Klass) -> () { bb0(%0 : $*Klass): @@ -333,6 +336,23 @@ bb0(%0 : $*GS, %1 : $*GS, %2 : $Builtin.Int64): return %9999 : $() } +// CHECK-LABEL: sil [ossa] @consider_implicit_aliases_in_callee +// CHECK: alloc_stack +// CHECK-NEXT: copy_addr +// CHECK: } // end sil function 'consider_implicit_aliases_in_callee' + +sil [ossa] @consider_implicit_aliases_in_callee : $@convention(thin) (@guaranteed Klass) -> String { +bb0(%0 : @guaranteed $Klass): + %1 = ref_element_addr %0 : $Klass, #Klass.a + %2 = alloc_stack $String + copy_addr %1 to [initialization] %2 : $*String + %f = function_ref @load_string : $@convention(thin) (@in_guaranteed String) -> String + %a = apply %f(%2) : $@convention(thin) (@in_guaranteed String) -> String + destroy_addr %2 : $*String + dealloc_stack %2 : $*String + return %a : $String +} + // Test temp RValue elimination on switches. // CHECK-LABEL: sil @rvalueSwitch // CHECK: bb1: diff --git a/test/SILOptimizer/temp_rvalue_opt.swift b/test/SILOptimizer/temp_rvalue_opt.swift new file mode 100644 index 0000000000000..8c8578bb6cb9f --- /dev/null +++ b/test/SILOptimizer/temp_rvalue_opt.swift @@ -0,0 +1,24 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -O %s -o %t/a.out +// RUN: %target-codesign %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s + +// REQUIRES: executable_test + +var global: Any = 1 +func withValue(action: (Any) -> Void) { + action(global) +} + +@inline(never) +public func test_global_side_effect() { + withValue { value in + print(value) + global = 24 + print(value) + } +} + +// CHECK: 1 +// CHECK-NEXT: 1 +test_global_side_effect() From 68f485424c2b655075c77877acc9ed05756c5d08 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 9 Oct 2020 20:45:03 +0200 Subject: [PATCH 361/745] SILOptimizer: add an additional TempRValueOpt pass later in the pipeline. This can compensate the performance regression of the more conservative handling of function calls in TempRValueOpt (see previous commit). The pass runs after the inlining passes and can therefore optimize in some cases where it's not possible before inlining. --- lib/SILOptimizer/PassManager/PassPipeline.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 2c81d1bbcb646..afcbd3c7ddbcd 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -393,6 +393,7 @@ void addFunctionPasses(SILPassPipelinePlan &P, P.addEarlyCodeMotion(); P.addReleaseHoisting(); P.addARCSequenceOpts(); + P.addTempRValueOpt(); P.addSimplifyCFG(); if (OpLevel == OptimizationLevelKind::LowLevel) { From a482c9c6466eeda1922baa4c5e1972f80ed6e23a Mon Sep 17 00:00:00 2001 From: Eric Miotto <1094986+edymtt@users.noreply.github.com> Date: Fri, 9 Oct 2020 13:52:08 -0700 Subject: [PATCH 362/745] [build] Remove FILES_MATCHING from SwiftShims/CMakeLists.txt (#34241) This is not an ideal solution, as we are likely installing more that we should with those instructions -- but it would unblock quickly the Source Compatibility suite. Addresses rdar://70040046 --- stdlib/public/SwiftShims/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/public/SwiftShims/CMakeLists.txt b/stdlib/public/SwiftShims/CMakeLists.txt index 0bd8c57a98505..4c2b3c4e59588 100644 --- a/stdlib/public/SwiftShims/CMakeLists.txt +++ b/stdlib/public/SwiftShims/CMakeLists.txt @@ -197,13 +197,13 @@ endif() swift_install_in_component(DIRECTORY "${clang_headers_location}/" DESTINATION "lib/swift/clang" COMPONENT clang-builtin-headers - FILES_MATCHING PATTERN "*.h") + PATTERN "*.h") if(SWIFT_BUILD_STATIC_STDLIB) swift_install_in_component(DIRECTORY "${clang_headers_location}/" DESTINATION "lib/swift_static/clang" COMPONENT clang-builtin-headers - FILES_MATCHING PATTERN "*.h") + PATTERN "*.h") endif() @@ -227,4 +227,4 @@ file(TO_CMAKE_PATH "${LLVM_LIBRARY_OUTPUT_INTDIR}" swift_install_in_component(DIRECTORY "${_SWIFT_SHIMS_PATH_TO_CLANG_BUILD}/lib/clang" DESTINATION "lib" COMPONENT clang-builtin-headers-in-clang-resource-dir - FILES_MATCHING PATTERN "*.h") + PATTERN "*.h") From 3364c51b1d53fd22a3adbaf3d406bd6685535221 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 6 Oct 2020 15:40:08 -0700 Subject: [PATCH 363/745] SIL: Verify invariants of async_continuation instructions. - Enforce types of continuations and resume/error BBargs for await - Can't access the continuation again or exit the function mid-suspend --- include/swift/SIL/SILInstruction.h | 49 +++++++++------ include/swift/SIL/SILNode.h | 2 +- include/swift/SIL/SILNodes.def | 10 +-- lib/SIL/IR/SILInstruction.cpp | 17 ++++- lib/SIL/IR/SILInstructions.cpp | 27 +------- lib/SIL/Verifier/SILVerifier.cpp | 99 +++++++++++++++++++++++++++++- 6 files changed, 151 insertions(+), 53 deletions(-) diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index c336ed5ab5d8a..2391afe52c634 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -514,6 +514,10 @@ class SILInstruction "Operand does not belong to a SILInstruction"); return isTypeDependentOperand(Op.getOperandNumber()); } + + /// Returns true if evaluation of this instruction may cause suspension of an + /// async task. + bool maySuspend() const; private: /// Predicate used to filter OperandValueRange. @@ -3039,12 +3043,34 @@ class KeyPathPattern final } }; -/// Accesses the continuation for an async task, to prepare a primitive suspend operation. +/// Base class for instructions that access the continuation of an async task, +/// in order to set up a suspension. /// The continuation must be consumed by an AwaitAsyncContinuation instruction locally, /// and must dynamically be resumed exactly once during the program's ensuing execution. +class GetAsyncContinuationInstBase + : public SingleValueInstruction +{ +protected: + using SingleValueInstruction::SingleValueInstruction; + +public: + /// Get the type of the value the async task receives on a resume. + CanType getFormalResumeType() const; + SILType getLoweredResumeType() const; + + /// True if the continuation can be used to resume the task by throwing an error. + bool throws() const; + + static bool classof(const SILNode *I) { + return I->getKind() >= SILNodeKind::First_GetAsyncContinuationInstBase && + I->getKind() <= SILNodeKind::Last_GetAsyncContinuationInstBase; + } +}; + +/// Accesses the continuation for an async task, to prepare a primitive suspend operation. class GetAsyncContinuationInst final : public InstructionBase + GetAsyncContinuationInstBase> { friend SILBuilder; @@ -3054,14 +3080,6 @@ class GetAsyncContinuationInst final {} public: - - /// Get the type of the value the async task receives on a resume. - CanType getFormalResumeType() const; - SILType getLoweredResumeType() const; - - /// True if the continuation can be used to resume the task by throwing an error. - bool throws() const; - ArrayRef getAllOperands() const { return {}; } MutableArrayRef getAllOperands() { return {}; } }; @@ -3074,7 +3092,7 @@ class GetAsyncContinuationInst final /// buffer that receives the incoming value when the continuation is resumed. class GetAsyncContinuationAddrInst final : public UnaryInstructionBase + GetAsyncContinuationInstBase> { friend SILBuilder; GetAsyncContinuationAddrInst(SILDebugLocation Loc, @@ -3082,15 +3100,6 @@ class GetAsyncContinuationAddrInst final SILType ContinuationTy) : UnaryInstructionBase(Loc, Operand, ContinuationTy) {} - -public: - - /// Get the type of the value the async task receives on a resume. - CanType getFormalResumeType() const; - SILType getLoweredResumeType() const; - - /// True if the continuation can be used to resume the task by throwing an error. - bool throws() const; }; /// Instantiates a key path object. diff --git a/include/swift/SIL/SILNode.h b/include/swift/SIL/SILNode.h index 829ac3536c2b7..d7d8b55cc9052 100644 --- a/include/swift/SIL/SILNode.h +++ b/include/swift/SIL/SILNode.h @@ -452,7 +452,7 @@ class alignas(8) SILNode { /// If this is a SILArgument or a SILInstruction get its parent module, /// otherwise return null. SILModule *getModule() const; - + /// Pretty-print the node. If the node is an instruction, the output /// will be valid SIL assembly; otherwise, it will be an arbitrary /// format suitable for debugging. diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index e79d11a57778b..c80bd67d4b093 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -719,10 +719,12 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) // be tightened, though we want to be careful that passes that try to do // code motion or eliminate this instruction don't do so without awareness of // its structural requirements. - SINGLE_VALUE_INST(GetAsyncContinuationInst, get_async_continuation, - SingleValueInstruction, MayHaveSideEffects, MayRelease) - SINGLE_VALUE_INST(GetAsyncContinuationAddrInst, get_async_continuation_addr, - SingleValueInstruction, MayHaveSideEffects, MayRelease) + ABSTRACT_SINGLE_VALUE_INST(GetAsyncContinuationInstBase, SingleValueInstruction) + SINGLE_VALUE_INST(GetAsyncContinuationInst, get_async_continuation, + GetAsyncContinuationInstBase, MayHaveSideEffects, MayRelease) + SINGLE_VALUE_INST(GetAsyncContinuationAddrInst, get_async_continuation_addr, + GetAsyncContinuationInstBase, MayHaveSideEffects, MayRelease) + SINGLE_VALUE_INST_RANGE(GetAsyncContinuationInstBase, GetAsyncContinuationInst, GetAsyncContinuationAddrInst) // Key paths // TODO: The only "side effect" is potentially retaining the returned key path diff --git a/lib/SIL/IR/SILInstruction.cpp b/lib/SIL/IR/SILInstruction.cpp index 235fa1918494f..9b602c7d4ee5f 100644 --- a/lib/SIL/IR/SILInstruction.cpp +++ b/lib/SIL/IR/SILInstruction.cpp @@ -1519,6 +1519,21 @@ MultipleValueInstruction *MultipleValueInstructionResult::getParent() { return reinterpret_cast(value); } +/// Returns true if evaluation of this node may cause suspension of an +/// async task. +bool SILInstruction::maySuspend() const { + // await_async_continuation always suspends the current task. + if (isa(this)) + return true; + + // Fully applying an async function may suspend the caller. + if (auto applySite = FullApplySite::isa(const_cast(this))) { + return applySite.getOrigCalleeType()->isAsync(); + } + + return false; +} + #ifndef NDEBUG //--- @@ -1536,7 +1551,7 @@ MultipleValueInstruction *MultipleValueInstructionResult::getParent() { // Check that all subclasses of MultipleValueInstructionResult are the same size // as MultipleValueInstructionResult. // -// If this changes, we just need to expand the size fo SILInstructionResultArray +// If this changes, we just need to expand the size of SILInstructionResultArray // to contain a stride. But we assume this now so we should enforce it. #define MULTIPLE_VALUE_INST_RESULT(ID, PARENT) \ static_assert( \ diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index 5655e95219b8a..3f97cbd6387ca 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -2923,12 +2923,12 @@ DestructureTupleInst *DestructureTupleInst::create(const SILFunction &F, DestructureTupleInst(M, Loc, Operand, Types, OwnershipKinds); } -CanType GetAsyncContinuationInst::getFormalResumeType() const { +CanType GetAsyncContinuationInstBase::getFormalResumeType() const { // The resume type is the type argument to the continuation type. return getType().castTo().getGenericArgs()[0]; } -SILType GetAsyncContinuationInst::getLoweredResumeType() const { +SILType GetAsyncContinuationInstBase::getLoweredResumeType() const { // The lowered resume type is the maximally-abstracted lowering of the // formal resume type. auto formalType = getFormalResumeType(); @@ -2937,29 +2937,8 @@ SILType GetAsyncContinuationInst::getLoweredResumeType() const { return M.Types.getLoweredType(AbstractionPattern::getOpaque(), formalType, c); } -bool GetAsyncContinuationInst::throws() const { +bool GetAsyncContinuationInstBase::throws() const { // The continuation throws if it's an UnsafeThrowingContinuation return getType().castTo()->getDecl() == getFunction()->getASTContext().getUnsafeThrowingContinuationDecl(); } - -CanType GetAsyncContinuationAddrInst::getFormalResumeType() const { - // The resume type is the type argument to the continuation type. - return getType().castTo().getGenericArgs()[0]; -} - -SILType GetAsyncContinuationAddrInst::getLoweredResumeType() const { - // The lowered resume type is the maximally-abstracted lowering of the - // formal resume type. - auto formalType = getFormalResumeType(); - auto &M = getFunction()->getModule(); - auto c = getFunction()->getTypeExpansionContext(); - return M.Types.getLoweredType(AbstractionPattern::getOpaque(), formalType, c); -} - -bool GetAsyncContinuationAddrInst::throws() const { - // The continuation throws if it's an UnsafeThrowingContinuation - return getType().castTo()->getDecl() - == getFunction()->getASTContext().getUnsafeThrowingContinuationDecl(); -} - diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 1fbf19738bef0..9a9df3bc03dfa 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -662,7 +662,7 @@ class SILVerifier : public SILVerifierBase { // Used for dominance checking within a basic block. llvm::DenseMap InstNumbers; - + DeadEndBlocks DEBlocks; LoadBorrowNeverInvalidatedAnalysis loadBorrowNeverInvalidatedAnalysis; bool SingleFunction = true; @@ -4748,6 +4748,72 @@ class SILVerifier : public SILVerifierBase { "Type of witness instruction does not match actual type of " "witnessed function"); } + + void checkGetAsyncContinuationInstBase(GetAsyncContinuationInstBase *GACI) { + auto resultTy = GACI->getType(); + auto &C = resultTy.getASTContext(); + auto resultBGT = resultTy.getAs(); + require(resultBGT, "Instruction type must be a continuation type"); + auto resultDecl = resultBGT->getDecl(); + require(resultDecl == C.getUnsafeContinuationDecl() + || resultDecl == C.getUnsafeThrowingContinuationDecl(), + "Instruction type must be a continuation type"); + } + + void checkGetAsyncContinuationInst(GetAsyncContinuationInst *GACI) { + checkGetAsyncContinuationInstBase(GACI); + } + + void checkGetAsyncContinuationAddrInst(GetAsyncContinuationAddrInst *GACI) { + checkGetAsyncContinuationInstBase(GACI); + + requireSameType(GACI->getOperand()->getType(), + GACI->getLoweredResumeType().getAddressType(), + "Operand type must match continuation resume type"); + } + + void checkAwaitAsyncContinuationInst(AwaitAsyncContinuationInst *AACI) { + // The operand must be a GetAsyncContinuation* instruction. + auto cont = dyn_cast(AACI->getOperand()); + require(cont, "can only await the result of a get_async_continuation instruction"); + bool isAddressForm = isa(cont); + + auto &C = cont->getType().getASTContext(); + + // The shape of the successors depends on the continuation instruction being + // awaited. + require((bool)AACI->getErrorBB() == cont->throws(), + "must have an error successor if and only if the continuation is throwing"); + if (cont->throws()) { + require(AACI->getErrorBB()->getNumArguments() == 1, + "error successor must take one argument"); + auto arg = AACI->getErrorBB()->getArgument(0); + auto errorType = C.getErrorDecl()->getDeclaredType()->getCanonicalType(); + requireSameType(arg->getType(), + SILType::getPrimitiveObjectType(errorType), + "error successor argument must have Error type"); + + if (AACI->getFunction()->hasOwnership()) { + require(arg->getOwnershipKind() == ValueOwnershipKind::Owned, + "error successor argument must be owned"); + } + } + if (isAddressForm) { + require(AACI->getResumeBB()->getNumArguments() == 0, + "resume successor must take no arguments for get_async_continuation_addr"); + } else { + require(AACI->getResumeBB()->getNumArguments() == 1, + "resume successor must take one argument for get_async_continuation"); + auto arg = AACI->getResumeBB()->getArgument(0); + + requireSameType(arg->getType(), cont->getLoweredResumeType(), + "resume successor must take an argument of the continuation resume type"); + if (AACI->getFunction()->hasOwnership()) { + require(arg->getOwnershipKind() == ValueOwnershipKind::Owned, + "resume successor argument must be owned"); + } + } + } // This verifies that the entry block of a SIL function doesn't have // any predecessors and also verifies the entry point arguments. @@ -4902,6 +4968,8 @@ class SILVerifier : public SILVerifierBase { std::set ActiveOps; CFGState CFG = Normal; + + GetAsyncContinuationInstBase *GotAsyncContinuation = nullptr; }; }; @@ -4909,6 +4977,8 @@ class SILVerifier : public SILVerifierBase { /// /// - stack allocations and deallocations must obey a stack discipline /// - accesses must be uniquely ended + /// - async continuations must be awaited before getting the continuation again, suspending + /// the task, or exiting the function /// - flow-sensitive states must be equivalent on all paths into a block void verifyFlowSensitiveRules(SILFunction *F) { // Do a traversal of the basic blocks. @@ -4924,6 +4994,20 @@ class SILVerifier : public SILVerifierBase { for (SILInstruction &i : *BB) { CurInstruction = &i; + if (i.maySuspend()) { + // Instructions that may suspend an async context must not happen + // while the continuation is being accessed, with the exception of + // the AwaitAsyncContinuationInst that completes suspending the task. + if (auto aaci = dyn_cast(&i)) { + require(state.GotAsyncContinuation == aaci->getOperand(), + "encountered await_async_continuation that doesn't match active gotten continuation"); + state.GotAsyncContinuation = nullptr; + } else { + require(!state.GotAsyncContinuation, + "cannot suspend async task while unawaited continuation is active"); + } + } + if (i.isAllocatingStack()) { state.Stack.push_back(cast(&i)); @@ -4946,13 +5030,18 @@ class SILVerifier : public SILVerifierBase { bool present = state.ActiveOps.erase(beginOp); require(present, "operation has already been ended"); } - + } else if (auto gaci = dyn_cast(&i)) { + require(!state.GotAsyncContinuation, + "get_async_continuation while unawaited continuation is already active"); + state.GotAsyncContinuation = gaci; } else if (auto term = dyn_cast(&i)) { if (term->isFunctionExiting()) { require(state.Stack.empty(), "return with stack allocs that haven't been deallocated"); require(state.ActiveOps.empty(), "return with operations still active"); + require(!state.GotAsyncContinuation, + "return with unawaited async continuation"); if (isa(term)) { require(state.CFG == VerifyFlowSensitiveRulesDetails::YieldUnwind, @@ -4970,12 +5059,14 @@ class SILVerifier : public SILVerifierBase { } } } - + if (isa(term)) { require(state.CFG != VerifyFlowSensitiveRulesDetails::YieldOnceResume, "encountered multiple 'yield's along single path"); require(state.CFG == VerifyFlowSensitiveRulesDetails::Normal, "encountered 'yield' on abnormal CFG path"); + require(!state.GotAsyncContinuation, + "encountered 'yield' while an unawaited continuation is active"); } auto successors = term->getSuccessors(); @@ -5037,6 +5128,8 @@ class SILVerifier : public SILVerifierBase { "inconsistent active-operations sets entering basic block"); require(state.CFG == foundState.CFG, "inconsistent coroutine states entering basic block"); + require(state.GotAsyncContinuation == foundState.GotAsyncContinuation, + "inconsistent active async continuations entering basic block"); } } } From 77584928da319b285a9f37b88ffce41dcc77b9a2 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 9 Oct 2020 15:20:01 -0700 Subject: [PATCH 364/745] [Concurrency] Implement global actor isolation rules. Extend the actor isolation checking rules to account for global actors. For example, a function annotated with a given global actor can invoke synchronous methods from the same global actor, but not from a different global actor or a particular actor instance. Similarly, a method of an (instance) actor that is annotated with a global actor attribute is not part of the (instance) actor and, therefore, cannot operate on its actor-isolated state. --- include/swift/AST/ActorIsolation.h | 40 +++- include/swift/AST/Decl.h | 6 +- include/swift/AST/DiagnosticsSema.def | 19 ++ include/swift/AST/TypeCheckRequests.h | 11 +- include/swift/AST/TypeCheckerTypeIDZone.def | 3 +- include/swift/Basic/CTypeIDZone.def | 2 + include/swift/Basic/DefineTypeIDZone.h | 24 +++ include/swift/Basic/SimpleDisplay.h | 13 +- lib/AST/Decl.cpp | 4 +- lib/AST/TypeCheckRequests.cpp | 17 ++ lib/Sema/TypeCheckAttr.cpp | 8 +- lib/Sema/TypeCheckConcurrency.cpp | 194 ++++++++++++++++++-- lib/Sema/TypeCheckDeclObjC.cpp | 2 + lib/Sema/TypeCheckProtocol.cpp | 6 + test/Concurrency/actor_isolation.swift | 83 ++++++++- test/attr/global_actor.swift | 7 + 16 files changed, 408 insertions(+), 31 deletions(-) diff --git a/include/swift/AST/ActorIsolation.h b/include/swift/AST/ActorIsolation.h index 2de16ff3fe547..0d4f44d855cc7 100644 --- a/include/swift/AST/ActorIsolation.h +++ b/include/swift/AST/ActorIsolation.h @@ -24,6 +24,11 @@ class raw_ostream; namespace swift { class ClassDecl; +class Type; + +/// Determine whether the given types are (canonically) equal, declared here +/// to avoid having to include Types.h. +bool areTypesEqual(Type type1, Type type2); /// Describes the actor isolation of a given declaration, which determines /// the actors with which it can interact. @@ -38,19 +43,31 @@ class ActorIsolation { /// the actor is isolated to the instance of that actor. ActorInstance, /// The declaration can refer to actor-isolated state, but can also be - //// referenced from outside the actor. + /// referenced from outside the actor. ActorPrivileged, /// The declaration is explicitly specified to be independent of any actor, /// meaning that it can be used from any actor but is also unable to /// refer to the isolated state of any given actor. Independent, + /// The declaration is isolated to a global actor. It can refer to other + /// entities with the same global actor. + GlobalActor, + /// The declaration can refer to state isolated by the given global actor, + /// and can be used from anywhere. + GlobalActorPrivileged, }; private: Kind kind; - ClassDecl *actor; + union { + ClassDecl *actor; + Type globalActor; + void *pointer; + }; ActorIsolation(Kind kind, ClassDecl *actor) : kind(kind), actor(actor) { } + ActorIsolation(Kind kind, Type globalActor) + : kind(kind), globalActor(globalActor) { } public: static ActorIsolation forUnspecified() { @@ -69,6 +86,14 @@ class ActorIsolation { return ActorIsolation(ActorInstance, actor); } + static ActorIsolation forGlobalActorPrivileged(Type globalActor) { + return ActorIsolation(GlobalActorPrivileged, globalActor); + } + + static ActorIsolation forGlobalActor(Type globalActor) { + return ActorIsolation(GlobalActor, globalActor); + } + Kind getKind() const { return kind; } operator Kind() const { return getKind(); } @@ -78,6 +103,11 @@ class ActorIsolation { return actor; } + Type getGlobalActor() const { + assert(getKind() == GlobalActor || getKind() == GlobalActorPrivileged); + return globalActor; + } + friend bool operator==(const ActorIsolation &lhs, const ActorIsolation &rhs) { if (lhs.kind != rhs.kind) @@ -91,6 +121,10 @@ class ActorIsolation { case ActorInstance: case ActorPrivileged: return lhs.actor == rhs.actor; + + case GlobalActor: + case GlobalActorPrivileged: + return areTypesEqual(lhs.globalActor, rhs.globalActor); } } @@ -100,7 +134,7 @@ class ActorIsolation { } friend llvm::hash_code hash_value(const ActorIsolation &state) { - return llvm::hash_combine(state.kind, state.actor); + return llvm::hash_combine(state.kind, state.pointer); } }; diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index be81585971c92..850a3f0b246d7 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -957,8 +957,10 @@ class alignas(1 << DeclAlignInBits) Decl { /// if any. /// /// This is the "raw" global actor attribute as written directly on the - /// declaration, with any inference rules applied. - CustomAttr *getGlobalActorAttr() const; + /// declaration, along with the nominal type declaration to which it refers, + /// without any inference rules applied. + Optional> + getGlobalActorAttr() const; /// If an alternative module name is specified for this decl, e.g. using /// @_originalDefinedIn attribute, this function returns this module name. diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 14fbb765f79d7..13272e13a4858 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4153,6 +4153,21 @@ ERROR(actor_isolated_self_independent_context,none, "actor-isolated %0 %1 can not be referenced from an " "'@actorIndependent' context", (DescriptiveDeclKind, DeclName)) +ERROR(actor_isolated_global_actor_context,none, + "actor-isolated %0 %1 can not be referenced from context of global " + "actor %2", + (DescriptiveDeclKind, DeclName, Type)) +ERROR(global_actor_from_instance_actor_context,none, + "%0 %1 isolated to global actor %2 can not be referenced from actor %3", + (DescriptiveDeclKind, DeclName, Type, DeclName)) +ERROR(global_actor_from_other_global_actor_context,none, + "%0 %1 isolated to global actor %2 can not be referenced from " + "different global actor %3", + (DescriptiveDeclKind, DeclName, Type, Type)) +ERROR(global_actor_from_independent_context,none, + "%0 %1 isolated to global actor %2 can not be referenced from an " + "'@actorIndependent' context", + (DescriptiveDeclKind, DeclName, Type)) ERROR(actor_isolated_partial_apply,none, "actor-isolated %0 %1 can not be partially applied", (DescriptiveDeclKind, DeclName)) @@ -4231,6 +4246,10 @@ ERROR(global_actor_on_actor_class,none, ERROR(global_actor_on_local_variable,none, "local variable %0 cannot have a global actor", (DeclName)) +ERROR(actor_isolation_multiple_attr,none, + "%0 %1 has multiple actor-isolation attributes ('%2' and '%3')", + (DescriptiveDeclKind, DeclName, StringRef, StringRef)) + //------------------------------------------------------------------------------ // MARK: Type Check Types //------------------------------------------------------------------------------ diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 8158673d867fe..88434b3103a18 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -880,15 +880,18 @@ class GlobalActorInstanceRequest : bool isCached() const { return true; } }; +using CustomAttrNominalPair = std::pair; + /// Request the custom attribute which denotes the global actor for the given /// declaration. /// /// This is the "raw" global actor attribute as written directly on the /// declaration, with any inference rules applied. class GlobalActorAttributeRequest : - public SimpleRequest { + public SimpleRequest< + GlobalActorAttributeRequest, + Optional(Decl *), + RequestFlags::Cached> { public: using SimpleRequest::SimpleRequest; @@ -896,7 +899,7 @@ class GlobalActorAttributeRequest : friend SimpleRequest; // Evaluation. - CustomAttr * + Optional> evaluate(Evaluator &evaluator, Decl *decl) const; public: diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index b60acafcddab4..aca7d77c08710 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -91,7 +91,8 @@ SWIFT_REQUEST(TypeChecker, GlobalActorInstanceRequest, VarDecl *(NominalTypeDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, GlobalActorAttributeRequest, - CustomAttr *(Decl *), Cached, NoLocationInfo) + Optional(Decl *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ActorIsolationRequest, ActorIsolationState(ValueDecl *), Cached, NoLocationInfo) diff --git a/include/swift/Basic/CTypeIDZone.def b/include/swift/Basic/CTypeIDZone.def index 007fa187389d4..51aa9bd8ceb61 100644 --- a/include/swift/Basic/CTypeIDZone.def +++ b/include/swift/Basic/CTypeIDZone.def @@ -38,7 +38,9 @@ SWIFT_TYPEID_NAMED(std::string, String) SWIFT_TYPEID_NAMED(evaluator::SideEffect, SideEffect) SWIFT_TYPEID_TEMPLATE1_NAMED(std::vector, Vector, typename T, T) SWIFT_TYPEID_TEMPLATE1_NAMED(std::unique_ptr, UniquePtr, typename T, T) +SWIFT_TYPEID_TEMPLATE2_NAMED(std::pair, Pair, typename T1, T1, typename T2, T2) // LLVM ADT types. SWIFT_TYPEID_TEMPLATE1_NAMED(llvm::TinyPtrVector, TinyPtrVector, typename T, T) SWIFT_TYPEID_TEMPLATE1_NAMED(llvm::ArrayRef, ArrayRef, typename T, T) +SWIFT_TYPEID_TEMPLATE1_NAMED(llvm::Optional, Optional, typename T, T) diff --git a/include/swift/Basic/DefineTypeIDZone.h b/include/swift/Basic/DefineTypeIDZone.h index 78779abdc7c9d..315a42a0aa9ed 100644 --- a/include/swift/Basic/DefineTypeIDZone.h +++ b/include/swift/Basic/DefineTypeIDZone.h @@ -40,9 +40,11 @@ template<> struct TypeIDZoneTypes { enum Types : uint8_t { #define SWIFT_TYPEID_NAMED(Type, Name) Name, #define SWIFT_TYPEID_TEMPLATE1_NAMED(Template, Name, Param1, Arg1) Name, +#define SWIFT_TYPEID_TEMPLATE2_NAMED(Template, Name, Param1, Arg1, Param2, Arg2) Name, #include SWIFT_TYPEID_HEADER #undef SWIFT_TYPEID_NAMED #undef SWIFT_TYPEID_TEMPLATE1_NAMED +#undef SWIFT_TYPEID_TEMPLATE2_NAMED }; }; @@ -77,12 +79,34 @@ public: \ \ template const uint64_t TypeID>::value; +#define SWIFT_TYPEID_TEMPLATE2_NAMED(Template, Name, Param1, Arg1, Param2, Arg2) \ +template struct TypeID> { \ +private: \ + static const uint64_t templateID = \ + formTypeID(static_cast(Zone::SWIFT_TYPEID_ZONE), \ + TypeIDZoneTypes::Name); \ + \ +public: \ + static const uint64_t value = \ + (TypeID::value << 32) | \ + (TypeID::value << 16) | \ + templateID; \ + \ + static std::string getName() { \ + return std::string(#Name) + "<" + TypeID::getName() + \ + ", " + TypeID::getName() + ">"; \ + } \ +}; \ + \ +template const uint64_t TypeID>::value; + #include SWIFT_TYPEID_HEADER #undef SWIFT_REQUEST #undef SWIFT_TYPEID_NAMED #undef SWIFT_TYPEID_TEMPLATE1_NAMED +#undef SWIFT_TYPEID_TEMPLATE2_NAMED #undef SWIFT_TYPEID #undef SWIFT_TYPEID_ZONE diff --git a/include/swift/Basic/SimpleDisplay.h b/include/swift/Basic/SimpleDisplay.h index 3a31da14654ac..349da1db5f661 100644 --- a/include/swift/Basic/SimpleDisplay.h +++ b/include/swift/Basic/SimpleDisplay.h @@ -23,6 +23,7 @@ #include "llvm/Support/raw_ostream.h" #include #include +#include namespace swift { template @@ -93,7 +94,17 @@ namespace swift { const std::tuple &value) { simple_display_tuple<0>(out, value); } - + + template + void simple_display(llvm::raw_ostream &out, + const std::pair &value) { + out << "("; + simple_display(out, value.first); + out << ", "; + simple_display(out, value.second); + out << ")"; + } + template void simple_display(llvm::raw_ostream &out, const llvm::TinyPtrVector &vector) { diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index c96dfc23ce203..b8a5a4ce7c04c 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -672,12 +672,12 @@ static_assert(sizeof(checkSourceLocType(&ID##Decl::getLoc)) == 2, \ llvm_unreachable("invalid file kind"); } -CustomAttr *Decl::getGlobalActorAttr() const { +Optional Decl::getGlobalActorAttr() const { auto &ctx = getASTContext(); auto mutableThis = const_cast(this); return evaluateOrDefault(ctx.evaluator, GlobalActorAttributeRequest{mutableThis}, - nullptr); + None); } Expr *AbstractFunctionDecl::getSingleExpressionBody() const { diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index da08ab9057d25..4379fd202a80c 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1466,9 +1466,26 @@ void swift::simple_display( case ActorIsolation::Unspecified: out << "unspecified actor isolation"; break; + + case ActorIsolation::GlobalActor: + out << "actor-isolated to global actor " + << state.getGlobalActor().getString(); + break; + + case ActorIsolation::GlobalActorPrivileged: + out << "actor-privileged to global actor " + << state.getGlobalActor().getString(); + break; } } +bool swift::areTypesEqual(Type type1, Type type2) { + if (!type1 || !type2) + return !type1 && !type2; + + return type1->isEqual(type2); +} + void swift::simple_display( llvm::raw_ostream &out, BodyInitKind initKind) { switch (initKind) { diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index fddbfa77847de..d5e36117c2e83 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "MiscDiagnostics.h" +#include "TypeCheckConcurrency.h" #include "TypeCheckObjC.h" #include "TypeCheckType.h" #include "TypeChecker.h" @@ -317,11 +318,14 @@ class AttributeChecker : public AttributeVisitor { return; } - if (!cast(D)->isInstanceMember()) { + auto VD = cast(D); + if (!VD->isInstanceMember()) { diagnoseAndRemoveAttr( attr, diag::actorisolated_not_actor_instance_member); return; } + + (void)getActorIsolation(VD); } void visitGlobalActorAttr(GlobalActorAttr *attr) { @@ -3069,6 +3073,8 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { // retrieval request perform checking for us. if (nominal->isGlobalActor()) { (void)D->getGlobalActorAttr(); + if (auto value = dyn_cast(D)) + (void)getActorIsolation(value); return; } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 0735e590202a4..e409cd4ef22e6 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -15,7 +15,9 @@ //===----------------------------------------------------------------------===// #include "TypeCheckConcurrency.h" #include "TypeChecker.h" +#include "TypeCheckType.h" #include "swift/AST/ASTWalker.h" +#include "swift/AST/Initializer.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/NameLookupRequests.h" @@ -328,7 +330,8 @@ VarDecl *GlobalActorInstanceRequest::evaluate( return nullptr; } -CustomAttr *GlobalActorAttributeRequest::evaluate( +Optional> +GlobalActorAttributeRequest::evaluate( Evaluator &evaluator, Decl *decl) const { ASTContext &ctx = decl->getASTContext(); auto dc = decl->getDeclContext(); @@ -363,7 +366,7 @@ CustomAttr *GlobalActorAttributeRequest::evaluate( } if (!globalActorAttr) - return nullptr; + return None; // Check that a global actor attribute makes sense on this kind of // declaration. @@ -374,7 +377,7 @@ CustomAttr *GlobalActorAttributeRequest::evaluate( // ... except for actor classes. nominal->diagnose(diag::global_actor_on_actor_class, nominal->getName()) .highlight(globalActorAttr->getRangeWithAt()); - return nullptr; + return None; } } } else if (auto storage = dyn_cast(decl)) { @@ -383,7 +386,7 @@ CustomAttr *GlobalActorAttributeRequest::evaluate( if (var->getDeclContext()->isLocalContext()) { var->diagnose(diag::global_actor_on_local_variable, var->getName()) .highlight(globalActorAttr->getRangeWithAt()); - return nullptr; + return None; } } } else if (isa(decl)) { @@ -393,10 +396,10 @@ CustomAttr *GlobalActorAttributeRequest::evaluate( } else { // Everything else is disallowed. decl->diagnose(diag::global_actor_disallowed, decl->getDescriptiveKind()); - return nullptr; + return None; } - return globalActorAttr; + return std::make_pair(globalActorAttr, globalActorNominal); } namespace { @@ -417,9 +420,12 @@ class IsolationRestriction { /// data races. The context in which the local was defined is provided. LocalCapture, - /// References to this local variable that can only be made from the /// References to this member of an actor are only permitted on 'self'. ActorSelf, + + /// References to a declaration that is part of a global actor are only + /// permitted from other declarations with that same global actor. + GlobalActor, }; private: @@ -432,6 +438,9 @@ class IsolationRestriction { /// The actor class that the entity is declared in. ClassDecl *actorClass; + + /// The global actor type. + TypeBase *globalActor; } data; explicit IsolationRestriction(Kind kind) : kind(kind) { } @@ -451,6 +460,12 @@ class IsolationRestriction { return data.actorClass; } + /// Retrieve the actor class that the declaration is within. + Type getGlobalActor() const { + assert(kind == GlobalActor); + return Type(data.globalActor); + } + /// There are no restrictions on the use of the entity. static IsolationRestriction forUnrestricted() { return IsolationRestriction(Unrestricted); @@ -476,6 +491,14 @@ class IsolationRestriction { return result; } + /// Accesses to the given declaration can only be made via this particular + /// global actor. + static IsolationRestriction forGlobalActor(Type globalActor) { + IsolationRestriction result(GlobalActor); + result.data.globalActor = globalActor.getPointer(); + return result; + } + /// Determine the isolation rules for a given declaration. static IsolationRestriction forDeclaration(Decl *decl) { switch (decl->getKind()) { @@ -538,8 +561,12 @@ class IsolationRestriction { // Protected actor instance members can only be accessed on 'self'. return forActorSelf(isolation.getActor()); + case ActorIsolation::GlobalActor: + return forGlobalActor(isolation.getGlobalActor()); + case ActorIsolation::Independent: case ActorIsolation::ActorPrivileged: + case ActorIsolation::GlobalActorPrivileged: // Actor-independent and actor-privileged declarations have no // restrictions on their access. return forUnrestricted(); @@ -712,6 +739,8 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { /// Note that the given actor member is isolated. static void noteIsolatedActorMember(ValueDecl *decl) { + // FIXME: Make this diagnostic more sensitive to the isolation context + // of the declaration. if (auto func = dyn_cast(decl)) { // FIXME: We'd like to insert 'async' at the appropriate place, but // FuncDecl/AbstractFunctionDecl doesn't have the right source-location @@ -793,6 +822,87 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { return true; } + /// Get the actor isolation of the innermost relevant context. + ActorIsolation getInnermostIsolatedContext(const DeclContext *constDC) { + auto dc = const_cast(constDC); + while (!dc->isModuleScopeContext()) { + // Look through non-escaping closures. + if (auto closure = dyn_cast(dc)) { + if (auto type = closure->getType()) { + if (auto fnType = type->getAs()) { + if (fnType->isNoEscape()) { + dc = closure->getParent(); + continue; + } + } + } + } + + // Functions have actor isolation defined on them. + if (auto func = dyn_cast(dc)) + return getActorIsolation(func); + + // Subscripts have actor isolation defined on them. + if (auto subscript = dyn_cast(dc)) + return getActorIsolation(subscript); + + // Pattern binding declarations have actor isolation defined on their + // properties, if any. + if (auto init = dyn_cast(dc)) { + auto var = init->getBinding()->getAnchoringVarDecl( + init->getBindingIndex()); + if (var) + return getActorIsolation(var); + + return ActorIsolation::forUnspecified(); + } + + return ActorIsolation::forUnspecified(); + } + + return ActorIsolation::forIndependent(); + } + + /// Check a reference to an entity within a global actor. + bool checkGlobalActorReference( + ValueDecl *value, SourceLoc loc, Type globalActor) { + switch (auto contextIsolation = + getInnermostIsolatedContext(getDeclContext())) { + case ActorIsolation::ActorInstance: + case ActorIsolation::ActorPrivileged: + ctx.Diags.diagnose( + loc, diag::global_actor_from_instance_actor_context, + value->getDescriptiveKind(), value->getName(), globalActor, + contextIsolation.getActor()->getName()); + noteIsolatedActorMember(value); + return true; + + case ActorIsolation::GlobalActor: + case ActorIsolation::GlobalActorPrivileged: + // If the global actor types are the same, we're done. + if (contextIsolation.getGlobalActor()->isEqual(globalActor)) + return false; + + ctx.Diags.diagnose( + loc, diag::global_actor_from_other_global_actor_context, + value->getDescriptiveKind(), value->getName(), globalActor, + contextIsolation.getGlobalActor()); + noteIsolatedActorMember(value); + return true; + + case ActorIsolation::Independent: + ctx.Diags.diagnose( + loc, diag::global_actor_from_independent_context, + value->getDescriptiveKind(), value->getName(), globalActor); + noteIsolatedActorMember(value); + return true; + + case ActorIsolation::Unspecified: + // Okay. + return false; + } + } + /// Check a reference to a local or global. bool checkNonMemberReference(ValueDecl *value, SourceLoc loc) { if (!value) @@ -805,6 +915,10 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { case IsolationRestriction::ActorSelf: llvm_unreachable("non-member reference into an actor"); + case IsolationRestriction::GlobalActor: + return checkGlobalActorReference( + value, loc, isolation.getGlobalActor()); + case IsolationRestriction::LocalCapture: // Only diagnose unsafe concurrent accesses within the context of an // actor. This is globally unsafe, but locally enforceable. @@ -856,7 +970,7 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { } // Check whether the context of 'self' is actor-isolated. - switch (getActorIsolation( + switch (auto contextIsolation = getActorIsolation( cast(selfVar->getDeclContext()->getAsDecl()))) { case ActorIsolation::ActorInstance: case ActorIsolation::ActorPrivileged: @@ -885,6 +999,18 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { member->getName()); noteIsolatedActorMember(member); return true; + + case ActorIsolation::GlobalActor: + case ActorIsolation::GlobalActorPrivileged: + // The 'self' is for a member that's part of a global actor, which + // means we cannot refer to actor-isolated state. + ctx.Diags.diagnose( + memberLoc, diag::actor_isolated_global_actor_context, + member->getDescriptiveKind(), + member->getName(), + contextIsolation.getGlobalActor()); + noteIsolatedActorMember(member); + return true; } // Check whether we are in a context that will not execute concurrently @@ -902,6 +1028,10 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { return false; } + case IsolationRestriction::GlobalActor: + return checkGlobalActorReference( + member, memberLoc, isolation.getGlobalActor()); + case IsolationRestriction::LocalCapture: llvm_unreachable("Locals cannot be referenced with member syntax"); @@ -917,10 +1047,50 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { ActorIsolation ActorIsolationRequest::evaluate( Evaluator &evaluator, ValueDecl *value) const { - // If the attribute is explicitly marked @actorIndependent, report it as - // independent. - if (value->getAttrs().hasAttribute()) { - return ActorIsolation::forIndependent(); + // Look up attributes on the declaration that can affect its actor isolation. + // If any of them are present, use that attribute. + auto independentAttr = value->getAttrs().getAttribute(); + auto globalActorAttr = value->getGlobalActorAttr(); + unsigned numIsolationAttrs = + (independentAttr ? 1 : 0) + (globalActorAttr ? 1 : 0); + if (numIsolationAttrs > 0) { + // Only one such attribute is valid. + if (numIsolationAttrs > 1) { + value->diagnose( + diag::actor_isolation_multiple_attr, value->getDescriptiveKind(), + value->getName(), independentAttr->getAttrName(), + globalActorAttr->second->getName().str()) + .highlight(independentAttr->getRangeWithAt()) + .highlight(globalActorAttr->first->getRangeWithAt()); + } + + // If the declaration is explicitly marked @actorIndependent, report it as + // independent. + if (independentAttr) { + return ActorIsolation::forIndependent(); + } + + // If the declaration is marked with a global actor, report it as being + // part of that global actor. + if (globalActorAttr) { + TypeResolutionOptions options(TypeResolverContext::None); + TypeResolution resolver = TypeResolution::forInterface( + value->getInnermostDeclContext(), options, nullptr); + Type globalActorType = resolver.resolveType( + globalActorAttr->first->getTypeRepr(), nullptr); + if (!globalActorType || globalActorType->hasError()) + return ActorIsolation::forUnspecified(); + + // A function that is an asynchronous context is actor-privileged. + if (auto func = dyn_cast(value)) { + if (func->isAsyncContext()) + return ActorIsolation::forGlobalActorPrivileged(globalActorType); + } + + return ActorIsolation::forGlobalActor(globalActorType); + } + + llvm_unreachable("Forgot about an attribute?"); } // If the declaration overrides another declaration, it must have the same diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index aceefa5e695e7..1fcb52b2b79d4 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -389,6 +389,8 @@ static bool checkObjCActorIsolation(const ValueDecl *VD, return true; case ActorIsolation::ActorPrivileged: + case ActorIsolation::GlobalActor: + case ActorIsolation::GlobalActorPrivileged: case ActorIsolation::Independent: case ActorIsolation::Unspecified: return false; diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 89030a79f4321..3c027264d78c8 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4359,7 +4359,13 @@ void ConformanceChecker::resolveValueWitnesses() { return; } + case ActorIsolation::GlobalActor: { + // FIXME: Check against the requirement. This needs serious refactoring. + break; + } + case ActorIsolation::ActorPrivileged: + case ActorIsolation::GlobalActorPrivileged: case ActorIsolation::Independent: case ActorIsolation::Unspecified: break; diff --git a/test/Concurrency/actor_isolation.swift b/test/Concurrency/actor_isolation.swift index 3162f37ec53c8..c27ded3135e3f 100644 --- a/test/Concurrency/actor_isolation.swift +++ b/test/Concurrency/actor_isolation.swift @@ -1,4 +1,7 @@ // RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// REQUIRES: concurrency + +import _Concurrency let immutableGlobal: String = "hello" var mutableGlobal: String = "can't touch this" // expected-note 2{{var declared here}} @@ -14,24 +17,24 @@ func acceptEscapingAsyncClosure(_: @escaping () async -> T) { } // Actor state isolation restrictions // ---------------------------------------------------------------------- actor class MySuperActor { - var superState: Int = 25 + var superState: Int = 25 // expected-note {{mutable state is only available within the actor instance}} - func superMethod() { } // expected-note 2 {{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} + func superMethod() { } // expected-note 3 {{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} func superAsyncMethod() async { } - subscript (index: Int) -> String { // expected-note{{subscript declared here}} + subscript (index: Int) -> String { // expected-note 3{{subscript declared here}} "\(index)" } } actor class MyActor: MySuperActor { let immutable: Int = 17 - var text: [String] = [] // expected-note 6{{mutable state is only available within the actor instance}} + var text: [String] = [] // expected-note 9{{mutable state is only available within the actor instance}} class func synchronousClass() { } static func synchronousStatic() { } - func synchronous() -> String { text.first ?? "nothing" } // expected-note 18{{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} + func synchronous() -> String { text.first ?? "nothing" } // expected-note 21{{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} func asynchronous() async -> String { synchronous() } } @@ -60,6 +63,9 @@ extension MyActor { _ = otherActor.actorIndependentVar otherActor.actorIndependentVar = 17 + // Global actors + syncGlobalActorFunc() /// expected-error{{global function 'syncGlobalActorFunc()' isolated to global actor 'SomeGlobalActor' can not be referenced from an '@actorIndependent' context}} + // Global data is okay if it is immutable. _ = immutableGlobal _ = mutableGlobal // expected-warning{{reference to var 'mutableGlobal' is not concurrency-safe because it involves shared mutable state}} @@ -113,6 +119,10 @@ extension MyActor { Self.synchronousClass() Self.synchronousStatic() + // Global actors + syncGlobalActorFunc() // expected-error{{global function 'syncGlobalActorFunc()' isolated to global actor 'SomeGlobalActor' can not be referenced from actor 'MyActor'}} + await asyncGlobalActorFunc() + // Closures. let localConstant = 17 var localVar = 17 // expected-note 3{{var declared here}} @@ -167,6 +177,69 @@ extension MyActor { } } +// ---------------------------------------------------------------------- +// Global actor isolation restrictions +// ---------------------------------------------------------------------- +actor class SomeActor { } + +@globalActor +struct SomeGlobalActor { + static let shared = SomeActor() +} + +@globalActor +struct SomeOtherGlobalActor { + static let shared = SomeActor() +} + +@SomeGlobalActor func syncGlobalActorFunc() { } // expected-note 3{{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} +@SomeGlobalActor func asyncGlobalActorFunc() async { } + +@SomeOtherGlobalActor func syncOtherGlobalActorFunc() { } // expected-note {{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} + +@SomeOtherGlobalActor func asyncOtherGlobalActorFunc() async { + syncGlobalActorFunc() // expected-error{{global function 'syncGlobalActorFunc()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'SomeOtherGlobalActor'}} + await asyncGlobalActorFunc() +} + +extension MyActor { + @SomeGlobalActor func onGlobalActor(otherActor: MyActor) async { + // Access to other functions in this actor are okay. + syncGlobalActorFunc() + await asyncGlobalActorFunc() + + // Other global actors are not okay + syncOtherGlobalActorFunc() // expected-error{{global function 'syncOtherGlobalActorFunc()' isolated to global actor 'SomeOtherGlobalActor' can not be referenced from different global actor 'SomeGlobalActor'}} + await asyncOtherGlobalActorFunc() + + _ = immutable + _ = synchronous() // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from context of global actor 'SomeGlobalActor'}} + _ = text[0] // expected-error{{actor-isolated property 'text' can not be referenced from context of global actor 'SomeGlobalActor'}} + + // Accesses on 'self' are only okay for immutable and asynchronous, because + // we are outside of the actor instance. + _ = self.immutable + _ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from context of global actor 'SomeGlobalActor'}} + _ = await self.asynchronous() + _ = self.text[0] // expected-error{{actor-isolated property 'text' can not be referenced from context of global actor 'SomeGlobalActor'}} + _ = self[0] // expected-error{{actor-isolated subscript 'subscript(_:)' can not be referenced from context of global actor 'SomeGlobalActor'}} + + // Accesses on 'super' are not okay; we're outside of the actor. + _ = super.superState // expected-error{{actor-isolated property 'superState' can not be referenced from context of global actor 'SomeGlobalActor'}} + super.superMethod() // expected-error{{actor-isolated instance method 'superMethod()' can not be referenced from context of global actor 'SomeGlobalActor'}} + await super.superAsyncMethod() + _ = super[0] // expected-error{{actor-isolated subscript 'subscript(_:)' can not be referenced from context of global actor 'SomeGlobalActor'}} + + // Accesses on other actors can only reference immutable data or + // call asychronous methods + _ = otherActor.immutable // okay + _ = otherActor.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' can only be referenced on 'self'}} + _ = await otherActor.asynchronous() + _ = otherActor.text[0] // expected-error{{actor-isolated property 'text' can only be referenced on 'self'}} + } +} + + // ---------------------------------------------------------------------- // Non-actor code isolation restrictions // ---------------------------------------------------------------------- diff --git a/test/attr/global_actor.swift b/test/attr/global_actor.swift index 52d027b1ca1c1..dc797c6f239e2 100644 --- a/test/attr/global_actor.swift +++ b/test/attr/global_actor.swift @@ -81,3 +81,10 @@ class SomeClass { @GA1 actor class ActorInTooManyPlaces { } // expected-error{{actor class 'ActorInTooManyPlaces' cannot have a global actor}} @GA1 @OtherGlobalActor func twoGlobalActors() { } // expected-error{{declaration can not have multiple global actor attributes ('OtherGlobalActor' and 'GA1')}} + +// ----------------------------------------------------------------------- +// Redundant attributes +// ----------------------------------------------------------------------- +extension SomeActor { + @GA1 @actorIndependent func conflict1() { } // expected-error{{instance method 'conflict1()' has multiple actor-isolation attributes ('actorIndependent' and 'GA1')}} +} From 0ba4e338d41700b2a3f8cee2fb0c0d0e9b5d8d02 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 9 Oct 2020 15:26:31 -0700 Subject: [PATCH 365/745] [NFC] Lambda-fy an Assertion --- lib/Sema/DerivedConformanceRawRepresentable.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/Sema/DerivedConformanceRawRepresentable.cpp b/lib/Sema/DerivedConformanceRawRepresentable.cpp index b8ecb3e61c3fa..cc19e13ac6200 100644 --- a/lib/Sema/DerivedConformanceRawRepresentable.cpp +++ b/lib/Sema/DerivedConformanceRawRepresentable.cpp @@ -401,13 +401,15 @@ deriveRawRepresentable_init(DerivedConformance &derived) { auto rawInterfaceType = enumDecl->getRawType(); auto rawType = parentDC->mapTypeIntoContext(rawInterfaceType); - auto equatableProto = TypeChecker::getProtocol(C, enumDecl->getLoc(), - KnownProtocolKind::Equatable); - assert(equatableProto); - assert( - TypeChecker::conformsToProtocol(rawType, equatableProto, enumDecl)); - (void)equatableProto; - (void)rawType; + + assert([&]() -> bool { + auto equatableProto = TypeChecker::getProtocol(C, enumDecl->getLoc(), + KnownProtocolKind::Equatable); + if (!equatableProto) { + return false; + } + return !TypeChecker::conformsToProtocol(rawType, equatableProto, enumDecl).isInvalid(); + }()); auto *rawDecl = new (C) ParamDecl(SourceLoc(), SourceLoc(), From 3cc1dbb63597745509f346fcc2c38d5dfea73a67 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 9 Oct 2020 15:28:28 -0700 Subject: [PATCH 366/745] Drop a Needless Reset of An Inherited Type For enums with broken raw representable bounds, we should aim to make derivation tolerant of the error state rather than work around it in TypeCheckDeclPrimary. This is the last user of the MutableArrayRef form of the inheritance clauses. Those will be removed in the subsequent commit. --- lib/Sema/TypeCheckDeclPrimary.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index adc9c837df6c3..d578c7a6c42aa 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1915,7 +1915,6 @@ class DeclChecker : public DeclVisitor { if (!rawTy->is()) { DE.diagnose(ED->getInherited().front().getSourceRange().Start, diag::raw_type_not_literal_convertible, rawTy); - ED->getInherited().front().setType(ErrorType::get(getASTContext())); } } From ff8d5bc2c8347771061bb1a3ff303225e8f7ac1f Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 9 Oct 2020 15:29:55 -0700 Subject: [PATCH 367/745] [NFC] MutableArrayRef -> ArrayRef The first step on the road to splitting the semantic type information here from the syntactic information in the other half of the TypeLoc. --- include/swift/AST/Decl.h | 50 ++++++++------------------ lib/AST/Decl.cpp | 18 +++++----- lib/IRGen/GenClass.cpp | 2 +- lib/Sema/DerivedConformanceCodable.cpp | 2 +- 4 files changed, 24 insertions(+), 48 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 8e899934bc1b4..45b13499f85b0 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -1193,7 +1193,7 @@ class ExtensionDecl final : public GenericContext, public Decl, /// extended nominal. llvm::PointerIntPair ExtendedNominal; - MutableArrayRef Inherited; + ArrayRef Inherited; /// The next extension in the linked list of extensions. /// @@ -1212,7 +1212,7 @@ class ExtensionDecl final : public GenericContext, public Decl, friend class IterableDeclContext; ExtensionDecl(SourceLoc extensionLoc, TypeRepr *extendedType, - MutableArrayRef inherited, + ArrayRef inherited, DeclContext *parent, TrailingWhereClause *trailingWhereClause); @@ -1237,7 +1237,7 @@ class ExtensionDecl final : public GenericContext, public Decl, /// Create a new extension declaration. static ExtensionDecl *create(ASTContext &ctx, SourceLoc extensionLoc, TypeRepr *extendedType, - MutableArrayRef inherited, + ArrayRef inherited, DeclContext *parent, TrailingWhereClause *trailingWhereClause, ClangNode clangNode = ClangNode()); @@ -1289,10 +1289,9 @@ class ExtensionDecl final : public GenericContext, public Decl, /// Retrieve the set of protocols that this type inherits (i.e, /// explicitly conforms to). - MutableArrayRef getInherited() { return Inherited; } ArrayRef getInherited() const { return Inherited; } - void setInherited(MutableArrayRef i) { Inherited = i; } + void setInherited(ArrayRef i) { Inherited = i; } bool hasDefaultAccessLevel() const { return Bits.ExtensionDecl.DefaultAndMaxAccessLevel != 0; @@ -2405,12 +2404,12 @@ class ValueDecl : public Decl { /// This is a common base class for declarations which declare a type. class TypeDecl : public ValueDecl { - MutableArrayRef Inherited; + ArrayRef Inherited; protected: TypeDecl(DeclKind K, llvm::PointerUnion context, Identifier name, SourceLoc NameLoc, - MutableArrayRef inherited) : + ArrayRef inherited) : ValueDecl(K, context, name, NameLoc), Inherited(inherited) {} public: @@ -2428,10 +2427,9 @@ class TypeDecl : public ValueDecl { /// Retrieve the set of protocols that this type inherits (i.e, /// explicitly conforms to). - MutableArrayRef getInherited() { return Inherited; } ArrayRef getInherited() const { return Inherited; } - void setInherited(MutableArrayRef i) { Inherited = i; } + void setInherited(ArrayRef i) { Inherited = i; } static bool classof(const Decl *D) { return D->getKind() >= DeclKind::First_TypeDecl && @@ -2456,7 +2454,7 @@ class GenericTypeDecl : public GenericContext, public TypeDecl { public: GenericTypeDecl(DeclKind K, DeclContext *DC, Identifier name, SourceLoc nameLoc, - MutableArrayRef inherited, + ArrayRef inherited, GenericParamList *GenericParams); // Resolve ambiguity due to multiple base classes. @@ -2982,7 +2980,7 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { NominalTypeDecl(DeclKind K, DeclContext *DC, Identifier name, SourceLoc NameLoc, - MutableArrayRef inherited, + ArrayRef inherited, GenericParamList *GenericParams) : GenericTypeDecl(K, DC, name, NameLoc, inherited, GenericParams), IterableDeclContext(IterableDeclContextKind::NominalTypeDecl) @@ -3224,34 +3222,14 @@ class EnumDecl final : public NominalTypeDecl { // Is the complete set of raw values type checked? HasFixedRawValuesAndTypes = 1 << 2, }; - - struct { - /// The raw type and a bit to indicate whether the - /// raw was computed yet or not. - llvm::PointerIntPair> RawTypeAndFlags; - - bool hasRawType() const { - return RawTypeAndFlags.getInt().contains(HasComputedRawType); - } - void cacheRawType(Type ty) { - auto flags = RawTypeAndFlags.getInt() | HasComputedRawType; - RawTypeAndFlags.setPointerAndInt(ty, flags); - } - - bool hasFixedRawValues() const { - return RawTypeAndFlags.getInt().contains(HasFixedRawValues); - } - bool hasCheckedRawValues() const { - return RawTypeAndFlags.getInt().contains(HasFixedRawValuesAndTypes); - } - } LazySemanticInfo; + OptionSet SemanticFlags; friend class EnumRawValuesRequest; friend class EnumRawTypeRequest; public: EnumDecl(SourceLoc EnumLoc, Identifier Name, SourceLoc NameLoc, - MutableArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *DC); SourceLoc getStartLoc() const { return EnumLoc; } @@ -3387,7 +3365,7 @@ class StructDecl final : public NominalTypeDecl { public: StructDecl(SourceLoc StructLoc, Identifier Name, SourceLoc NameLoc, - MutableArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *DC); SourceLoc getStartLoc() const { return StructLoc; } @@ -3526,7 +3504,7 @@ class ClassDecl final : public NominalTypeDecl { public: ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc, - MutableArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *DC); SourceLoc getStartLoc() const { return ClassLoc; } @@ -3907,7 +3885,7 @@ class ProtocolDecl final : public NominalTypeDecl { public: ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc, SourceLoc NameLoc, - Identifier Name, MutableArrayRef Inherited, + Identifier Name, ArrayRef Inherited, TrailingWhereClause *TrailingWhere); using Decl::getASTContext; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 56363cd72fb74..fa8db4a4b5a49 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1106,7 +1106,7 @@ NominalTypeDecl::takeConformanceLoaderSlow() { ExtensionDecl::ExtensionDecl(SourceLoc extensionLoc, TypeRepr *extendedType, - MutableArrayRef inherited, + ArrayRef inherited, DeclContext *parent, TrailingWhereClause *trailingWhereClause) : GenericContext(DeclContextKind::ExtensionDecl, parent, nullptr), @@ -1123,7 +1123,7 @@ ExtensionDecl::ExtensionDecl(SourceLoc extensionLoc, ExtensionDecl *ExtensionDecl::create(ASTContext &ctx, SourceLoc extensionLoc, TypeRepr *extendedType, - MutableArrayRef inherited, + ArrayRef inherited, DeclContext *parent, TrailingWhereClause *trailingWhereClause, ClangNode clangNode) { @@ -3704,7 +3704,7 @@ PropertyWrapperTypeInfo NominalTypeDecl::getPropertyWrapperTypeInfo() const { GenericTypeDecl::GenericTypeDecl(DeclKind K, DeclContext *DC, Identifier name, SourceLoc nameLoc, - MutableArrayRef inherited, + ArrayRef inherited, GenericParamList *GenericParams) : GenericContext(DeclContextKind::GenericTypeDecl, DC, GenericParams), TypeDecl(K, DC, name, nameLoc, inherited) {} @@ -3914,7 +3914,7 @@ AssociatedTypeDecl *AssociatedTypeDecl::getAssociatedTypeAnchor() const { EnumDecl::EnumDecl(SourceLoc EnumLoc, Identifier Name, SourceLoc NameLoc, - MutableArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *Parent) : NominalTypeDecl(DeclKind::Enum, Parent, Name, NameLoc, Inherited, GenericParams), @@ -3934,7 +3934,7 @@ Type EnumDecl::getRawType() const { } StructDecl::StructDecl(SourceLoc StructLoc, Identifier Name, SourceLoc NameLoc, - MutableArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *Parent) : NominalTypeDecl(DeclKind::Struct, Parent, Name, NameLoc, Inherited, GenericParams), @@ -4045,7 +4045,7 @@ void NominalTypeDecl::synthesizeSemanticMembersIfNeeded(DeclName member) { } ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc, - MutableArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *Parent) : NominalTypeDecl(DeclKind::Class, Parent, Name, NameLoc, Inherited, GenericParams), @@ -4509,9 +4509,7 @@ bool EnumDecl::isEffectivelyExhaustive(ModuleDecl *M, } void EnumDecl::setHasFixedRawValues() { - auto flags = LazySemanticInfo.RawTypeAndFlags.getInt() | - EnumDecl::HasFixedRawValues; - LazySemanticInfo.RawTypeAndFlags.setInt(flags); + SemanticFlags |= OptionSet{EnumDecl::HasFixedRawValues}; } bool EnumDecl::hasCircularRawValue() const { @@ -4523,7 +4521,7 @@ bool EnumDecl::hasCircularRawValue() const { ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc, SourceLoc NameLoc, Identifier Name, - MutableArrayRef Inherited, + ArrayRef Inherited, TrailingWhereClause *TrailingWhere) : NominalTypeDecl(DeclKind::Protocol, DC, Name, NameLoc, Inherited, nullptr), diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 94ab37632feba..74f0376b9683a 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -2340,7 +2340,7 @@ ClassDecl *IRGenModule::getObjCRuntimeBaseClass(Identifier name, // Make a really fake-looking class. auto SwiftRootClass = new (Context) ClassDecl(SourceLoc(), name, SourceLoc(), - MutableArrayRef(), + ArrayRef(), /*generics*/ nullptr, Context.TheBuiltinModule); SwiftRootClass->setIsObjC(Context.LangOpts.EnableObjCInterop); diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index 61fa5b46d98b1..e5406758665f4 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -234,7 +234,7 @@ static EnumDecl *synthesizeCodingKeysEnum(DerivedConformance &derived) { auto *codingKeyProto = C.getProtocol(KnownProtocolKind::CodingKey); auto codingKeyType = codingKeyProto->getDeclaredInterfaceType(); TypeLoc protoTypeLoc[1] = {TypeLoc::withoutLoc(codingKeyType)}; - MutableArrayRef inherited = C.AllocateCopy(protoTypeLoc); + ArrayRef inherited = C.AllocateCopy(protoTypeLoc); auto *enumDecl = new (C) EnumDecl(SourceLoc(), C.Id_CodingKeys, SourceLoc(), inherited, nullptr, target); From 59b00d115b28865c5ac325e905ffec7534de5490 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 9 Oct 2020 15:30:36 -0700 Subject: [PATCH 368/745] [NFC] Make EnumRawTypeRequest Cached Drop the extra bit of state in the AST for the semantic type. --- include/swift/AST/Decl.h | 6 +---- include/swift/AST/TypeCheckRequests.h | 17 +++++------- include/swift/AST/TypeCheckerTypeIDZone.def | 3 +-- lib/AST/Decl.cpp | 10 ++++--- lib/AST/TypeCheckRequests.cpp | 27 +++++-------------- .../DerivedConformanceRawRepresentable.cpp | 6 ++++- lib/Sema/TypeCheckDecl.cpp | 4 +-- lib/Sema/TypeCheckRequestFunctions.cpp | 11 ++++---- 8 files changed, 34 insertions(+), 50 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 45b13499f85b0..678b1b0b3030c 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3304,11 +3304,7 @@ class EnumDecl final : public NominalTypeDecl { Type getRawType() const; /// Set the raw type of the enum from its inheritance clause. - void setRawType(Type rawType) { - auto flags = LazySemanticInfo.RawTypeAndFlags.getInt(); - LazySemanticInfo.RawTypeAndFlags.setPointerAndInt( - rawType, flags | HasComputedRawType); - } + void setRawType(Type rawType); /// True if none of the enum cases have associated values. /// diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 9546b7dfab229..4f9d7e9489d2f 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -126,10 +126,9 @@ class SuperclassTypeRequest }; /// Request the raw type of the given enum. -class EnumRawTypeRequest : - public SimpleRequest { +class EnumRawTypeRequest + : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -137,18 +136,14 @@ class EnumRawTypeRequest : friend SimpleRequest; // Evaluation. - Type - evaluate(Evaluator &evaluator, EnumDecl *enumDecl, - TypeResolutionStage stage) const; + Type evaluate(Evaluator &evaluator, EnumDecl *enumDecl) const; public: // Cycle handling void diagnoseCycle(DiagnosticEngine &diags) const; + void noteCycleStep(DiagnosticEngine &diags) const; - // Separate caching. - bool isCached() const; - Optional getCachedResult() const; - void cacheResult(Type value) const; + bool isCached() const { return true; } }; /// Request to determine the set of declarations that were are overridden diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 7b3f269555979..a8c62f4a0b683 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -71,8 +71,7 @@ SWIFT_REQUEST(TypeChecker, EnumRawValuesRequest, evaluator::SideEffect (EnumDecl *, TypeResolutionStage), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, EnumRawTypeRequest, - Type(EnumDecl *, TypeResolutionStage), SeparatelyCached, - NoLocationInfo) + Type(EnumDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ExistentialConformsToSelfRequest, bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ExistentialTypeSupportedRequest, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index fa8db4a4b5a49..1e08dd21947da 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3928,9 +3928,13 @@ EnumDecl::EnumDecl(SourceLoc EnumLoc, Type EnumDecl::getRawType() const { ASTContext &ctx = getASTContext(); - return evaluateOrDefault(ctx.evaluator, - EnumRawTypeRequest{const_cast(this), - TypeResolutionStage::Interface}, Type()); + return evaluateOrDefault( + ctx.evaluator, EnumRawTypeRequest{const_cast(this)}, Type()); +} + +void EnumDecl::setRawType(Type rawType) { + getASTContext().evaluator.cacheOutput(EnumRawTypeRequest{this}, + std::move(rawType)); } StructDecl::StructDecl(SourceLoc StructLoc, Identifier Name, SourceLoc NameLoc, diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index da08ab9057d25..d2d787cbb29b3 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -179,21 +179,10 @@ void EnumRawTypeRequest::diagnoseCycle(DiagnosticEngine &diags) const { diags.diagnose(enumDecl, diag::circular_enum_inheritance, enumDecl->getName()); } -bool EnumRawTypeRequest::isCached() const { - return std::get<1>(getStorage()) == TypeResolutionStage::Interface; -} - -Optional EnumRawTypeRequest::getCachedResult() const { - auto enumDecl = std::get<0>(getStorage()); - if (enumDecl->LazySemanticInfo.hasRawType()) - return enumDecl->LazySemanticInfo.RawTypeAndFlags.getPointer(); - - return None; -} - -void EnumRawTypeRequest::cacheResult(Type value) const { - auto enumDecl = std::get<0>(getStorage()); - enumDecl->LazySemanticInfo.cacheRawType(value); +void EnumRawTypeRequest::noteCycleStep(DiagnosticEngine &diags) const { + auto *decl = std::get<0>(getStorage()); + diags.diagnose(decl, diag::kind_declname_declared_here, + decl->getDescriptiveKind(), decl->getName()); } //----------------------------------------------------------------------------// @@ -854,17 +843,15 @@ bool EnumRawValuesRequest::isCached() const { Optional EnumRawValuesRequest::getCachedResult() const { auto *ED = std::get<0>(getStorage()); - if (ED->LazySemanticInfo.hasCheckedRawValues()) + if (ED->SemanticFlags.contains(EnumDecl::HasFixedRawValuesAndTypes)) return std::make_tuple<>(); return None; } void EnumRawValuesRequest::cacheResult(evaluator::SideEffect) const { auto *ED = std::get<0>(getStorage()); - auto flags = ED->LazySemanticInfo.RawTypeAndFlags.getInt() | - EnumDecl::HasFixedRawValues | - EnumDecl::HasFixedRawValuesAndTypes; - ED->LazySemanticInfo.RawTypeAndFlags.setInt(flags); + ED->SemanticFlags |= OptionSet{ + EnumDecl::HasFixedRawValues | EnumDecl::HasFixedRawValuesAndTypes}; } void EnumRawValuesRequest::diagnoseCycle(DiagnosticEngine &diags) const { diff --git a/lib/Sema/DerivedConformanceRawRepresentable.cpp b/lib/Sema/DerivedConformanceRawRepresentable.cpp index cc19e13ac6200..39f21a42819a6 100644 --- a/lib/Sema/DerivedConformanceRawRepresentable.cpp +++ b/lib/Sema/DerivedConformanceRawRepresentable.cpp @@ -24,6 +24,7 @@ #include "swift/AST/Types.h" #include "llvm/ADT/APInt.h" #include "DerivedConformances.h" +#include "TypeCheckDecl.h" using namespace swift; @@ -448,7 +449,10 @@ bool DerivedConformance::canDeriveRawRepresentable(DeclContext *DC, return false; Type rawType = enumDecl->getRawType(); - if (!rawType) + if (!rawType || rawType->hasError()) + return false; + + if (!computeAutomaticEnumValueKind(enumDecl)) return false; rawType = DC->mapTypeIntoContext(rawType); diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index b58de0fcb7255..3e21327297eba 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -1162,7 +1162,7 @@ EnumRawValuesRequest::evaluate(Evaluator &eval, EnumDecl *ED, if (uncheckedRawValueOf(elt)) { if (!uncheckedRawValueOf(elt)->isImplicit()) lastExplicitValueElt = elt; - } else if (!ED->LazySemanticInfo.hasFixedRawValues()) { + } else if (!ED->SemanticFlags.contains(EnumDecl::HasFixedRawValues)) { // Try to pull out the automatic enum value kind. If that fails, bail. if (!valueKind) { valueKind = computeAutomaticEnumValueKind(ED); @@ -1217,7 +1217,7 @@ EnumRawValuesRequest::evaluate(Evaluator &eval, EnumDecl *ED, // to have set things up correctly. This comes up with imported enums // and deserialized @objc enums which always have their raw values setup // beforehand. - if (ED->LazySemanticInfo.hasFixedRawValues()) + if (ED->SemanticFlags.contains(EnumDecl::HasFixedRawValues)) continue; // Using magic literals like #file as raw value is not supported right now. diff --git a/lib/Sema/TypeCheckRequestFunctions.cpp b/lib/Sema/TypeCheckRequestFunctions.cpp index e8bad702d48a1..c3abd1a067820 100644 --- a/lib/Sema/TypeCheckRequestFunctions.cpp +++ b/lib/Sema/TypeCheckRequestFunctions.cpp @@ -136,13 +136,12 @@ SuperclassTypeRequest::evaluate(Evaluator &evaluator, return Type(); } -Type -EnumRawTypeRequest::evaluate(Evaluator &evaluator, EnumDecl *enumDecl, - TypeResolutionStage stage) const { +Type EnumRawTypeRequest::evaluate(Evaluator &evaluator, + EnumDecl *enumDecl) const { for (unsigned int idx : indices(enumDecl->getInherited())) { - auto inheritedTypeResult = - evaluator(InheritedTypeRequest{enumDecl, idx, stage}); - + auto inheritedTypeResult = evaluator( + InheritedTypeRequest{enumDecl, idx, TypeResolutionStage::Interface}); + if (auto err = inheritedTypeResult.takeError()) { llvm::handleAllErrors(std::move(err), [](const CyclicalRequestError &E) { From c6f4be2392574d126aeed691242b3c5a48dabf98 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 9 Oct 2020 15:44:54 -0700 Subject: [PATCH 369/745] [Concurrency] Eliminate "privileged" forms of ActorIsolation. The "privileged" cases for actor instance and global actor isolation covered the case where the entity itself is within the particular actor but can be freely used from outside the actor, e.g., because it is asynchronous or an asynchronous handler. This is the wrong modeling for the problem, because it's only the first part of that---what the isolation of the particular entity is---that is needed for most clients. There will be a different abstraction for that. --- include/swift/AST/ActorIsolation.h | 20 ++------------------ lib/AST/TypeCheckRequests.cpp | 9 --------- lib/Sema/TypeCheckConcurrency.cpp | 30 +++++++++--------------------- lib/Sema/TypeCheckDeclObjC.cpp | 11 +++++++++-- lib/Sema/TypeCheckProtocol.cpp | 10 ++++++++-- 5 files changed, 28 insertions(+), 52 deletions(-) diff --git a/include/swift/AST/ActorIsolation.h b/include/swift/AST/ActorIsolation.h index 0d4f44d855cc7..2aec57cdf20d4 100644 --- a/include/swift/AST/ActorIsolation.h +++ b/include/swift/AST/ActorIsolation.h @@ -42,9 +42,6 @@ class ActorIsolation { /// For example, a mutable stored property or synchronous function within /// the actor is isolated to the instance of that actor. ActorInstance, - /// The declaration can refer to actor-isolated state, but can also be - /// referenced from outside the actor. - ActorPrivileged, /// The declaration is explicitly specified to be independent of any actor, /// meaning that it can be used from any actor but is also unable to /// refer to the isolated state of any given actor. @@ -52,9 +49,6 @@ class ActorIsolation { /// The declaration is isolated to a global actor. It can refer to other /// entities with the same global actor. GlobalActor, - /// The declaration can refer to state isolated by the given global actor, - /// and can be used from anywhere. - GlobalActorPrivileged, }; private: @@ -78,18 +72,10 @@ class ActorIsolation { return ActorIsolation(Independent, nullptr); } - static ActorIsolation forActorPrivileged(ClassDecl *actor) { - return ActorIsolation(ActorPrivileged, actor); - } - static ActorIsolation forActorInstance(ClassDecl *actor) { return ActorIsolation(ActorInstance, actor); } - static ActorIsolation forGlobalActorPrivileged(Type globalActor) { - return ActorIsolation(GlobalActorPrivileged, globalActor); - } - static ActorIsolation forGlobalActor(Type globalActor) { return ActorIsolation(GlobalActor, globalActor); } @@ -99,12 +85,12 @@ class ActorIsolation { operator Kind() const { return getKind(); } ClassDecl *getActor() const { - assert(getKind() == ActorInstance || getKind() == ActorPrivileged); + assert(getKind() == ActorInstance); return actor; } Type getGlobalActor() const { - assert(getKind() == GlobalActor || getKind() == GlobalActorPrivileged); + assert(getKind() == GlobalActor); return globalActor; } @@ -119,11 +105,9 @@ class ActorIsolation { return true; case ActorInstance: - case ActorPrivileged: return lhs.actor == rhs.actor; case GlobalActor: - case GlobalActorPrivileged: return areTypesEqual(lhs.globalActor, rhs.globalActor); } } diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 4379fd202a80c..8b774caaea0d2 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1455,10 +1455,6 @@ void swift::simple_display( out << "actor-isolated to instance of " << state.getActor()->getName(); break; - case ActorIsolation::ActorPrivileged: - out << "actor-privileged to instance of " << state.getActor()->getName(); - break; - case ActorIsolation::Independent: out << "actor-independent"; break; @@ -1471,11 +1467,6 @@ void swift::simple_display( out << "actor-isolated to global actor " << state.getGlobalActor().getString(); break; - - case ActorIsolation::GlobalActorPrivileged: - out << "actor-privileged to global actor " - << state.getGlobalActor().getString(); - break; } } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index e409cd4ef22e6..d751f46150b07 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -550,6 +550,13 @@ class IsolationRestriction { case DeclKind::Accessor: case DeclKind::Func: case DeclKind::Subscript: + // A function that provides an asynchronous context has no restrictions + // on its access. + if (auto func = dyn_cast(decl)) { + if (func->isAsyncContext()) + return forUnrestricted(); + } + // Local captures can only be referenced in their local context or a // context that is guaranteed not to run concurrently with it. if (cast(decl)->isLocalCapture()) @@ -565,10 +572,7 @@ class IsolationRestriction { return forGlobalActor(isolation.getGlobalActor()); case ActorIsolation::Independent: - case ActorIsolation::ActorPrivileged: - case ActorIsolation::GlobalActorPrivileged: - // Actor-independent and actor-privileged declarations have no - // restrictions on their access. + // Actor-independent have no restrictions on their access. return forUnrestricted(); case ActorIsolation::Unspecified: @@ -869,7 +873,6 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { switch (auto contextIsolation = getInnermostIsolatedContext(getDeclContext())) { case ActorIsolation::ActorInstance: - case ActorIsolation::ActorPrivileged: ctx.Diags.diagnose( loc, diag::global_actor_from_instance_actor_context, value->getDescriptiveKind(), value->getName(), globalActor, @@ -878,7 +881,6 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { return true; case ActorIsolation::GlobalActor: - case ActorIsolation::GlobalActorPrivileged: // If the global actor types are the same, we're done. if (contextIsolation.getGlobalActor()->isEqual(globalActor)) return false; @@ -973,7 +975,6 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { switch (auto contextIsolation = getActorIsolation( cast(selfVar->getDeclContext()->getAsDecl()))) { case ActorIsolation::ActorInstance: - case ActorIsolation::ActorPrivileged: // An escaping partial application of something that is part of // the actor's isolated state is never permitted. if (isEscapingPartialApply) { @@ -1001,7 +1002,6 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { return true; case ActorIsolation::GlobalActor: - case ActorIsolation::GlobalActorPrivileged: // The 'self' is for a member that's part of a global actor, which // means we cannot refer to actor-isolated state. ctx.Diags.diagnose( @@ -1081,12 +1081,6 @@ ActorIsolation ActorIsolationRequest::evaluate( if (!globalActorType || globalActorType->hasError()) return ActorIsolation::forUnspecified(); - // A function that is an asynchronous context is actor-privileged. - if (auto func = dyn_cast(value)) { - if (func->isAsyncContext()) - return ActorIsolation::forGlobalActorPrivileged(globalActorType); - } - return ActorIsolation::forGlobalActor(globalActorType); } @@ -1104,13 +1098,7 @@ ActorIsolation ActorIsolationRequest::evaluate( // actor-isolated state. auto classDecl = value->getDeclContext()->getSelfClassDecl(); if (classDecl && classDecl->isActor() && value->isInstanceMember()) { - // A function that is an asynchronous context is actor-privileged. - if (auto func = dyn_cast(value)) { - if (func->isAsyncContext()) - return ActorIsolation::forActorPrivileged(classDecl); - } - - // Everything else is part of the actor's isolated state. + // Part of the actor's isolated state. return ActorIsolation::forActorInstance(classDecl); } diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 1fcb52b2b79d4..639f435b11003 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -375,6 +375,13 @@ static bool checkObjCActorIsolation(const ValueDecl *VD, // Check actor isolation. bool Diagnose = shouldDiagnoseObjCReason(Reason, VD->getASTContext()); + // Asynchronous contexts can be @objc. + // FIXME: Feels duplicative of TypeCheckConcurrency. + if (auto func = dyn_cast(VD)) { + if (func->isAsyncContext()) + return false; + } + switch (getActorIsolation(const_cast(VD))) { case ActorIsolation::ActorInstance: // Actor-isolated functions cannot be @objc. @@ -388,9 +395,9 @@ static bool checkObjCActorIsolation(const ValueDecl *VD, } return true; - case ActorIsolation::ActorPrivileged: case ActorIsolation::GlobalActor: - case ActorIsolation::GlobalActorPrivileged: + // FIXME: Consider whether to limit @objc on global-actor-qualified + // declarations. case ActorIsolation::Independent: case ActorIsolation::Unspecified: return false; diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 3c027264d78c8..1375721359fb8 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4340,6 +4340,14 @@ void ConformanceChecker::resolveValueWitnesses() { // Check for actor-isolation consistency. switch (getActorIsolation(witness)) { case ActorIsolation::ActorInstance: { + // Asynchronous contexts can be used to conform to protocol + // requirements. + // FIXME: Feels duplicative. + if (auto func = dyn_cast(witness)) { + if (func->isAsyncContext()) + break; + } + // Actor-isolated witnesses cannot conform to protocol requirements. bool canBeAsyncHandler = false; if (auto witnessFunc = dyn_cast(witness)) { @@ -4364,8 +4372,6 @@ void ConformanceChecker::resolveValueWitnesses() { break; } - case ActorIsolation::ActorPrivileged: - case ActorIsolation::GlobalActorPrivileged: case ActorIsolation::Independent: case ActorIsolation::Unspecified: break; From 9c983a49c41f411e6a5e535b0daa02789f98cae0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 9 Oct 2020 16:00:58 -0700 Subject: [PATCH 370/745] [Concurrency] Rename IsolationRestriction and use it more widely. Rename IsolationRestriction to ActorIsolationRestriction. This type governs the *use* of a potentially actor-isolated entity, which includes the notion that (e.g.) an asychronous function can be used from any context even when it is actor-isolated. Use this for @objc checking and protocol conformance checking, in addition to its original use for enforcing actor isolation in statements and expressions. --- lib/Sema/TypeCheckConcurrency.cpp | 264 +++++++++--------------------- lib/Sema/TypeCheckConcurrency.h | 106 ++++++++++++ lib/Sema/TypeCheckDeclObjC.cpp | 19 +-- lib/Sema/TypeCheckProtocol.cpp | 20 +-- 4 files changed, 201 insertions(+), 208 deletions(-) diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index d751f46150b07..8a398ecee545e 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -402,188 +402,86 @@ GlobalActorAttributeRequest::evaluate( return std::make_pair(globalActorAttr, globalActorNominal); } -namespace { - -/// The isolation restriction in effect for a given declaration that is -/// referenced from source. -class IsolationRestriction { -public: - enum Kind { - /// There is no restriction on references to the given declaration, - /// e.g., because it's immutable. - Unrestricted, - - /// Access to the declaration is unsafe in a concurrent context. - Unsafe, - - /// The declaration is a local entity whose capture could introduce - /// data races. The context in which the local was defined is provided. - LocalCapture, - - /// References to this member of an actor are only permitted on 'self'. - ActorSelf, - - /// References to a declaration that is part of a global actor are only - /// permitted from other declarations with that same global actor. - GlobalActor, - }; - -private: - /// The kind of restriction - Kind kind; - - union { - /// The local context that an entity is tied to. - DeclContext *localContext; - - /// The actor class that the entity is declared in. - ClassDecl *actorClass; - - /// The global actor type. - TypeBase *globalActor; - } data; - - explicit IsolationRestriction(Kind kind) : kind(kind) { } - -public: - Kind getKind() const { return kind; } - - /// Retrieve the declaration context in which a local was defined. - DeclContext *getLocalContext() const { - assert(kind == LocalCapture); - return data.localContext; - } - - /// Retrieve the actor class that the declaration is within. - ClassDecl *getActorClass() const { - assert(kind == ActorSelf); - return data.actorClass; - } - - /// Retrieve the actor class that the declaration is within. - Type getGlobalActor() const { - assert(kind == GlobalActor); - return Type(data.globalActor); - } - - /// There are no restrictions on the use of the entity. - static IsolationRestriction forUnrestricted() { - return IsolationRestriction(Unrestricted); - } - - /// Accesses to the given declaration are unsafe. - static IsolationRestriction forUnsafe() { - return IsolationRestriction(Unsafe); - } - - /// Accesses to the given declaration can only be made via the 'self' of - /// the current actor. - static IsolationRestriction forActorSelf(ClassDecl *actorClass) { - IsolationRestriction result(ActorSelf); - result.data.actorClass = actorClass; - return result; - } - - /// Access is restricted to code running within the given local context. - static IsolationRestriction forLocalCapture(DeclContext *dc) { - IsolationRestriction result(LocalCapture); - result.data.localContext = dc; - return result; - } - - /// Accesses to the given declaration can only be made via this particular - /// global actor. - static IsolationRestriction forGlobalActor(Type globalActor) { - IsolationRestriction result(GlobalActor); - result.data.globalActor = globalActor.getPointer(); - return result; - } - - /// Determine the isolation rules for a given declaration. - static IsolationRestriction forDeclaration(Decl *decl) { - switch (decl->getKind()) { - case DeclKind::AssociatedType: - case DeclKind::Class: - case DeclKind::Enum: - case DeclKind::Extension: - case DeclKind::GenericTypeParam: - case DeclKind::OpaqueType: - case DeclKind::Protocol: - case DeclKind::Struct: - case DeclKind::TypeAlias: - // Types are always available. - return forUnrestricted(); - - case DeclKind::Constructor: - case DeclKind::EnumCase: - case DeclKind::EnumElement: - // Type-level entities don't require isolation. - return forUnrestricted(); - - case DeclKind::IfConfig: - case DeclKind::Import: - case DeclKind::InfixOperator: - case DeclKind::MissingMember: - case DeclKind::Module: - case DeclKind::PatternBinding: - case DeclKind::PostfixOperator: - case DeclKind::PoundDiagnostic: - case DeclKind::PrecedenceGroup: - case DeclKind::PrefixOperator: - case DeclKind::TopLevelCode: - // Non-value entities don't require isolation. +/// Determine the isolation rules for a given declaration. +ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(Decl *decl) { + switch (decl->getKind()) { + case DeclKind::AssociatedType: + case DeclKind::Class: + case DeclKind::Enum: + case DeclKind::Extension: + case DeclKind::GenericTypeParam: + case DeclKind::OpaqueType: + case DeclKind::Protocol: + case DeclKind::Struct: + case DeclKind::TypeAlias: + // Types are always available. + return forUnrestricted(); + + case DeclKind::Constructor: + case DeclKind::EnumCase: + case DeclKind::EnumElement: + // Type-level entities don't require isolation. + return forUnrestricted(); + + case DeclKind::IfConfig: + case DeclKind::Import: + case DeclKind::InfixOperator: + case DeclKind::MissingMember: + case DeclKind::Module: + case DeclKind::PatternBinding: + case DeclKind::PostfixOperator: + case DeclKind::PoundDiagnostic: + case DeclKind::PrecedenceGroup: + case DeclKind::PrefixOperator: + case DeclKind::TopLevelCode: + // Non-value entities don't require isolation. + return forUnrestricted(); + + case DeclKind::Destructor: + // Destructors don't require isolation. + return forUnrestricted(); + + case DeclKind::Param: + case DeclKind::Var: + // 'let' declarations are immutable, so there are no restrictions on + // their access. + if (cast(decl)->isLet()) return forUnrestricted(); - case DeclKind::Destructor: - // Destructors don't require isolation. - return forUnrestricted(); + LLVM_FALLTHROUGH; - case DeclKind::Param: - case DeclKind::Var: - // 'let' declarations are immutable, so there are no restrictions on - // their access. - if (cast(decl)->isLet()) + case DeclKind::Accessor: + case DeclKind::Func: + case DeclKind::Subscript: + // A function that provides an asynchronous context has no restrictions + // on its access. + if (auto func = dyn_cast(decl)) { + if (func->isAsyncContext()) return forUnrestricted(); + } - LLVM_FALLTHROUGH; - - case DeclKind::Accessor: - case DeclKind::Func: - case DeclKind::Subscript: - // A function that provides an asynchronous context has no restrictions - // on its access. - if (auto func = dyn_cast(decl)) { - if (func->isAsyncContext()) - return forUnrestricted(); - } - - // Local captures can only be referenced in their local context or a - // context that is guaranteed not to run concurrently with it. - if (cast(decl)->isLocalCapture()) - return forLocalCapture(decl->getDeclContext()); + // Local captures can only be referenced in their local context or a + // context that is guaranteed not to run concurrently with it. + if (cast(decl)->isLocalCapture()) + return forLocalCapture(decl->getDeclContext()); - // Determine the actor isolation of the given declaration. - switch (auto isolation = getActorIsolation(cast(decl))) { - case ActorIsolation::ActorInstance: - // Protected actor instance members can only be accessed on 'self'. - return forActorSelf(isolation.getActor()); + // Determine the actor isolation of the given declaration. + switch (auto isolation = getActorIsolation(cast(decl))) { + case ActorIsolation::ActorInstance: + // Protected actor instance members can only be accessed on 'self'. + return forActorSelf(isolation.getActor()); - case ActorIsolation::GlobalActor: - return forGlobalActor(isolation.getGlobalActor()); + case ActorIsolation::GlobalActor: + return forGlobalActor(isolation.getGlobalActor()); - case ActorIsolation::Independent: - // Actor-independent have no restrictions on their access. - return forUnrestricted(); + case ActorIsolation::Independent: + // Actor-independent have no restrictions on their access. + return forUnrestricted(); - case ActorIsolation::Unspecified: - return forUnsafe(); - } + case ActorIsolation::Unspecified: + return forUnsafe(); } } - - operator Kind() const { return kind; }; -}; - } namespace { @@ -910,18 +808,18 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { if (!value) return false; - switch (auto isolation = IsolationRestriction::forDeclaration(value)) { - case IsolationRestriction::Unrestricted: + switch (auto isolation = ActorIsolationRestriction::forDeclaration(value)) { + case ActorIsolationRestriction::Unrestricted: return false; - case IsolationRestriction::ActorSelf: + case ActorIsolationRestriction::ActorSelf: llvm_unreachable("non-member reference into an actor"); - case IsolationRestriction::GlobalActor: + case ActorIsolationRestriction::GlobalActor: return checkGlobalActorReference( value, loc, isolation.getGlobalActor()); - case IsolationRestriction::LocalCapture: + case ActorIsolationRestriction::LocalCapture: // Only diagnose unsafe concurrent accesses within the context of an // actor. This is globally unsafe, but locally enforceable. if (!getNearestEnclosingActorContext(getDeclContext())) @@ -941,7 +839,7 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { return false; - case IsolationRestriction::Unsafe: + case ActorIsolationRestriction::Unsafe: return diagnoseReferenceToUnsafe(value, loc); } } @@ -953,11 +851,11 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { if (!base || !member) return false; - switch (auto isolation = IsolationRestriction::forDeclaration(member)) { - case IsolationRestriction::Unrestricted: + switch (auto isolation = ActorIsolationRestriction::forDeclaration(member)) { + case ActorIsolationRestriction::Unrestricted: return false; - case IsolationRestriction::ActorSelf: { + case ActorIsolationRestriction::ActorSelf: { // Must reference actor-isolated state on 'self'. auto selfVar = getSelfReference(base); if (!selfVar) { @@ -1028,14 +926,14 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { return false; } - case IsolationRestriction::GlobalActor: + case ActorIsolationRestriction::GlobalActor: return checkGlobalActorReference( member, memberLoc, isolation.getGlobalActor()); - case IsolationRestriction::LocalCapture: + case ActorIsolationRestriction::LocalCapture: llvm_unreachable("Locals cannot be referenced with member syntax"); - case IsolationRestriction::Unsafe: + case ActorIsolationRestriction::Unsafe: return diagnoseReferenceToUnsafe(member, memberLoc); } } diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index b2ec4f05784ec..477bd7a18d257 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -17,14 +17,19 @@ #ifndef SWIFT_SEMA_TYPECHECKCONCURRENCY_H #define SWIFT_SEMA_TYPECHECKCONCURRENCY_H +#include "swift/AST/Type.h" +#include + namespace swift { class ActorIsolation; class ASTContext; class ClassDecl; +class Decl; class DeclContext; class Expr; class FuncDecl; +class TypeBase; class ValueDecl; /// Add notes suggesting the addition of 'async' or '@asyncHandler', as @@ -37,6 +42,107 @@ void checkActorIsolation(const Expr *expr, const DeclContext *dc); /// Determine how the given value declaration is isolated. ActorIsolation getActorIsolation(ValueDecl *value); +/// The isolation restriction in effect for a given declaration that is +/// referenced from source. +class ActorIsolationRestriction { +public: + enum Kind { + /// There is no restriction on references to the given declaration, + /// e.g., because it's immutable. + Unrestricted, + + /// Access to the declaration is unsafe in a concurrent context. + Unsafe, + + /// The declaration is a local entity whose capture could introduce + /// data races. The context in which the local was defined is provided. + LocalCapture, + + /// References to this member of an actor are only permitted on 'self'. + ActorSelf, + + /// References to a declaration that is part of a global actor are only + /// permitted from other declarations with that same global actor. + GlobalActor, + }; + +private: + /// The kind of restriction + Kind kind; + + union { + /// The local context that an entity is tied to. + DeclContext *localContext; + + /// The actor class that the entity is declared in. + ClassDecl *actorClass; + + /// The global actor type. + TypeBase *globalActor; + } data; + + explicit ActorIsolationRestriction(Kind kind) : kind(kind) { } + +public: + Kind getKind() const { return kind; } + + /// Retrieve the declaration context in which a local was defined. + DeclContext *getLocalContext() const { + assert(kind == LocalCapture); + return data.localContext; + } + + /// Retrieve the actor class that the declaration is within. + ClassDecl *getActorClass() const { + assert(kind == ActorSelf); + return data.actorClass; + } + + /// Retrieve the actor class that the declaration is within. + Type getGlobalActor() const { + assert(kind == GlobalActor); + return Type(data.globalActor); + } + + /// There are no restrictions on the use of the entity. + static ActorIsolationRestriction forUnrestricted() { + return ActorIsolationRestriction(Unrestricted); + } + + /// Accesses to the given declaration are unsafe. + static ActorIsolationRestriction forUnsafe() { + return ActorIsolationRestriction(Unsafe); + } + + /// Accesses to the given declaration can only be made via the 'self' of + /// the current actor. + static ActorIsolationRestriction forActorSelf(ClassDecl *actorClass) { + ActorIsolationRestriction result(ActorSelf); + result.data.actorClass = actorClass; + return result; + } + + /// Access is restricted to code running within the given local context. + static ActorIsolationRestriction forLocalCapture(DeclContext *dc) { + ActorIsolationRestriction result(LocalCapture); + result.data.localContext = dc; + return result; + } + + /// Accesses to the given declaration can only be made via this particular + /// global actor. + static ActorIsolationRestriction forGlobalActor(Type globalActor) { + ActorIsolationRestriction result(GlobalActor); + result.data.globalActor = globalActor.getPointer(); + return result; + } + + /// Determine the isolation rules for a given declaration. + static ActorIsolationRestriction forDeclaration(Decl *decl); + + operator Kind() const { return kind; }; +}; + } // end namespace swift #endif /* SWIFT_SEMA_TYPECHECKCONCURRENCY_H */ diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 639f435b11003..287acfc7708dd 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -375,15 +375,9 @@ static bool checkObjCActorIsolation(const ValueDecl *VD, // Check actor isolation. bool Diagnose = shouldDiagnoseObjCReason(Reason, VD->getASTContext()); - // Asynchronous contexts can be @objc. - // FIXME: Feels duplicative of TypeCheckConcurrency. - if (auto func = dyn_cast(VD)) { - if (func->isAsyncContext()) - return false; - } - - switch (getActorIsolation(const_cast(VD))) { - case ActorIsolation::ActorInstance: + switch (auto restriction = ActorIsolationRestriction::forDeclaration( + const_cast(VD))) { + case ActorIsolationRestriction::ActorSelf: // Actor-isolated functions cannot be @objc. if (Diagnose) { VD->diagnose( @@ -395,11 +389,12 @@ static bool checkObjCActorIsolation(const ValueDecl *VD, } return true; - case ActorIsolation::GlobalActor: + case ActorIsolationRestriction::GlobalActor: // FIXME: Consider whether to limit @objc on global-actor-qualified // declarations. - case ActorIsolation::Independent: - case ActorIsolation::Unspecified: + case ActorIsolationRestriction::Unrestricted: + case ActorIsolationRestriction::LocalCapture: + case ActorIsolationRestriction::Unsafe: return false; } } diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 1375721359fb8..91e738fb3e384 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4338,16 +4338,9 @@ void ConformanceChecker::resolveValueWitnesses() { } // Check for actor-isolation consistency. - switch (getActorIsolation(witness)) { - case ActorIsolation::ActorInstance: { - // Asynchronous contexts can be used to conform to protocol - // requirements. - // FIXME: Feels duplicative. - if (auto func = dyn_cast(witness)) { - if (func->isAsyncContext()) - break; - } - + switch (auto restriction = + ActorIsolationRestriction::forDeclaration(witness)) { + case ActorIsolationRestriction::ActorSelf: { // Actor-isolated witnesses cannot conform to protocol requirements. bool canBeAsyncHandler = false; if (auto witnessFunc = dyn_cast(witness)) { @@ -4367,13 +4360,14 @@ void ConformanceChecker::resolveValueWitnesses() { return; } - case ActorIsolation::GlobalActor: { + case ActorIsolationRestriction::GlobalActor: { // FIXME: Check against the requirement. This needs serious refactoring. break; } - case ActorIsolation::Independent: - case ActorIsolation::Unspecified: + case ActorIsolationRestriction::Unrestricted: + case ActorIsolationRestriction::Unsafe: + case ActorIsolationRestriction::LocalCapture: break; } From a91cede79b812b041da5d7efb6c63ebebe6d3aea Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Fri, 9 Oct 2020 13:51:19 -0700 Subject: [PATCH 371/745] [Parse][CodeCompletion] Don't special case code completion when forming single-expression closures/function bodies (NFC) Code completion used to avoid forming single expression closures/function bodies when the single expression contained the code completion expression because a contextual type mismatch could result in types not being applied to the AST, giving no completions. Completions that have been migrated to the new solver-based completion mechanism don't need this behavior, however. Rather than trying to guess whether the type of completion we're going to end up performing is one of the ones that haven't been migrated to the solver yet when parsing, instead just always form single-expression closures/function bodies (like we do for regular compilation) and undo the transformation if and when we know we're going to perform a completion kind we haven't migrated yet. Once all completion kinds are migrated, the undo-ing code can be removed. --- include/swift/Parse/Parser.h | 9 ------ lib/IDE/CodeCompletion.cpp | 62 +++++++++++++++++++++++------------- lib/Parse/ParseDecl.cpp | 6 +--- lib/Parse/ParseExpr.cpp | 7 +--- lib/Parse/Parser.cpp | 28 ---------------- 5 files changed, 41 insertions(+), 71 deletions(-) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 20c4cf720dff8..224e5db97a524 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -709,15 +709,6 @@ class Parser { Context.LangOpts.ParseForSyntaxTreeOnly; } - /// If a function or closure body consists of a single expression, determine - /// whether we should turn wrap it in a return statement or not. - /// - /// We don't do this transformation for non-solver-based code completion - /// positions, as the source may be incomplete and the type mismatch in the - /// return statement will just confuse the type checker. - bool shouldSuppressSingleExpressionBodyTransform( - ParserStatus Status, MutableArrayRef BodyElems); - public: InFlightDiagnostic diagnose(SourceLoc Loc, Diagnostic Diag) { if (Diags.isDiagnosticPointsToFirstBadToken(Diag.getID()) && diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 4fc0b5b149841..01b84ef8deb9a 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -1672,7 +1672,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { private: void addKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody); - bool trySolverCompletion(); + bool trySolverCompletion(bool MaybeFuncBody); }; } // end anonymous namespace @@ -6054,25 +6054,7 @@ void deliverDotExprResults( deliverCompletionResults(CompletionCtx, Lookup, *SF, Consumer); } -bool CodeCompletionCallbacksImpl::trySolverCompletion() { - CompletionContext.CodeCompletionKind = Kind; - - if (Kind == CompletionKind::None) - return true; - - bool MaybeFuncBody = true; - if (CurDeclContext) { - auto *CD = CurDeclContext->getLocalContext(); - if (!CD || CD->getContextKind() == DeclContextKind::Initializer || - CD->getContextKind() == DeclContextKind::TopLevelCodeDecl) - MaybeFuncBody = false; - } - - if (auto *DC = dyn_cast_or_null(ParsedDecl)) { - if (DC->isChildContextOf(CurDeclContext)) - CurDeclContext = DC; - } - +bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) { assert(ParsedExpr || CurDeclContext); SourceLoc CompletionLoc = ParsedExpr @@ -6111,12 +6093,42 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion() { } } +// Undoes the single-expression closure/function body transformation on the +// given DeclContext and its parent contexts if they have a single expression +// body that contains the code completion location. +// +// FIXME: Remove this once all expression position completions are migrated +// to work via TypeCheckCompletionCallback. +static void undoSingleExpressionReturn(DeclContext *DC) { + auto updateBody = [](BraceStmt *BS, ASTContext &Ctx) -> bool { + ASTNode FirstElem = BS->getFirstElement(); + auto *RS = dyn_cast_or_null(FirstElem.dyn_cast()); + + if (!RS || !RS->isImplicit()) + return false; + + BS->setFirstElement(RS->getResult()); + return true; + }; + + while (ClosureExpr *CE = dyn_cast_or_null(DC)) { + if (CE->hasSingleExpressionBody()) { + if (updateBody(CE->getBody(), CE->getASTContext())) + CE->setBody(CE->getBody(), false); + } + DC = DC->getParent(); + } + if (FuncDecl *FD = dyn_cast_or_null(DC)) { + if (FD->hasSingleExpressionBody()) { + if (updateBody(FD->getBody(), FD->getASTContext())) + FD->setHasSingleExpressionBody(false); + } + } +} + void CodeCompletionCallbacksImpl::doneParsing() { CompletionContext.CodeCompletionKind = Kind; - if (trySolverCompletion()) - return; - if (Kind == CompletionKind::None) { return; } @@ -6134,6 +6146,10 @@ void CodeCompletionCallbacksImpl::doneParsing() { CurDeclContext = DC; } + if (trySolverCompletion(MaybeFuncBody)) + return; + + undoSingleExpressionReturn(CurDeclContext); typeCheckContextAt( CurDeclContext, ParsedExpr diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index b8d19723dc113..46b04cd8462ee 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -6588,11 +6588,7 @@ BraceStmt *Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) { // If the body consists of a single expression, turn it into a return // statement. - // - // But don't do this transformation when performing certain kinds of code - // completion, as the source may be incomplete and the type mismatch in return - // statement will just confuse the type checker. - if (shouldSuppressSingleExpressionBodyTransform(Body, BS->getElements())) + if (BS->getNumElements() != 1) return BS; auto Element = BS->getFirstElement(); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index e05b08cd4ece8..f98d08a66caca 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -2841,13 +2841,8 @@ ParserResult Parser::parseExprClosure() { // If the body consists of a single expression, turn it into a return // statement. - // - // But don't do this transformation when performing certain kinds of code - // completion, as the source may be incomplete and the type mismatch in return - // statement will just confuse the type checker. bool hasSingleExpressionBody = false; - if (!missingRBrace && - !shouldSuppressSingleExpressionBodyTransform(Status, bodyElements)) { + if (!missingRBrace && bodyElements.size() == 1) { // If the closure's only body element is a single return statement, // use that instead of creating a new wrapping return expression. Expr *returnExpr = nullptr; diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 4dd6fc627b3b7..ce02e6849f797 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -1185,34 +1185,6 @@ Parser::getStringLiteralIfNotInterpolated(SourceLoc Loc, Segments.front().Length)); } -bool Parser:: -shouldSuppressSingleExpressionBodyTransform(ParserStatus Status, - MutableArrayRef BodyElems) { - if (BodyElems.size() != 1) - return true; - - if (!Status.hasCodeCompletion()) - return false; - - struct HasMemberCompletion: public ASTWalker { - bool Value = false; - std::pair walkToExprPre(Expr *E) override { - if (auto *CCE = dyn_cast(E)) { - // If it has a base expression this is member completion, which is - // performed using the new solver-based mechanism, so it's ok to go - // ahead with the transform (and necessary to pick up the correct - // expected type). - Value = CCE->getBase(); - return {false, nullptr}; - } - return {true, E}; - } - }; - HasMemberCompletion Check; - BodyElems.front().walk(Check); - return !Check.Value; -} - struct ParserUnit::Implementation { std::shared_ptr SPActions; LangOptions LangOpts; From 00501fcaabe316673d3db8ee476b4a5b63043c46 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Fri, 9 Oct 2020 16:05:37 -0700 Subject: [PATCH 372/745] Update GettingStarted doc to use `--scheme` for update-checkout script https://bugs.swift.org/browse/SR-13717 --- docs/HowToGuides/GettingStarted.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/HowToGuides/GettingStarted.md b/docs/HowToGuides/GettingStarted.md index 8ee02ea85add9..8db9a0db716d9 100644 --- a/docs/HowToGuides/GettingStarted.md +++ b/docs/HowToGuides/GettingStarted.md @@ -97,7 +97,7 @@ toolchain as a one-off, there are a couple of differences: or a specific snapshot. You can update the branch/tag for all repositories as follows: ```sh - utils/update-checkout --branch mybranchname + utils/update-checkout --scheme mybranchname # OR utils/update-checkout --tag mytagname ``` From 3b807a448d55faf86acc8ecacb21724e9f5cda20 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 9 Oct 2020 16:49:13 -0700 Subject: [PATCH 373/745] [Concurrency] Handle substitutions into generic global actors. --- lib/Sema/TypeCheckConcurrency.cpp | 60 +++++++++++++++++++------- lib/Sema/TypeCheckConcurrency.h | 3 +- test/Concurrency/actor_isolation.swift | 26 +++++++++++ test/attr/global_actor.swift | 10 +++++ 4 files changed, 82 insertions(+), 17 deletions(-) diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 8a398ecee545e..87afeb4734f04 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -403,7 +403,10 @@ GlobalActorAttributeRequest::evaluate( } /// Determine the isolation rules for a given declaration. -ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(Decl *decl) { +ActorIsolationRestriction ActorIsolationRestriction::forDeclaration( + ConcreteDeclRef declRef) { + auto decl = declRef.getDecl(); + switch (decl->getKind()) { case DeclKind::AssociatedType: case DeclKind::Class: @@ -471,8 +474,13 @@ ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(Decl *decl) // Protected actor instance members can only be accessed on 'self'. return forActorSelf(isolation.getActor()); - case ActorIsolation::GlobalActor: - return forGlobalActor(isolation.getGlobalActor()); + case ActorIsolation::GlobalActor: { + Type actorType = isolation.getGlobalActor(); + if (auto subs = declRef.getSubstitutions()) + actorType = actorType.subst(subs); + + return forGlobalActor(actorType); + } case ActorIsolation::Independent: // Actor-independent have no restrictions on their access. @@ -520,14 +528,13 @@ static Optional decomposePartialApplyThunk( } /// Find the immediate member reference in the given expression. -static Optional> +static Optional> findMemberReference(Expr *expr) { if (auto declRef = dyn_cast(expr)) - return std::make_pair(declRef->getDecl(), declRef->getLoc()); + return std::make_pair(declRef->getDeclRef(), declRef->getLoc()); if (auto otherCtor = dyn_cast(expr)) { - return std::make_pair( - static_cast(otherCtor->getDecl()), otherCtor->getLoc()); + return std::make_pair(otherCtor->getDeclRef(), otherCtor->getLoc()); } return None; @@ -562,13 +569,13 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { } if (auto lookup = dyn_cast(expr)) { - checkMemberReference(lookup->getBase(), lookup->getMember().getDecl(), + checkMemberReference(lookup->getBase(), lookup->getMember(), lookup->getLoc()); return { true, expr }; } if (auto declRef = dyn_cast(expr)) { - checkNonMemberReference(declRef->getDecl(), declRef->getLoc()); + checkNonMemberReference(declRef->getDeclRef(), declRef->getLoc()); return { true, expr }; } @@ -726,6 +733,22 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { /// Get the actor isolation of the innermost relevant context. ActorIsolation getInnermostIsolatedContext(const DeclContext *constDC) { + // Retrieve the actor isolation for a declaration somewhere in our + // declaration context chain and map it into our own context so that + // the types can be compared. + auto getActorIsolation = [constDC](ValueDecl *value) { + switch (auto isolation = swift::getActorIsolation(value)) { + case ActorIsolation::ActorInstance: + case ActorIsolation::Independent: + case ActorIsolation::Unspecified: + return isolation; + + case ActorIsolation::GlobalActor: + return ActorIsolation::forGlobalActor( + constDC->mapTypeIntoContext(isolation.getGlobalActor())); + } + }; + auto dc = const_cast(constDC); while (!dc->isModuleScopeContext()) { // Look through non-escaping closures. @@ -778,7 +801,7 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { noteIsolatedActorMember(value); return true; - case ActorIsolation::GlobalActor: + case ActorIsolation::GlobalActor: { // If the global actor types are the same, we're done. if (contextIsolation.getGlobalActor()->isEqual(globalActor)) return false; @@ -789,6 +812,7 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { contextIsolation.getGlobalActor()); noteIsolatedActorMember(value); return true; + } case ActorIsolation::Independent: ctx.Diags.diagnose( @@ -804,11 +828,13 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { } /// Check a reference to a local or global. - bool checkNonMemberReference(ValueDecl *value, SourceLoc loc) { - if (!value) + bool checkNonMemberReference(ConcreteDeclRef valueRef, SourceLoc loc) { + if (!valueRef) return false; - switch (auto isolation = ActorIsolationRestriction::forDeclaration(value)) { + auto value = valueRef.getDecl(); + switch (auto isolation = + ActorIsolationRestriction::forDeclaration(valueRef)) { case ActorIsolationRestriction::Unrestricted: return false; @@ -846,12 +872,14 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { /// Check a reference with the given base expression to the given member. bool checkMemberReference( - Expr *base, ValueDecl *member, SourceLoc memberLoc, + Expr *base, ConcreteDeclRef memberRef, SourceLoc memberLoc, bool isEscapingPartialApply = false) { - if (!base || !member) + if (!base || !memberRef) return false; - switch (auto isolation = ActorIsolationRestriction::forDeclaration(member)) { + auto member = memberRef.getDecl(); + switch (auto isolation = + ActorIsolationRestriction::forDeclaration(memberRef)) { case ActorIsolationRestriction::Unrestricted: return false; diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index 477bd7a18d257..5a4869dbae150 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -25,6 +25,7 @@ namespace swift { class ActorIsolation; class ASTContext; class ClassDecl; +class ConcreteDeclRef; class Decl; class DeclContext; class Expr; @@ -138,7 +139,7 @@ class ActorIsolationRestriction { } /// Determine the isolation rules for a given declaration. - static ActorIsolationRestriction forDeclaration(Decl *decl); + static ActorIsolationRestriction forDeclaration(ConcreteDeclRef declRef); operator Kind() const { return kind; }; }; diff --git a/test/Concurrency/actor_isolation.swift b/test/Concurrency/actor_isolation.swift index c27ded3135e3f..e3932306ebcf2 100644 --- a/test/Concurrency/actor_isolation.swift +++ b/test/Concurrency/actor_isolation.swift @@ -192,6 +192,11 @@ struct SomeOtherGlobalActor { static let shared = SomeActor() } +@globalActor +struct GenericGlobalActor { + static var shared: SomeActor { SomeActor() } +} + @SomeGlobalActor func syncGlobalActorFunc() { } // expected-note 3{{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} @SomeGlobalActor func asyncGlobalActorFunc() async { } @@ -239,6 +244,27 @@ extension MyActor { } } +struct GenericStruct { + @GenericGlobalActor func f() { } // expected-note{{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} + + @GenericGlobalActor func g() { + f() // okay + } + + @GenericGlobalActor func h() { + f() // expected-error{{instance method 'f()' isolated to global actor 'GenericGlobalActor' can not be referenced from different global actor 'GenericGlobalActor'}} + } +} + +extension GenericStruct where T == String { + @GenericGlobalActor + func h2() { + f() + g() + h() + } +} + // ---------------------------------------------------------------------- // Non-actor code isolation restrictions diff --git a/test/attr/global_actor.swift b/test/attr/global_actor.swift index dc797c6f239e2..52fedc4515a62 100644 --- a/test/attr/global_actor.swift +++ b/test/attr/global_actor.swift @@ -15,6 +15,11 @@ struct GA1 { static let shared = SomeActor() } +@globalActor +struct GenericGlobalActor { + static var shared: SomeActor { SomeActor() } +} + // Ill-formed global actors. @globalActor open class GA2 { // expected-error{{global actor 'GA2' requires a static property 'shared' that produces an actor instance}}{{17-17=\n public static let shared = <#actor instance#>}} @@ -82,6 +87,11 @@ class SomeClass { @GA1 @OtherGlobalActor func twoGlobalActors() { } // expected-error{{declaration can not have multiple global actor attributes ('OtherGlobalActor' and 'GA1')}} +struct Container { + // FIXME: Diagnostic could be improved to show the generic arguments. +@GenericGlobalActor @GenericGlobalActor func twoGenericGlobalActors() { } // expected-error{{declaration can not have multiple global actor attributes ('GenericGlobalActor' and 'GenericGlobalActor')}} +} + // ----------------------------------------------------------------------- // Redundant attributes // ----------------------------------------------------------------------- From 411751218c82f22ac4d9193be3a28dc2eaf56701 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 9 Oct 2020 15:22:20 -0700 Subject: [PATCH 374/745] [Syntax] Parse attributed types in expr position as TypeExprSyntax rdar://problem/70101520 https://bugs.swift.org/browse/SR-13711 --- lib/Parse/ParseExpr.cpp | 1 + test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds | 1 + test/Syntax/round_trip_parse_gen.swift | 1 + 3 files changed, 3 insertions(+) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index e05b08cd4ece8..1ac8a45905e1f 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -421,6 +421,7 @@ ParserResult Parser::parseExprSequenceElement(Diag<> message, isType = canParseType(); } if (isType) { + SyntaxParsingContext TyExprCtx(SyntaxContext, SyntaxKind::TypeExpr); ParserResult ty = parseType(); if (ty.isNonNull()) return makeParserResult( diff --git a/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds b/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds index 61dfcac3126bb..5bb514fb29d4b 100644 --- a/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds +++ b/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds @@ -73,6 +73,7 @@ class C { func foo3() { _ = [Any]() + _ = (@convention(c) (Int) -> Void).self _ = a.a.a _ = a.b _ = 1.a diff --git a/test/Syntax/round_trip_parse_gen.swift b/test/Syntax/round_trip_parse_gen.swift index 6c68ff5aadae2..66ab03e9195b1 100644 --- a/test/Syntax/round_trip_parse_gen.swift +++ b/test/Syntax/round_trip_parse_gen.swift @@ -73,6 +73,7 @@ class C { func foo3() { _ = [Any]() + _ = (@convention(c) (Int) -> Void).self _ = a.a.a _ = a.b _ = 1.a From b440ab73315b4d4bb288078e241d974ae8a6e7d4 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Fri, 25 Sep 2020 13:48:30 -0700 Subject: [PATCH 375/745] [NFC] Move several types/functions to Import.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To help consolidate our various types describing imports, this commit moves the following types and methods to Import.h: * ImplicitImports * ImplicitStdlibKind * ImplicitImportInfo * ModuleDecl::ImportedModule * ModuleDecl::OrderImportedModules (as ImportedModule::Order) * ModuleDecl::removeDuplicateImports() (as ImportedModule::removeDuplicates()) * SourceFile::ImportFlags * SourceFile::ImportOptions * SourceFile::ImportedModuleDesc This commit is large and intentionally kept mechanical—nothing interesting to see here. --- include/swift/AST/FileUnit.h | 4 +- include/swift/AST/Import.h | 242 +++++++++++++++++- include/swift/AST/ImportCache.h | 28 +- include/swift/AST/Module.h | 103 -------- include/swift/AST/SourceFile.h | 111 +------- include/swift/AST/TypeCheckRequests.h | 19 -- include/swift/ClangImporter/ClangModule.h | 6 +- .../Serialization/SerializedModuleLoader.h | 2 +- lib/AST/ImportCache.cpp | 42 ++- lib/AST/Module.cpp | 22 +- lib/AST/ModuleNameLookup.cpp | 5 +- lib/AST/NameLookup.cpp | 3 +- lib/AST/OperatorNameLookup.cpp | 3 +- lib/ClangImporter/ClangImporter.cpp | 6 +- lib/ClangImporter/DWARFImporter.cpp | 4 +- lib/Frontend/ModuleInterfaceSupport.cpp | 18 +- lib/FrontendTool/FrontendTool.cpp | 4 +- lib/FrontendTool/ImportedModules.cpp | 2 +- lib/IDE/CodeCompletion.cpp | 8 +- lib/IDE/REPLCodeCompletion.cpp | 2 +- lib/IRGen/IRGenDebugInfo.cpp | 8 +- lib/Index/Index.cpp | 6 +- lib/Index/IndexRecord.cpp | 6 +- lib/Sema/ImportResolution.cpp | 7 +- lib/Serialization/ModuleFile.cpp | 11 +- lib/Serialization/ModuleFile.h | 4 +- lib/Serialization/Serialization.cpp | 19 +- lib/Serialization/SerializedModuleLoader.cpp | 4 +- .../lib/SwiftLang/CodeCompletionOrganizer.cpp | 4 +- .../lib/SwiftLang/SwiftASTManager.cpp | 2 +- tools/swift-ide-test/swift-ide-test.cpp | 2 +- 31 files changed, 350 insertions(+), 357 deletions(-) diff --git a/include/swift/AST/FileUnit.h b/include/swift/AST/FileUnit.h index ec8ea98f22b88..16d6e46d749dd 100644 --- a/include/swift/AST/FileUnit.h +++ b/include/swift/AST/FileUnit.h @@ -235,12 +235,12 @@ class FileUnit : public DeclContext { /// \p filter controls whether public, private, or any imports are included /// in this list. virtual void - getImportedModules(SmallVectorImpl &imports, + getImportedModules(SmallVectorImpl &imports, ModuleDecl::ImportFilter filter) const {} /// \see ModuleDecl::getImportedModulesForLookup virtual void getImportedModulesForLookup( - SmallVectorImpl &imports) const { + SmallVectorImpl &imports) const { return getImportedModules(imports, ModuleDecl::ImportFilterKind::Exported); } diff --git a/include/swift/AST/Import.h b/include/swift/AST/Import.h index 55f5bd642f0f5..6bb179543213e 100644 --- a/include/swift/AST/Import.h +++ b/include/swift/AST/Import.h @@ -21,14 +21,19 @@ #include "swift/AST/Identifier.h" #include "swift/Basic/Located.h" +#include "swift/Basic/OptionSet.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include namespace swift { class ASTContext; +class ModuleDecl; + +// MARK: - Fundamental import enums /// Describes what kind of name is being imported. /// @@ -45,6 +50,39 @@ enum class ImportKind : uint8_t { Func }; +/// Possible attributes for imports in source files. +enum class ImportFlags { + /// The imported module is exposed to anyone who imports the parent module. + Exported = 0x1, + + /// This source file has access to testable declarations in the imported + /// module. + Testable = 0x2, + + /// This source file has access to private declarations in the imported + /// module. + PrivateImport = 0x4, + + /// The imported module is an implementation detail of this file and should + /// not be required to be present if the main module is ever imported + /// elsewhere. + /// + /// Mutually exclusive with Exported. + ImplementationOnly = 0x8, + + // The module is imported to have access to named SPIs which is an + // implementation detail of this file. + SPIAccessControl = 0x10, + + /// Used for DenseMap. + Reserved = 0x80 +}; + +/// \see ImportFlags +using ImportOptions = OptionSet; + +// MARK: - Import Paths + namespace detail { using ImportPathElement = Located; using ImportPathRaw = llvm::ArrayRef; @@ -362,6 +400,208 @@ class ImportPath : public detail::ImportPathBase { } }; +// MARK: - Abstractions of imports + +/// Convenience struct to keep track of a module along with its access path. +struct alignas(uint64_t) ImportedModule { + /// The access path from an import: `import Foo.Bar` -> `Foo.Bar`. + ImportPath::Access accessPath; + /// The actual module corresponding to the import. + /// + /// Invariant: The pointer is non-null. + ModuleDecl *importedModule; + + ImportedModule(ImportPath::Access accessPath, + ModuleDecl *importedModule) + : accessPath(accessPath), importedModule(importedModule) { + assert(this->importedModule); + } + + bool operator==(const ImportedModule &other) const { + return (this->importedModule == other.importedModule) && + (this->accessPath == other.accessPath); + } + + /// Uniques the items in \p imports, ignoring the source locations of the + /// access paths. + /// + /// The order of items in \p imports is \e not preserved. + static void removeDuplicates(SmallVectorImpl &imports); + + /// Arbitrarily orders ImportedModule records, for inclusion in sets and such. + class Order { + public: + bool operator()(const ImportedModule &lhs, + const ImportedModule &rhs) const { + if (lhs.importedModule != rhs.importedModule) + return std::less()(lhs.importedModule, + rhs.importedModule); + if (lhs.accessPath.getRaw().data() != rhs.accessPath.getRaw().data()) + return std::less()(lhs.accessPath.begin(), + rhs.accessPath.begin()); + return lhs.accessPath.size() < rhs.accessPath.size(); + } + }; +}; + +struct ImportedModuleDesc { + ImportedModule module; + ImportOptions importOptions; + + // Filename for a @_private import. + StringRef filename; + + // Names of explicitly imported SPIs. + ArrayRef spiGroups; + + ImportedModuleDesc(ImportedModule module, ImportOptions options, + StringRef filename = {}, + ArrayRef spiGroups = {}) + : module(module), importOptions(options), filename(filename), + spiGroups(spiGroups) { + assert(!(importOptions.contains(ImportFlags::Exported) && + importOptions.contains(ImportFlags::ImplementationOnly)) || + importOptions.contains(ImportFlags::Reserved)); + } +}; + +// MARK: - Implicit imports + +/// A module which has been implicitly imported. +struct ImplicitImport { + ModuleDecl *Module; + ImportOptions Options; + + ImplicitImport(ModuleDecl *module, ImportOptions opts = {}) + : Module(module), Options(opts) {} + + friend bool operator==(const ImplicitImport &lhs, + const ImplicitImport &rhs) { + return lhs.Module == rhs.Module && + lhs.Options.toRaw() == rhs.Options.toRaw(); + } +}; + +void simple_display(llvm::raw_ostream &out, const ImplicitImport &import); + +/// The kind of stdlib that should be imported. +enum class ImplicitStdlibKind { + /// No standard library should be implicitly imported. + None, + + /// The Builtin module should be implicitly imported. + Builtin, + + /// The regular Swift standard library should be implicitly imported. + Stdlib +}; + +struct ImplicitImportInfo { + /// The implicit stdlib to import. + ImplicitStdlibKind StdlibKind; + + /// Whether we should attempt to import an underlying Clang half of this + /// module. + bool ShouldImportUnderlyingModule; + + /// The bridging header path for this module, empty if there is none. + StringRef BridgingHeaderPath; + + /// The names of additional modules to be implicitly imported. + SmallVector ModuleNames; + + /// An additional list of already-loaded modules which should be implicitly + /// imported. + SmallVector, 4> + AdditionalModules; + + ImplicitImportInfo() + : StdlibKind(ImplicitStdlibKind::None), + ShouldImportUnderlyingModule(false) {} +}; + +} + +// MARK: - DenseMapInfo + +namespace llvm { + +template<> +struct DenseMapInfo { + using ImportOptions = swift::ImportOptions; + + using UnsignedDMI = DenseMapInfo; + + static inline ImportOptions getEmptyKey() { + return ImportOptions(UnsignedDMI::getEmptyKey()); + } + static inline ImportOptions getTombstoneKey() { + return ImportOptions(UnsignedDMI::getTombstoneKey()); + } + static inline unsigned getHashValue(ImportOptions options) { + return UnsignedDMI::getHashValue(options.toRaw()); + } + static bool isEqual(ImportOptions a, ImportOptions b) { + return UnsignedDMI::isEqual(a.toRaw(), b.toRaw()); + } +}; + +template <> +class DenseMapInfo { + using ImportedModule = swift::ImportedModule; + using ModuleDecl = swift::ModuleDecl; +public: + static ImportedModule getEmptyKey() { + return {{}, llvm::DenseMapInfo::getEmptyKey()}; + } + static ImportedModule getTombstoneKey() { + return {{}, llvm::DenseMapInfo::getTombstoneKey()}; + } + + static unsigned getHashValue(const ImportedModule &val) { + auto pair = std::make_pair(val.accessPath.size(), val.importedModule); + return llvm::DenseMapInfo::getHashValue(pair); + } + + static bool isEqual(const ImportedModule &lhs, + const ImportedModule &rhs) { + return lhs.importedModule == rhs.importedModule && + lhs.accessPath.isSameAs(rhs.accessPath); + } +}; + +template<> +struct DenseMapInfo { + using ImportedModuleDesc = swift::ImportedModuleDesc; + + using ImportedModuleDMI = DenseMapInfo; + using ImportOptionsDMI = DenseMapInfo; + using StringRefDMI = DenseMapInfo; + + static inline ImportedModuleDesc getEmptyKey() { + return ImportedModuleDesc(ImportedModuleDMI::getEmptyKey(), + ImportOptionsDMI::getEmptyKey(), + StringRefDMI::getEmptyKey()); + } + static inline ImportedModuleDesc getTombstoneKey() { + return ImportedModuleDesc(ImportedModuleDMI::getTombstoneKey(), + ImportOptionsDMI::getTombstoneKey(), + StringRefDMI::getTombstoneKey()); + } + static inline unsigned getHashValue(const ImportedModuleDesc &import) { + return detail::combineHashValue( + ImportedModuleDMI::getHashValue(import.module), + detail::combineHashValue( + ImportOptionsDMI::getHashValue(import.importOptions), + StringRefDMI::getHashValue(import.filename))); + } + static bool isEqual(const ImportedModuleDesc &a, + const ImportedModuleDesc &b) { + return ImportedModuleDMI::isEqual(a.module, b.module) && + ImportOptionsDMI::isEqual(a.importOptions, b.importOptions) && + StringRefDMI::isEqual(a.filename, b.filename); + } +}; } #endif diff --git a/include/swift/AST/ImportCache.h b/include/swift/AST/ImportCache.h index e52e322c604a0..5334b5ddddd36 100644 --- a/include/swift/AST/ImportCache.h +++ b/include/swift/AST/ImportCache.h @@ -49,7 +49,7 @@ namespace namelookup { /// it was explicitly imported (or re-exported). class ImportSet final : public llvm::FoldingSetNode, - private llvm::TrailingObjects { + private llvm::TrailingObjects { friend TrailingObjects; friend class ImportCache; @@ -58,8 +58,8 @@ class ImportSet final : unsigned NumTransitiveImports; ImportSet(bool hasHeaderImportModule, - ArrayRef topLevelImports, - ArrayRef transitiveImports); + ArrayRef topLevelImports, + ArrayRef transitiveImports); ImportSet(const ImportSet &) = delete; void operator=(const ImportSet &) = delete; @@ -70,9 +70,9 @@ class ImportSet final : } static void Profile( llvm::FoldingSetNodeID &ID, - ArrayRef topLevelImports); + ArrayRef topLevelImports); - size_t numTrailingObjects(OverloadToken) const { + size_t numTrailingObjects(OverloadToken) const { return NumTopLevelImports + NumTransitiveImports; } @@ -83,24 +83,24 @@ class ImportSet final : return HasHeaderImportModule; } - ArrayRef getTopLevelImports() const { - return {getTrailingObjects(), + ArrayRef getTopLevelImports() const { + return {getTrailingObjects(), NumTopLevelImports}; } - ArrayRef getTransitiveImports() const { - return {getTrailingObjects() + + ArrayRef getTransitiveImports() const { + return {getTrailingObjects() + NumTopLevelImports, NumTransitiveImports}; } - ArrayRef getAllImports() const { - return {getTrailingObjects(), + ArrayRef getAllImports() const { + return {getTrailingObjects(), NumTopLevelImports + NumTransitiveImports}; } }; -class alignas(ModuleDecl::ImportedModule) ImportCache { +class alignas(ImportedModule) ImportCache { ImportCache(const ImportCache &) = delete; void operator=(const ImportCache &) = delete; @@ -121,7 +121,7 @@ class alignas(ModuleDecl::ImportedModule) ImportCache { SmallVectorImpl &results); ImportSet &getImportSet(ASTContext &ctx, - ArrayRef topLevelImports); + ArrayRef topLevelImports); public: ImportCache() {} @@ -154,7 +154,7 @@ class alignas(ModuleDecl::ImportedModule) ImportCache { } }; -ArrayRef getAllImports(const DeclContext *dc); +ArrayRef getAllImports(const DeclContext *dc); } // namespace namelookup diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index 7717bc02cfee3..3c3dcb0485995 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -154,42 +154,6 @@ enum class ResilienceStrategy : unsigned { Resilient }; -/// The kind of stdlib that should be imported. -enum class ImplicitStdlibKind { - /// No standard library should be implicitly imported. - None, - - /// The Builtin module should be implicitly imported. - Builtin, - - /// The regular Swift standard library should be implicitly imported. - Stdlib -}; - -struct ImplicitImportInfo { - /// The implicit stdlib to import. - ImplicitStdlibKind StdlibKind; - - /// Whether we should attempt to import an underlying Clang half of this - /// module. - bool ShouldImportUnderlyingModule; - - /// The bridging header path for this module, empty if there is none. - StringRef BridgingHeaderPath; - - /// The names of additional modules to be implicitly imported. - SmallVector ModuleNames; - - /// An additional list of already-loaded modules which should be implicitly - /// imported. - SmallVector, 4> - AdditionalModules; - - ImplicitImportInfo() - : StdlibKind(ImplicitStdlibKind::None), - ShouldImportUnderlyingModule(false) {} -}; - class OverlayFile; /// The minimum unit of compilation. @@ -203,42 +167,6 @@ class ModuleDecl : public DeclContext, public TypeDecl { friend class DirectPrecedenceGroupLookupRequest; public: - /// Convenience struct to keep track of a module along with its access path. - struct alignas(uint64_t) ImportedModule { - /// The access path from an import: `import Foo.Bar` -> `Foo.Bar`. - ImportPath::Access accessPath; - /// The actual module corresponding to the import. - /// - /// Invariant: The pointer is non-null. - ModuleDecl *importedModule; - - ImportedModule(ImportPath::Access accessPath, - ModuleDecl *importedModule) - : accessPath(accessPath), importedModule(importedModule) { - assert(this->importedModule); - } - - bool operator==(const ModuleDecl::ImportedModule &other) const { - return (this->importedModule == other.importedModule) && - (this->accessPath == other.accessPath); - } - }; - - /// Arbitrarily orders ImportedModule records, for inclusion in sets and such. - class OrderImportedModules { - public: - bool operator()(const ImportedModule &lhs, - const ImportedModule &rhs) const { - if (lhs.importedModule != rhs.importedModule) - return std::less()(lhs.importedModule, - rhs.importedModule); - if (lhs.accessPath.getRaw().data() != rhs.accessPath.getRaw().data()) - return std::less()(lhs.accessPath.begin(), - rhs.accessPath.begin()); - return lhs.accessPath.size() < rhs.accessPath.size(); - } - }; - /// Produces the components of a given module's full name in reverse order. /// /// For a Swift module, this will only ever have one component, but an @@ -715,12 +643,6 @@ class ModuleDecl : public DeclContext, public TypeDecl { /// This assumes that \p module was imported. bool isImportedImplementationOnly(const ModuleDecl *module) const; - /// Uniques the items in \p imports, ignoring the source locations of the - /// access paths. - /// - /// The order of items in \p imports is \e not preserved. - static void removeDuplicateImports(SmallVectorImpl &imports); - /// Finds all top-level decls of this module. /// /// This does a simple local lookup, not recursively looking through imports. @@ -887,29 +809,4 @@ inline SourceLoc extractNearestSourceLoc(const ModuleDecl *mod) { } // end namespace swift -namespace llvm { - template <> - class DenseMapInfo { - using ModuleDecl = swift::ModuleDecl; - public: - static ModuleDecl::ImportedModule getEmptyKey() { - return {{}, llvm::DenseMapInfo::getEmptyKey()}; - } - static ModuleDecl::ImportedModule getTombstoneKey() { - return {{}, llvm::DenseMapInfo::getTombstoneKey()}; - } - - static unsigned getHashValue(const ModuleDecl::ImportedModule &val) { - auto pair = std::make_pair(val.accessPath.size(), val.importedModule); - return llvm::DenseMapInfo::getHashValue(pair); - } - - static bool isEqual(const ModuleDecl::ImportedModule &lhs, - const ModuleDecl::ImportedModule &rhs) { - return lhs.importedModule == rhs.importedModule && - lhs.accessPath.isSameAs(rhs.accessPath); - } - }; -} - #endif diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index 2939ffd079cd7..f92ebbae10a42 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -14,6 +14,7 @@ #define SWIFT_AST_SOURCEFILE_H #include "swift/AST/FileUnit.h" +#include "swift/AST/Import.h" #include "swift/AST/SynthesizedFileUnit.h" #include "swift/Basic/Debug.h" #include "llvm/ADT/SetVector.h" @@ -32,58 +33,6 @@ class SourceFile final : public FileUnit { friend class ParseSourceFileRequest; public: - /// Possible attributes for imports in source files. - enum class ImportFlags { - /// The imported module is exposed to anyone who imports the parent module. - Exported = 0x1, - - /// This source file has access to testable declarations in the imported - /// module. - Testable = 0x2, - - /// This source file has access to private declarations in the imported - /// module. - PrivateImport = 0x4, - - /// The imported module is an implementation detail of this file and should - /// not be required to be present if the main module is ever imported - /// elsewhere. - /// - /// Mutually exclusive with Exported. - ImplementationOnly = 0x8, - - // The module is imported to have access to named SPIs which is an - // implementation detail of this file. - SPIAccessControl = 0x10, - - /// Used for DenseMap. - Reserved = 0x80 - }; - - /// \see ImportFlags - using ImportOptions = OptionSet; - - struct ImportedModuleDesc { - ModuleDecl::ImportedModule module; - ImportOptions importOptions; - - // Filename for a @_private import. - StringRef filename; - - // Names of explicitly imported SPIs. - ArrayRef spiGroups; - - ImportedModuleDesc(ModuleDecl::ImportedModule module, ImportOptions options, - StringRef filename = {}, - ArrayRef spiGroups = {}) - : module(module), importOptions(options), filename(filename), - spiGroups(spiGroups) { - assert(!(importOptions.contains(ImportFlags::Exported) && - importOptions.contains(ImportFlags::ImplementationOnly)) || - importOptions.contains(ImportFlags::Reserved)); - } - }; - /// Flags that direct how the source file is parsed. enum class ParsingFlags : uint8_t { /// Whether to disable delayed parsing for nominal type, extension, and @@ -447,7 +396,7 @@ class SourceFile final : public FileUnit { getOpaqueReturnTypeDecls(SmallVectorImpl &results) const override; virtual void - getImportedModules(SmallVectorImpl &imports, + getImportedModules(SmallVectorImpl &imports, ModuleDecl::ImportFilter filter) const override; virtual void @@ -660,60 +609,4 @@ inline void simple_display(llvm::raw_ostream &out, const SourceFile *SF) { } } // end namespace swift -namespace llvm { - -template<> -struct DenseMapInfo { - using ImportOptions = swift::SourceFile::ImportOptions; - - using UnsignedDMI = DenseMapInfo; - - static inline ImportOptions getEmptyKey() { - return ImportOptions(UnsignedDMI::getEmptyKey()); - } - static inline ImportOptions getTombstoneKey() { - return ImportOptions(UnsignedDMI::getTombstoneKey()); - } - static inline unsigned getHashValue(ImportOptions options) { - return UnsignedDMI::getHashValue(options.toRaw()); - } - static bool isEqual(ImportOptions a, ImportOptions b) { - return UnsignedDMI::isEqual(a.toRaw(), b.toRaw()); - } -}; - -template<> -struct DenseMapInfo { - using ImportedModuleDesc = swift::SourceFile::ImportedModuleDesc; - - using ImportedModuleDMI = DenseMapInfo; - using ImportOptionsDMI = DenseMapInfo; - using StringRefDMI = DenseMapInfo; - - static inline ImportedModuleDesc getEmptyKey() { - return ImportedModuleDesc(ImportedModuleDMI::getEmptyKey(), - ImportOptionsDMI::getEmptyKey(), - StringRefDMI::getEmptyKey()); - } - static inline ImportedModuleDesc getTombstoneKey() { - return ImportedModuleDesc(ImportedModuleDMI::getTombstoneKey(), - ImportOptionsDMI::getTombstoneKey(), - StringRefDMI::getTombstoneKey()); - } - static inline unsigned getHashValue(const ImportedModuleDesc &import) { - return detail::combineHashValue( - ImportedModuleDMI::getHashValue(import.module), - detail::combineHashValue( - ImportOptionsDMI::getHashValue(import.importOptions), - StringRefDMI::getHashValue(import.filename))); - } - static bool isEqual(const ImportedModuleDesc &a, - const ImportedModuleDesc &b) { - return ImportedModuleDMI::isEqual(a.module, b.module) && - ImportOptionsDMI::isEqual(a.importOptions, b.importOptions) && - StringRefDMI::isEqual(a.filename, b.filename); - } -}; -} - #endif diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 9546b7dfab229..4d2b1a8acc57d 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -2494,25 +2494,6 @@ class SimpleDidSetRequest } }; -/// A module which has been implicitly imported. -struct ImplicitImport { - using ImportOptions = SourceFile::ImportOptions; - - ModuleDecl *Module; - ImportOptions Options; - - ImplicitImport(ModuleDecl *module, ImportOptions opts = {}) - : Module(module), Options(opts) {} - - friend bool operator==(const ImplicitImport &lhs, - const ImplicitImport &rhs) { - return lhs.Module == rhs.Module && - lhs.Options.toRaw() == rhs.Options.toRaw(); - } -}; - -void simple_display(llvm::raw_ostream &out, const ImplicitImport &import); - /// Computes the loaded modules that should be implicitly imported by each file /// of a given module. class ModuleImplicitImportsRequest diff --git a/include/swift/ClangImporter/ClangModule.h b/include/swift/ClangImporter/ClangModule.h index c79fb8c04ece4..6541f1fcacdce 100644 --- a/include/swift/ClangImporter/ClangModule.h +++ b/include/swift/ClangImporter/ClangModule.h @@ -36,7 +36,7 @@ class ClangModuleUnit final : public LoadedFile { ClangImporter::Implementation &owner; const clang::Module *clangModule; llvm::PointerIntPair overlayModule; - mutable Optional> importedModulesForLookup; + mutable Optional> importedModulesForLookup; /// The metadata of the underlying Clang module. clang::ASTSourceDescriptor ASTSourceDescriptor; @@ -92,11 +92,11 @@ class ClangModuleUnit final : public LoadedFile { virtual void getDisplayDecls(SmallVectorImpl &results) const override; virtual void - getImportedModules(SmallVectorImpl &imports, + getImportedModules(SmallVectorImpl &imports, ModuleDecl::ImportFilter filter) const override; virtual void getImportedModulesForLookup( - SmallVectorImpl &imports) const override; + SmallVectorImpl &imports) const override; virtual void collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const override; diff --git a/include/swift/Serialization/SerializedModuleLoader.h b/include/swift/Serialization/SerializedModuleLoader.h index e0aa714fd3914..59e813857f324 100644 --- a/include/swift/Serialization/SerializedModuleLoader.h +++ b/include/swift/Serialization/SerializedModuleLoader.h @@ -414,7 +414,7 @@ class SerializedASTFile final : public LoadedFile { virtual void getDisplayDecls(SmallVectorImpl &results) const override; virtual void - getImportedModules(SmallVectorImpl &imports, + getImportedModules(SmallVectorImpl &imports, ModuleDecl::ImportFilter filter) const override; virtual void diff --git a/lib/AST/ImportCache.cpp b/lib/AST/ImportCache.cpp index a9a893934b824..af6e922ba0773 100644 --- a/lib/AST/ImportCache.cpp +++ b/lib/AST/ImportCache.cpp @@ -26,19 +26,19 @@ using namespace swift; using namespace namelookup; ImportSet::ImportSet(bool hasHeaderImportModule, - ArrayRef topLevelImports, - ArrayRef transitiveImports) + ArrayRef topLevelImports, + ArrayRef transitiveImports) : HasHeaderImportModule(hasHeaderImportModule), NumTopLevelImports(topLevelImports.size()), NumTransitiveImports(transitiveImports.size()) { - auto buffer = getTrailingObjects(); + auto buffer = getTrailingObjects(); std::uninitialized_copy(topLevelImports.begin(), topLevelImports.end(), buffer); std::uninitialized_copy(transitiveImports.begin(), transitiveImports.end(), buffer + topLevelImports.size()); #ifndef NDEBUG - llvm::SmallDenseSet unique; + llvm::SmallDenseSet unique; for (auto import : topLevelImports) { auto result = unique.insert(import).second; assert(result && "Duplicate imports in import set"); @@ -52,7 +52,7 @@ ImportSet::ImportSet(bool hasHeaderImportModule, void ImportSet::Profile( llvm::FoldingSetNodeID &ID, - ArrayRef topLevelImports) { + ArrayRef topLevelImports) { ID.AddInteger(topLevelImports.size()); for (auto import : topLevelImports) { ID.AddInteger(import.accessPath.size()); @@ -63,9 +63,9 @@ void ImportSet::Profile( } } -static void collectExports(ModuleDecl::ImportedModule next, - SmallVectorImpl &stack) { - SmallVector exports; +static void collectExports(ImportedModule next, + SmallVectorImpl &stack) { + SmallVector exports; next.importedModule->getImportedModulesForLookup(exports); for (auto exported : exports) { if (next.accessPath.empty()) @@ -81,16 +81,16 @@ static void collectExports(ModuleDecl::ImportedModule next, ImportSet & ImportCache::getImportSet(ASTContext &ctx, - ArrayRef imports) { + ArrayRef imports) { bool hasHeaderImportModule = false; ModuleDecl *headerImportModule = nullptr; if (auto *loader = ctx.getClangModuleLoader()) headerImportModule = loader->getImportedHeaderModule(); - SmallVector topLevelImports; + SmallVector topLevelImports; - SmallVector transitiveImports; - llvm::SmallDenseSet visited; + SmallVector transitiveImports; + llvm::SmallDenseSet visited; for (auto next : imports) { if (!visited.insert(next).second) @@ -115,7 +115,7 @@ ImportCache::getImportSet(ASTContext &ctx, if (ctx.Stats) ++ctx.Stats->getFrontendCounters().ImportSetFoldMiss; - SmallVector stack; + SmallVector stack; for (auto next : topLevelImports) { collectExports(next, stack); } @@ -139,7 +139,7 @@ ImportCache::getImportSet(ASTContext &ctx, if (ImportSet *result = ImportSets.FindNodeOrInsertPos(ID, InsertPos)) return *result; - size_t bytes = ImportSet::totalSizeToAlloc(topLevelImports.size() + transitiveImports.size()); + size_t bytes = ImportSet::totalSizeToAlloc(topLevelImports.size() + transitiveImports.size()); void *mem = ctx.Allocate(bytes, alignof(ImportSet), AllocationArena::Permanent); auto *result = new (mem) ImportSet(hasHeaderImportModule, @@ -169,10 +169,9 @@ ImportSet &ImportCache::getImportSet(const DeclContext *dc) { if (ctx.Stats) ++ctx.Stats->getFrontendCounters().ImportSetCacheMiss; - SmallVector imports; + SmallVector imports; - imports.emplace_back( - ModuleDecl::ImportedModule{ImportPath::Access(), mod}); + imports.emplace_back(ImportPath::Access(), mod); if (file) { // Should include both SPI & non-SPI. @@ -254,11 +253,10 @@ ImportCache::getAllAccessPathsNotShadowedBy(const ModuleDecl *mod, if (ctx.Stats) ++ctx.Stats->getFrontendCounters().ModuleShadowCacheMiss; - SmallVector stack; - llvm::SmallDenseSet visited; + SmallVector stack; + llvm::SmallDenseSet visited; - stack.emplace_back( - ModuleDecl::ImportedModule{ImportPath::Access(), currentMod}); + stack.emplace_back(ImportPath::Access(), currentMod); if (auto *file = dyn_cast(dc)) { // Should include both SPI & non-SPI @@ -297,7 +295,7 @@ ImportCache::getAllAccessPathsNotShadowedBy(const ModuleDecl *mod, return result; }; -ArrayRef +ArrayRef swift::namelookup::getAllImports(const DeclContext *dc) { return dc->getASTContext().getImportCache().getImportSet(dc) .getAllImports(); diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 135efbd415b97..ac0c4bcf9d868 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -1175,7 +1175,7 @@ void ModuleDecl::getImportedModules(SmallVectorImpl &modules, } void -SourceFile::getImportedModules(SmallVectorImpl &modules, +SourceFile::getImportedModules(SmallVectorImpl &modules, ModuleDecl::ImportFilter filter) const { // FIXME: Ideally we should assert that the file has had its imports resolved // before calling this function. However unfortunately that can cause issues @@ -1281,7 +1281,7 @@ ModuleDecl::ReverseFullNameIterator::printForward(raw_ostream &out, } void -ModuleDecl::removeDuplicateImports(SmallVectorImpl &imports) { +ImportedModule::removeDuplicates(SmallVectorImpl &imports) { std::sort(imports.begin(), imports.end(), [](const ImportedModule &lhs, const ImportedModule &rhs) -> bool { // Arbitrarily sort by name to get a deterministic order. @@ -1451,7 +1451,7 @@ void ModuleDecl::collectLinkLibraries(LinkLibraryCallback callback) const { void SourceFile::collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const { llvm::SmallDenseSet visited; - SmallVector stack; + SmallVector stack; ModuleDecl::ImportFilter filter = { ModuleDecl::ImportFilterKind::Exported, @@ -1465,9 +1465,7 @@ SourceFile::collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const topLevel->getImportedModules(stack, topLevelFilter); // Make sure the top-level module is first; we want pre-order-ish traversal. - auto topLevelModule = - ModuleDecl::ImportedModule{ImportPath::Access(), topLevel}; - stack.emplace_back(topLevelModule); + stack.emplace_back(ImportPath::Access(), topLevel); while (!stack.empty()) { auto next = stack.pop_back_val().importedModule; @@ -1685,8 +1683,8 @@ ModuleDecl::getDeclaringModuleAndBystander() { // Search the transitive set of imported @_exported modules to see if any have // this module as their overlay. SmallPtrSet seen; - SmallVector imported; - SmallVector furtherImported; + SmallVector imported; + SmallVector furtherImported; ModuleDecl *overlayModule = this; getImportedModules(imported, ModuleDecl::ImportFilterKind::Exported); @@ -1891,10 +1889,8 @@ void SourceFile::setImports(ArrayRef imports) { bool HasImplementationOnlyImportsRequest::evaluate(Evaluator &evaluator, SourceFile *SF) const { - using ModuleDesc = SourceFile::ImportedModuleDesc; - return llvm::any_of(SF->getImports(), [](ModuleDesc desc) { - return desc.importOptions.contains( - SourceFile::ImportFlags::ImplementationOnly); + return llvm::any_of(SF->getImports(), [](ImportedModuleDesc desc) { + return desc.importOptions.contains(ImportFlags::ImplementationOnly); }); } @@ -2001,7 +1997,7 @@ bool ModuleDecl::isImportedImplementationOnly(const ModuleDecl *module) const { ModuleDecl::ImportFilterKind::Default, ModuleDecl::ImportFilterKind::SPIAccessControl, ModuleDecl::ImportFilterKind::ShadowedByCrossImportOverlay}; - SmallVector results; + SmallVector results; getImportedModules(results, filter); for (auto &desc : results) { diff --git a/lib/AST/ModuleNameLookup.cpp b/lib/AST/ModuleNameLookup.cpp index af29ee62144e3..56ea9383c6d29 100644 --- a/lib/AST/ModuleNameLookup.cpp +++ b/lib/AST/ModuleNameLookup.cpp @@ -182,7 +182,7 @@ void ModuleNameLookup::lookupInModule( if (!canReturnEarly) { auto &imports = ctx.getImportCache().getImportSet(moduleOrFile); - auto visitImport = [&](ModuleDecl::ImportedModule import, + auto visitImport = [&](ImportedModule import, const DeclContext *moduleScopeContext) { if (import.accessPath.empty()) import.accessPath = accessPath; @@ -202,8 +202,7 @@ void ModuleNameLookup::lookupInModule( if (auto *loader = ctx.getClangModuleLoader()) { headerImportModule = loader->getImportedHeaderModule(); if (headerImportModule) { - ModuleDecl::ImportedModule import{ImportPath::Access(), - headerImportModule}; + ImportedModule import{ImportPath::Access(), headerImportModule}; visitImport(import, nullptr); } } diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index c448198233fec..17cf849302574 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -290,8 +290,7 @@ static void recordShadowedDeclsAfterTypeMatch( auto file = dc->getParentSourceFile(); if (!file) return false; for (const auto &import : file->getImports()) { - if (import.importOptions.contains( - SourceFile::ImportFlags::PrivateImport) + if (import.importOptions.contains(ImportFlags::PrivateImport) && import.module.importedModule == module && import.module.accessPath.matches(name)) return true; diff --git a/lib/AST/OperatorNameLookup.cpp b/lib/AST/OperatorNameLookup.cpp index b074fbf30f630..e05e3347de08b 100644 --- a/lib/AST/OperatorNameLookup.cpp +++ b/lib/AST/OperatorNameLookup.cpp @@ -83,8 +83,7 @@ static TinyPtrVector lookupOperatorImpl( if (!visitedModules.insert(mod).second) continue; - bool isExported = - import.importOptions.contains(SourceFile::ImportFlags::Exported); + bool isExported = import.importOptions.contains(ImportFlags::Exported); if (!includePrivate && !isExported) continue; diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 0720569da9558..37ad1a8ce665e 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3434,7 +3434,7 @@ ModuleDecl *ClangModuleUnit::getOverlayModule() const { } void ClangModuleUnit::getImportedModules( - SmallVectorImpl &imports, + SmallVectorImpl &imports, ModuleDecl::ImportFilter filter) const { // Bail out if we /only/ want ImplementationOnly imports; Clang modules never // have any of these. @@ -3505,7 +3505,7 @@ void ClangModuleUnit::getImportedModules( } void ClangModuleUnit::getImportedModulesForLookup( - SmallVectorImpl &imports) const { + SmallVectorImpl &imports) const { // Reuse our cached list of imports if we have one. if (importedModulesForLookup.hasValue()) { @@ -3530,7 +3530,7 @@ void ClangModuleUnit::getImportedModulesForLookup( } if (imported.empty()) { - importedModulesForLookup = ArrayRef(); + importedModulesForLookup = ArrayRef(); return; } diff --git a/lib/ClangImporter/DWARFImporter.cpp b/lib/ClangImporter/DWARFImporter.cpp index 79bb6eeea19e4..8b635d1ce5ed6 100644 --- a/lib/ClangImporter/DWARFImporter.cpp +++ b/lib/ClangImporter/DWARFImporter.cpp @@ -67,11 +67,11 @@ class DWARFModuleUnit final : public LoadedFile { getDisplayDecls(SmallVectorImpl &results) const override {} virtual void - getImportedModules(SmallVectorImpl &imports, + getImportedModules(SmallVectorImpl &imports, ModuleDecl::ImportFilter filter) const override {} virtual void getImportedModulesForLookup( - SmallVectorImpl &imports) const override {}; + SmallVectorImpl &imports) const override {}; virtual void collectLinkLibraries( ModuleDecl::LinkLibraryCallback callback) const override {}; diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index 52eb640375d99..4c442fd32feec 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -46,8 +46,8 @@ version::Version swift::InterfaceFormatVersion({1, 0}); /// /// These come from declarations like `import class FooKit.MainFooController`. static void diagnoseScopedImports(DiagnosticEngine &diags, - ArrayRef imports){ - for (const ModuleDecl::ImportedModule &importPair : imports) { + ArrayRef imports){ + for (const ImportedModule &importPair : imports) { if (importPair.accessPath.empty()) continue; diags.diagnose(importPair.accessPath.front().Loc, @@ -109,29 +109,27 @@ static void printImports(raw_ostream &out, // When printing the private swiftinterface file, print implementation-only // imports only if they are also SPI. First, list all implementation-only // imports and filter them later. - llvm::SmallSet ioiImportSet; + llvm::SmallSet ioiImportSet; if (Opts.PrintSPIs && Opts.ExperimentalSPIImports) { allImportFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly; - SmallVector ioiImport; + SmallVector ioiImport; M->getImportedModules(ioiImport, {ModuleDecl::ImportFilterKind::ImplementationOnly, ModuleDecl::ImportFilterKind::SPIAccessControl}); ioiImportSet.insert(ioiImport.begin(), ioiImport.end()); } - SmallVector allImports; + SmallVector allImports; M->getImportedModules(allImports, allImportFilter); - ModuleDecl::removeDuplicateImports(allImports); + ImportedModule::removeDuplicates(allImports); diagnoseScopedImports(M->getASTContext().Diags, allImports); // Collect the public imports as a subset so that we can mark them with // '@_exported'. - SmallVector publicImports; + SmallVector publicImports; M->getImportedModules(publicImports, ModuleDecl::ImportFilterKind::Exported); - llvm::SmallSet publicImportSet; + llvm::SmallSet publicImportSet; publicImportSet.insert(publicImports.begin(), publicImports.end()); diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 39231330e0c30..65ed97bc973aa 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -318,10 +318,10 @@ static void getImmediateImports( ModuleDecl::ImportFilterKind::SPIAccessControl, ModuleDecl::ImportFilterKind::ShadowedByCrossImportOverlay }) { - SmallVector importList; + SmallVector importList; module->getImportedModules(importList, importFilter); - for (ModuleDecl::ImportedModule &import : importList) + for (ImportedModule &import : importList) imports.insert(import.importedModule); } diff --git a/lib/FrontendTool/ImportedModules.cpp b/lib/FrontendTool/ImportedModules.cpp index 4ced1a7c5c8aa..857efcda8f98b 100644 --- a/lib/FrontendTool/ImportedModules.cpp +++ b/lib/FrontendTool/ImportedModules.cpp @@ -78,7 +78,7 @@ bool swift::emitImportedModules(ModuleDecl *mainModule, StringRef implicitHeaderPath = opts.ImplicitObjCHeaderPath; if (!implicitHeaderPath.empty()) { if (!clangImporter->importBridgingHeader(implicitHeaderPath, mainModule)) { - SmallVector imported; + SmallVector imported; clangImporter->getImportedHeaderModule()->getImportedModules( imported, {ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default, diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 4fc0b5b149841..8079cd00c93d9 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -2036,8 +2036,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { } void collectImportedModules(llvm::StringSet<> &ImportedModules) { - SmallVector Imported; - SmallVector FurtherImported; + SmallVector Imported; + SmallVector FurtherImported; CurrDeclContext->getParentSourceFile()->getImportedModules( Imported, {ModuleDecl::ImportFilterKind::Exported, @@ -5923,7 +5923,7 @@ static void deliverCompletionResults(CodeCompletionContext &CompletionContext, for (auto &Request: Lookup.RequestedCachedResults) { llvm::DenseSet ImportsSeen; - auto handleImport = [&](ModuleDecl::ImportedModule Import) { + auto handleImport = [&](ImportedModule Import) { ModuleDecl *TheModule = Import.importedModule; ImportPath::Access Path = Import.accessPath; if (TheModule->getFiles().empty()) @@ -5991,7 +5991,7 @@ static void deliverCompletionResults(CodeCompletionContext &CompletionContext, Lookup.addModuleName(curModule); // Add results for all imported modules. - SmallVector Imports; + SmallVector Imports; SF.getImportedModules( Imports, {ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default, diff --git a/lib/IDE/REPLCodeCompletion.cpp b/lib/IDE/REPLCodeCompletion.cpp index caf7aef65bf70..1c7ffa17883b6 100644 --- a/lib/IDE/REPLCodeCompletion.cpp +++ b/lib/IDE/REPLCodeCompletion.cpp @@ -225,7 +225,7 @@ doCodeCompletion(SourceFile &SF, StringRef EnteredCode, unsigned *BufferID, /*exported*/ false); // Carry over the private imports from the last module. - SmallVector imports; + SmallVector imports; lastModule->getImportedModules(imports, ModuleDecl::ImportFilterKind::Default); for (auto &import : imports) { diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index 5d91564f12c26..d462676af3db3 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -661,7 +661,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { } void createImportedModule(llvm::DIScope *Context, - ModuleDecl::ImportedModule M, llvm::DIFile *File, + ImportedModule M, llvm::DIFile *File, unsigned Line) { // For overlays of Clang modules also emit an import of the underlying Clang // module. The helps the debugger resolve types that are present only in the @@ -761,7 +761,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { return None; } - llvm::DIModule *getOrCreateModule(ModuleDecl::ImportedModule IM) { + llvm::DIModule *getOrCreateModule(ImportedModule IM) { ModuleDecl *M = IM.importedModule; if (Optional ModuleDesc = getClangModule(*M)) return getOrCreateModule(*ModuleDesc, ModuleDesc->getModuleOrNull()); @@ -1882,7 +1882,7 @@ void IRGenDebugInfoImpl::finalize() { // Get the list of imported modules (which may actually be different // from all ImportDecls). - SmallVector ModuleWideImports; + SmallVector ModuleWideImports; IGM.getSwiftModule()->getImportedModules( ModuleWideImports, {ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default, @@ -2129,7 +2129,7 @@ void IRGenDebugInfoImpl::emitImport(ImportDecl *D) { return; assert(D->getModule() && "compiler-synthesized ImportDecl is incomplete"); - ModuleDecl::ImportedModule Imported = { D->getAccessPath(), D->getModule() }; + ImportedModule Imported = { D->getAccessPath(), D->getModule() }; auto L = getDebugLoc(*this, D); auto *File = getOrCreateFile(L.Filename); createImportedModule(File, Imported, File, L.Line); diff --git a/lib/Index/Index.cpp b/lib/Index/Index.cpp index f26580b5d3324..e8e6fab61ddc9 100644 --- a/lib/Index/Index.cpp +++ b/lib/Index/Index.cpp @@ -124,7 +124,7 @@ class SourceFileOrModule { } void - getImportedModules(SmallVectorImpl &Modules) const { + getImportedModules(SmallVectorImpl &Modules) const { constexpr ModuleDecl::ImportFilter ImportFilter = { ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default, @@ -715,7 +715,7 @@ bool IndexSwiftASTWalker::visitImports( if (!IsNew) return true; - SmallVector Imports; + SmallVector Imports; TopMod.getImportedModules(Imports); llvm::SmallPtrSet Reported; @@ -1602,7 +1602,7 @@ void IndexSwiftASTWalker::collectRecursiveModuleImports( ImportFilter |= ModuleDecl::ImportFilterKind::Exported; ImportFilter |= ModuleDecl::ImportFilterKind::Default; // FIXME: ImportFilterKind::ShadowedByCrossImportOverlay? - SmallVector Imports; + SmallVector Imports; TopMod.getImportedModules(Imports); for (auto Import : Imports) { diff --git a/lib/Index/IndexRecord.cpp b/lib/Index/IndexRecord.cpp index 9beed7ea04ca0..4d0706ff6fa6e 100644 --- a/lib/Index/IndexRecord.cpp +++ b/lib/Index/IndexRecord.cpp @@ -383,7 +383,7 @@ emitDataForSwiftSerializedModule(ModuleDecl *module, IndexUnitWriter &parentUnitWriter, SourceFile *initialFile); -static void addModuleDependencies(ArrayRef imports, +static void addModuleDependencies(ArrayRef imports, StringRef indexStorePath, bool indexSystemModules, bool skipStdlib, @@ -580,7 +580,7 @@ emitDataForSwiftSerializedModule(ModuleDecl *module, unitWriter.addRecordFile(recordFile, *FE, isSystemModule, mod); } - SmallVector imports; + SmallVector imports; module->getImportedModules(imports, {ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default}); StringScratchSpace moduleNameScratch; @@ -619,7 +619,7 @@ recordSourceFileUnit(SourceFile *primarySourceFile, StringRef indexUnitToken, getModuleInfoFromOpaqueModule); // Module dependencies. - SmallVector imports; + SmallVector imports; primarySourceFile->getImportedModules( imports, {ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default, diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index 9b2a4362eaa3f..8583c03d3ea25 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -43,11 +43,6 @@ using namespace swift; // MARK: ImportResolver and supporting types //===----------------------------------------------------------------------===// -using ImportedModule = ModuleDecl::ImportedModule; -using ImportedModuleDesc = SourceFile::ImportedModuleDesc; -using ImportOptions = SourceFile::ImportOptions; -using ImportFlags = SourceFile::ImportFlags; - namespace { /// Represents an import which the ImportResolver knows exists, but which has /// not yet had its options checked, module loaded, or cross-imports found. @@ -520,7 +515,7 @@ UnboundImport::UnboundImport(ImportDecl *ID) SmallVector spiGroups; for (auto attr : ID->getAttrs().getAttributes()) { - options |= SourceFile::ImportFlags::SPIAccessControl; + options |= ImportFlags::SPIAccessControl; auto attrSPIs = attr->getSPIGroups(); spiGroups.append(attrSPIs.begin(), attrSPIs.end()); } diff --git a/lib/Serialization/ModuleFile.cpp b/lib/Serialization/ModuleFile.cpp index 5bd056555121f..31e559be687bf 100644 --- a/lib/Serialization/ModuleFile.cpp +++ b/lib/Serialization/ModuleFile.cpp @@ -167,8 +167,8 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc) { return error(Status::FailedToLoadBridgingHeader); } ModuleDecl *importedHeaderModule = clangImporter->getImportedHeaderModule(); - dependency.Import = ModuleDecl::ImportedModule{ImportPath::Access(), - importedHeaderModule}; + dependency.Import = ImportedModule{ImportPath::Access(), + importedHeaderModule}; continue; } @@ -212,7 +212,7 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc) { continue; } - dependency.Import = ModuleDecl::ImportedModule{accessPath, module}; + dependency.Import = ImportedModule{accessPath, module}; // SPI StringRef spisStr = dependency.Core.RawSPIs; @@ -420,9 +420,8 @@ PrecedenceGroupDecl *ModuleFile::lookupPrecedenceGroup(Identifier name) { return cast(getDecl(data[0].second)); } -void ModuleFile::getImportedModules( - SmallVectorImpl &results, - ModuleDecl::ImportFilter filter) { +void ModuleFile::getImportedModules(SmallVectorImpl &results, + ModuleDecl::ImportFilter filter) { PrettyStackTraceModuleFile stackEntry(*this); for (auto &dep : Dependencies) { diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index 7749f82adee91..dfa5594eaf46f 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -90,7 +90,7 @@ class ModuleFile public: const ModuleFileSharedCore::Dependency &Core; - llvm::Optional Import = llvm::None; + llvm::Optional Import = llvm::None; SmallVector spiGroups; Dependency(const ModuleFileSharedCore::Dependency &coreDependency) @@ -527,7 +527,7 @@ class ModuleFile PrecedenceGroupDecl *lookupPrecedenceGroup(Identifier name); /// Adds any imported modules to the given vector. - void getImportedModules(SmallVectorImpl &results, + void getImportedModules(SmallVectorImpl &results, ModuleDecl::ImportFilter filter); void getImportDecls(SmallVectorImpl &Results); diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index ad6e002234df8..f5f2830ae446a 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -953,7 +953,7 @@ void Serializer::writeHeader(const SerializationOptions &options) { } } -static void flattenImportPath(const ModuleDecl::ImportedModule &import, +static void flattenImportPath(const ImportedModule &import, SmallVectorImpl &out) { llvm::raw_svector_ostream outStream(out); import.importedModule->getReverseFullModuleName().printForward( @@ -974,11 +974,10 @@ uint64_t getRawModTimeOrHash(const SerializationOptions::FileDependency &dep) { return dep.getModificationTime(); } -using ImportSet = llvm::SmallSet; +using ImportSet = llvm::SmallSet; static ImportSet getImportsAsSet(const ModuleDecl *M, ModuleDecl::ImportFilter filter) { - SmallVector imports; + SmallVector imports; M->getImportedModules(imports, filter); ImportSet importSet; importSet.insert(imports.begin(), imports.end()); @@ -987,7 +986,7 @@ static ImportSet getImportsAsSet(const ModuleDecl *M, void Serializer::writeInputBlock(const SerializationOptions &options) { BCBlockRAII restoreBlock(Out, INPUT_BLOCK_ID, 4); - input_block::ImportedModuleLayout ImportedModule(Out); + input_block::ImportedModuleLayout importedModule(Out); input_block::ImportedModuleLayoutSPI ImportedModuleSPI(Out); input_block::LinkLibraryLayout LinkLibrary(Out); input_block::ImportedHeaderLayout ImportedHeader(Out); @@ -1031,13 +1030,13 @@ void Serializer::writeInputBlock(const SerializationOptions &options) { if (!options.ModuleInterface.empty()) ModuleInterface.emit(ScratchRecord, options.ModuleInterface); - SmallVector allImports; + SmallVector allImports; M->getImportedModules(allImports, {ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default, ModuleDecl::ImportFilterKind::ImplementationOnly, ModuleDecl::ImportFilterKind::SPIAccessControl}); - ModuleDecl::removeDuplicateImports(allImports); + ImportedModule::removeDuplicates(allImports); // Collect the public and private imports as a subset so that we can // distinguish them. @@ -1055,8 +1054,8 @@ void Serializer::writeInputBlock(const SerializationOptions &options) { auto clangImporter = static_cast(M->getASTContext().getClangModuleLoader()); ModuleDecl *bridgingHeaderModule = clangImporter->getImportedHeaderModule(); - ModuleDecl::ImportedModule bridgingHeaderImport{ImportPath::Access(), - bridgingHeaderModule}; + ImportedModule bridgingHeaderImport{ImportPath::Access(), + bridgingHeaderModule}; // Make sure the bridging header module is always at the top of the import // list, mimicking how it is processed before any module imports when @@ -1104,7 +1103,7 @@ void Serializer::writeInputBlock(const SerializationOptions &options) { llvm::SmallSetVector spis; M->lookupImportedSPIGroups(import.importedModule, spis); - ImportedModule.emit(ScratchRecord, + importedModule.emit(ScratchRecord, static_cast(stableImportControl), !import.accessPath.empty(), !spis.empty(), importPath); diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index c3ae09130cf70..30e5baaa778e2 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -1109,14 +1109,14 @@ void SerializedModuleLoaderBase::verifyAllModules() { //----------------------------------------------------------------------------- void SerializedASTFile::getImportedModules( - SmallVectorImpl &imports, + SmallVectorImpl &imports, ModuleDecl::ImportFilter filter) const { File.getImportedModules(imports, filter); } void SerializedASTFile::collectLinkLibrariesFromImports( ModuleDecl::LinkLibraryCallback callback) const { - llvm::SmallVector Imports; + llvm::SmallVector Imports; File.getImportedModules(Imports, {ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default}); diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp index 13a1b571e34c3..731ba21165ffa 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp @@ -354,7 +354,7 @@ ImportDepth::ImportDepth(ASTContext &context, // Private imports from this module. // FIXME: only the private imports from the current source file. // FIXME: ImportFilterKind::ShadowedByCrossImportOverlay? - SmallVector mainImports; + SmallVector mainImports; main->getImportedModules(mainImports, {ModuleDecl::ImportFilterKind::Default, ModuleDecl::ImportFilterKind::ImplementationOnly}); @@ -384,7 +384,7 @@ ImportDepth::ImportDepth(ASTContext &context, } // Add imports to the worklist. - SmallVector imports; + SmallVector imports; module->getImportedModules(imports); for (auto &import : imports) { uint8_t next = std::max(depth, uint8_t(depth + 1)); // unsigned wrap diff --git a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp index e90b71447c525..2620dfb3be2bb 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp @@ -754,7 +754,7 @@ static void collectModuleDependencies(ModuleDecl *TopMod, ImportFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly; } // FIXME: ImportFilterKind::ShadowedByCrossImportOverlay? - SmallVector Imports; + SmallVector Imports; TopMod->getImportedModules(Imports, ImportFilter); for (auto Import : Imports) { diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 2bc1c5206ab80..d0d874bbc2d17 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -3176,7 +3176,7 @@ static int doPrintModuleImports(const CompilerInvocation &InitInvok, continue; } - SmallVector scratch; + SmallVector scratch; for (auto next : namelookup::getAllImports(M)) { llvm::outs() << next.importedModule->getName(); if (next.importedModule->isNonSwiftModule()) From f3d99cefbb63960fdcd2b177b418d2a9a43930df Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Sat, 12 Sep 2020 17:09:30 -0700 Subject: [PATCH 376/745] [NFC] Turn ImportedModuleDesc into AttributedImport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename ImportedModuleDesc to AttributedImport and make it a template that’s parameterized on the representation of the module. This will allow us to reduce duplicated representations of “abstract” ImportDecls. --- include/swift/AST/Import.h | 52 +++++++++++++++------------- include/swift/AST/SourceFile.h | 8 +++-- lib/AST/Module.cpp | 24 ++++++------- lib/Sema/ImportResolution.cpp | 62 ++++++++++++++++++---------------- 4 files changed, 78 insertions(+), 68 deletions(-) diff --git a/include/swift/AST/Import.h b/include/swift/AST/Import.h index 6bb179543213e..5482e714b10af 100644 --- a/include/swift/AST/Import.h +++ b/include/swift/AST/Import.h @@ -70,8 +70,8 @@ enum class ImportFlags { /// Mutually exclusive with Exported. ImplementationOnly = 0x8, - // The module is imported to have access to named SPIs which is an - // implementation detail of this file. + /// The module is imported to have access to named SPIs which is an + /// implementation detail of this file. SPIAccessControl = 0x10, /// Used for DenseMap. @@ -444,8 +444,10 @@ struct alignas(uint64_t) ImportedModule { }; }; -struct ImportedModuleDesc { - ImportedModule module; +template +struct AttributedImport { + ModuleInfo module; + ImportOptions importOptions; // Filename for a @_private import. @@ -454,9 +456,8 @@ struct ImportedModuleDesc { // Names of explicitly imported SPIs. ArrayRef spiGroups; - ImportedModuleDesc(ImportedModule module, ImportOptions options, - StringRef filename = {}, - ArrayRef spiGroups = {}) + AttributedImport(ModuleInfo module, ImportOptions options, + StringRef filename = {}, ArrayRef spiGroups = {}) : module(module), importOptions(options), filename(filename), spiGroups(spiGroups) { assert(!(importOptions.contains(ImportFlags::Exported) && @@ -465,6 +466,8 @@ struct ImportedModuleDesc { } }; +using ImportedModuleDesc = AttributedImport; + // MARK: - Implicit imports /// A module which has been implicitly imported. @@ -570,34 +573,35 @@ class DenseMapInfo { } }; -template<> -struct DenseMapInfo { - using ImportedModuleDesc = swift::ImportedModuleDesc; +template +struct DenseMapInfo> { + using AttributedImport = swift::AttributedImport; - using ImportedModuleDMI = DenseMapInfo; + using ModuleInfoDMI = DenseMapInfo; using ImportOptionsDMI = DenseMapInfo; using StringRefDMI = DenseMapInfo; + // FIXME: SPI groups not used by DenseMapInfo??? - static inline ImportedModuleDesc getEmptyKey() { - return ImportedModuleDesc(ImportedModuleDMI::getEmptyKey(), - ImportOptionsDMI::getEmptyKey(), - StringRefDMI::getEmptyKey()); + static inline AttributedImport getEmptyKey() { + return AttributedImport(ModuleInfoDMI::getEmptyKey(), + ImportOptionsDMI::getEmptyKey(), + StringRefDMI::getEmptyKey()); } - static inline ImportedModuleDesc getTombstoneKey() { - return ImportedModuleDesc(ImportedModuleDMI::getTombstoneKey(), - ImportOptionsDMI::getTombstoneKey(), - StringRefDMI::getTombstoneKey()); + static inline AttributedImport getTombstoneKey() { + return AttributedImport(ModuleInfoDMI::getTombstoneKey(), + ImportOptionsDMI::getTombstoneKey(), + StringRefDMI::getTombstoneKey()); } - static inline unsigned getHashValue(const ImportedModuleDesc &import) { + static inline unsigned getHashValue(const AttributedImport &import) { return detail::combineHashValue( - ImportedModuleDMI::getHashValue(import.module), + ModuleInfoDMI::getHashValue(import.module), detail::combineHashValue( ImportOptionsDMI::getHashValue(import.importOptions), StringRefDMI::getHashValue(import.filename))); } - static bool isEqual(const ImportedModuleDesc &a, - const ImportedModuleDesc &b) { - return ImportedModuleDMI::isEqual(a.module, b.module) && + static bool isEqual(const AttributedImport &a, + const AttributedImport &b) { + return ModuleInfoDMI::isEqual(a.module, b.module) && ImportOptionsDMI::isEqual(a.importOptions, b.importOptions) && StringRefDMI::isEqual(a.filename, b.filename); } diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index f92ebbae10a42..5679e5d5f76de 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -82,7 +82,7 @@ class SourceFile final : public FileUnit { /// This is the list of modules that are imported by this module. /// /// This is \c None until it is filled in by the import resolution phase. - Optional> Imports; + Optional>> Imports; /// A unique identifier representing this file; used to mark private decls /// within the file to keep them from conflicting with other files in the @@ -288,11 +288,13 @@ class SourceFile final : public FileUnit { ~SourceFile(); /// Retrieve an immutable view of the source file's imports. - ArrayRef getImports() const { return *Imports; } + ArrayRef> getImports() const { + return *Imports; + } /// Set the imports for this source file. This gets called by import /// resolution. - void setImports(ArrayRef imports); + void setImports(ArrayRef> imports); enum ImportQueryKind { /// Return the results for testable or private imports. diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index ac0c4bcf9d868..66fcd8bc4d466 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -1882,14 +1882,16 @@ void SourceFile::print(ASTPrinter &Printer, const PrintOptions &PO) { } } -void SourceFile::setImports(ArrayRef imports) { +void +SourceFile::setImports(ArrayRef> imports) { assert(!Imports && "Already computed imports"); Imports = getASTContext().AllocateCopy(imports); } bool HasImplementationOnlyImportsRequest::evaluate(Evaluator &evaluator, SourceFile *SF) const { - return llvm::any_of(SF->getImports(), [](ImportedModuleDesc desc) { + return llvm::any_of(SF->getImports(), + [](AttributedImport desc) { return desc.importOptions.contains(ImportFlags::ImplementationOnly); }); } @@ -1911,9 +1913,8 @@ bool SourceFile::hasTestableOrPrivateImport( // internal/public access only needs an import marked as @_private. The // filename does not need to match (and we don't serialize it for such // decls). - return std::any_of( - Imports->begin(), Imports->end(), - [module, queryKind](ImportedModuleDesc desc) -> bool { + return llvm::any_of(*Imports, + [module, queryKind](AttributedImport desc) -> bool { if (queryKind == ImportQueryKind::TestableAndPrivate) return desc.module.importedModule == module && (desc.importOptions.contains(ImportFlags::PrivateImport) || @@ -1954,13 +1955,12 @@ bool SourceFile::hasTestableOrPrivateImport( if (filename.empty()) return false; - return std::any_of(Imports->begin(), Imports->end(), - [module, filename](ImportedModuleDesc desc) -> bool { - return desc.module.importedModule == module && - desc.importOptions.contains( - ImportFlags::PrivateImport) && - desc.filename == filename; - }); + return llvm::any_of(*Imports, + [module, filename](AttributedImport desc) { + return desc.module.importedModule == module && + desc.importOptions.contains(ImportFlags::PrivateImport) && + desc.filename == filename; + }); } bool SourceFile::isImportedImplementationOnly(const ModuleDecl *module) const { diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index 8583c03d3ea25..c03b927af3953 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -102,8 +102,8 @@ struct UnboundImport { /// Create an UnboundImport for a cross-import overlay. explicit UnboundImport(ASTContext &ctx, const UnboundImport &base, Identifier overlayName, - const ImportedModuleDesc &declaringImport, - const ImportedModuleDesc &bystandingImport); + const AttributedImport &declaringImport, + const AttributedImport &bystandingImport); /// Diagnoses if the import would simply load the module \p SF already /// belongs to, with no actual effect. @@ -133,11 +133,12 @@ struct UnboundImport { /// non-implementation-only import of a fragile library from a resilient one. void validateOptions(NullablePtr topLevelModule, SourceFile &SF); - /// Create an \c ImportedModuleDesc from the information in this + /// Create an \c AttributedImport from the information in this /// UnboundImport. - ImportedModuleDesc makeDesc(ModuleDecl *module) const { - return ImportedModuleDesc({ accessPath, module }, options, - privateImportFileName, spiGroups); + AttributedImport + makeAttributedImport(ModuleDecl *module) const { + return AttributedImport({ accessPath, module }, options, + privateImportFileName, spiGroups); } private: @@ -165,7 +166,7 @@ class ImportResolver final : public DeclVisitor { SmallVector unboundImports; /// The list of fully bound imports. - SmallVector boundImports; + SmallVector, 16> boundImports; /// All imported modules which should be considered when cross-importing. /// This is basically the transitive import graph, but with only top-level @@ -174,14 +175,14 @@ class ImportResolver final : public DeclVisitor { /// We use a \c SmallSetVector here because this doubles as the worklist for /// cross-importing, so we want to keep it in order; this is feasible /// because this set is usually fairly small. - SmallSetVector crossImportableModules; + SmallSetVector, 64> crossImportableModules; /// The subset of \c crossImportableModules which may declare cross-imports. /// /// This is a performance optimization. Since most modules do not register /// any cross-imports, we can usually compare against this list, which is /// much, much smaller than \c crossImportableModules. - SmallVector crossImportDeclaringModules; + SmallVector, 16> crossImportDeclaringModules; /// The index of the next module in \c visibleModules that should be /// cross-imported. @@ -203,7 +204,7 @@ class ImportResolver final : public DeclVisitor { } /// Retrieve the finalized imports. - ArrayRef getFinishedImports() const { + ArrayRef> getFinishedImports() const { return boundImports; } @@ -228,7 +229,7 @@ class ImportResolver final : public DeclVisitor { /// Adds \p desc and everything it re-exports to \c visibleModules using /// the settings from \c desc. - void addCrossImportableModules(ImportedModuleDesc desc); + void addCrossImportableModules(AttributedImport desc); /// * If \p I is a cross-import overlay, registers \p M as overlaying /// \p I.underlyingModule in \c SF. @@ -239,18 +240,19 @@ class ImportResolver final : public DeclVisitor { /// Discovers any cross-imports between \p newImport and /// \p oldImports and adds them to \c unboundImports, using source /// locations from \p I. - void findCrossImportsInLists(UnboundImport &I, - ArrayRef declaring, - ArrayRef bystanding, - bool shouldDiagnoseRedundantCrossImports); + void findCrossImportsInLists( + UnboundImport &I, + ArrayRef> declaring, + ArrayRef> bystanding, + bool shouldDiagnoseRedundantCrossImports); /// Discovers any cross-imports between \p declaringImport and /// \p bystandingImport and adds them to \c unboundImports, using source /// locations from \p I. void findCrossImports(UnboundImport &I, - const ImportedModuleDesc &declaringImport, - const ImportedModuleDesc &bystandingImport, - bool shouldDiagnoseRedundantCrossImports); + const AttributedImport &declaringImport, + const AttributedImport &bystandingImport, + bool shouldDiagnoseRedundantCrossImports); /// Load a module referenced by an import statement. /// @@ -353,7 +355,7 @@ void ImportResolver::bindImport(UnboundImport &&I) { } void ImportResolver::addImport(const UnboundImport &I, ModuleDecl *M) { - auto importDesc = I.makeDesc(M); + auto importDesc = I.makeAttributedImport(M); addCrossImportableModules(importDesc); boundImports.push_back(importDesc); } @@ -912,7 +914,7 @@ ScopedImportLookupRequest::evaluate(Evaluator &evaluator, // MARK: Cross-import overlays //===----------------------------------------------------------------------===// -static bool canCrossImport(const ImportedModuleDesc &import) { +static bool canCrossImport(const AttributedImport &import) { if (import.importOptions.contains(ImportFlags::Testable)) return false; if (import.importOptions.contains(ImportFlags::PrivateImport)) @@ -922,10 +924,10 @@ static bool canCrossImport(const ImportedModuleDesc &import) { } /// Create an UnboundImport for a cross-import overlay. -UnboundImport::UnboundImport(ASTContext &ctx, const UnboundImport &base, - Identifier overlayName, - const ImportedModuleDesc &declaringImport, - const ImportedModuleDesc &bystandingImport) +UnboundImport::UnboundImport( + ASTContext &ctx, const UnboundImport &base, Identifier overlayName, + const AttributedImport &declaringImport, + const AttributedImport &bystandingImport) : importLoc(base.importLoc), options(), privateImportFileName(), // Cross-imports are not backed by an ImportDecl, so we need to provide // our own storage for their module paths. @@ -1032,8 +1034,8 @@ void ImportResolver::crossImport(ModuleDecl *M, UnboundImport &I) { } void ImportResolver::findCrossImportsInLists( - UnboundImport &I, ArrayRef declaring, - ArrayRef bystanding, + UnboundImport &I, ArrayRef> declaring, + ArrayRef> bystanding, bool shouldDiagnoseRedundantCrossImports) { for (auto &declaringImport : declaring) { if (!canCrossImport(declaringImport)) @@ -1050,8 +1052,9 @@ void ImportResolver::findCrossImportsInLists( } void ImportResolver::findCrossImports( - UnboundImport &I, const ImportedModuleDesc &declaringImport, - const ImportedModuleDesc &bystandingImport, + UnboundImport &I, + const AttributedImport &declaringImport, + const AttributedImport &bystandingImport, bool shouldDiagnoseRedundantCrossImports) { assert(&declaringImport != &bystandingImport); @@ -1120,7 +1123,8 @@ static bool isSubmodule(ModuleDecl* M) { return clangMod && clangMod->Parent; } -void ImportResolver::addCrossImportableModules(ImportedModuleDesc importDesc) { +void ImportResolver::addCrossImportableModules( + AttributedImport importDesc) { // FIXME: namelookup::getAllImports() doesn't quite do what we need (mainly // w.r.t. scoped imports), but it seems like we could extend it to do so, and // then eliminate most of this. From c489fffb796432c4c463b4cb11a2d88f23b8b508 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Fri, 25 Sep 2020 15:04:53 -0700 Subject: [PATCH 377/745] [NFC] Rename and document fields of AttributedImport Also renames a member in ImportResolution.cpp to align with this naming. --- include/swift/AST/Import.h | 30 ++++++++++++++++++------------ lib/AST/Module.cpp | 24 ++++++++++++------------ lib/AST/NameLookup.cpp | 2 +- lib/AST/OperatorNameLookup.cpp | 2 +- lib/Sema/ImportResolution.cpp | 22 +++++++++++----------- 5 files changed, 43 insertions(+), 37 deletions(-) diff --git a/include/swift/AST/Import.h b/include/swift/AST/Import.h index 5482e714b10af..5ff068feb5bc5 100644 --- a/include/swift/AST/Import.h +++ b/include/swift/AST/Import.h @@ -444,25 +444,31 @@ struct alignas(uint64_t) ImportedModule { }; }; +/// Augments a type representing an import to also include information about the +/// import's attributes. This is usually used with either \c ImportedModule or +/// \c UnloadedImportedModule. template struct AttributedImport { + /// Information about the module and access path being imported. ModuleInfo module; - ImportOptions importOptions; + /// Flags indicating which attributes of this import are present. + ImportOptions options; - // Filename for a @_private import. - StringRef filename; + /// If this is a @_private import, the value of its 'sourceFile:' argument; + /// otherwise, empty string. + StringRef sourceFileArg; - // Names of explicitly imported SPIs. + /// Names of explicitly imported SPI groups. ArrayRef spiGroups; AttributedImport(ModuleInfo module, ImportOptions options, StringRef filename = {}, ArrayRef spiGroups = {}) - : module(module), importOptions(options), filename(filename), + : module(module), options(options), sourceFileArg(filename), spiGroups(spiGroups) { - assert(!(importOptions.contains(ImportFlags::Exported) && - importOptions.contains(ImportFlags::ImplementationOnly)) || - importOptions.contains(ImportFlags::Reserved)); + assert(!(options.contains(ImportFlags::Exported) && + options.contains(ImportFlags::ImplementationOnly)) || + options.contains(ImportFlags::Reserved)); } }; @@ -596,14 +602,14 @@ struct DenseMapInfo> { return detail::combineHashValue( ModuleInfoDMI::getHashValue(import.module), detail::combineHashValue( - ImportOptionsDMI::getHashValue(import.importOptions), - StringRefDMI::getHashValue(import.filename))); + ImportOptionsDMI::getHashValue(import.options), + StringRefDMI::getHashValue(import.sourceFileArg))); } static bool isEqual(const AttributedImport &a, const AttributedImport &b) { return ModuleInfoDMI::isEqual(a.module, b.module) && - ImportOptionsDMI::isEqual(a.importOptions, b.importOptions) && - StringRefDMI::isEqual(a.filename, b.filename); + ImportOptionsDMI::isEqual(a.options, b.options) && + StringRefDMI::isEqual(a.sourceFileArg, b.sourceFileArg); } }; } diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 66fcd8bc4d466..e553925cf85c7 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -1191,14 +1191,14 @@ SourceFile::getImportedModules(SmallVectorImpl &modules, for (auto desc : *Imports) { ModuleDecl::ImportFilter requiredFilter; - if (desc.importOptions.contains(ImportFlags::Exported)) + if (desc.options.contains(ImportFlags::Exported)) requiredFilter |= ModuleDecl::ImportFilterKind::Exported; - else if (desc.importOptions.contains(ImportFlags::ImplementationOnly)) + else if (desc.options.contains(ImportFlags::ImplementationOnly)) requiredFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly; else requiredFilter |= ModuleDecl::ImportFilterKind::Default; - if (desc.importOptions.contains(ImportFlags::SPIAccessControl)) + if (desc.options.contains(ImportFlags::SPIAccessControl)) requiredFilter |= ModuleDecl::ImportFilterKind::SPIAccessControl; if (!separatelyImportedOverlays.lookup(desc.module.importedModule).empty()) @@ -1892,7 +1892,7 @@ bool HasImplementationOnlyImportsRequest::evaluate(Evaluator &evaluator, SourceFile *SF) const { return llvm::any_of(SF->getImports(), [](AttributedImport desc) { - return desc.importOptions.contains(ImportFlags::ImplementationOnly); + return desc.options.contains(ImportFlags::ImplementationOnly); }); } @@ -1917,15 +1917,15 @@ bool SourceFile::hasTestableOrPrivateImport( [module, queryKind](AttributedImport desc) -> bool { if (queryKind == ImportQueryKind::TestableAndPrivate) return desc.module.importedModule == module && - (desc.importOptions.contains(ImportFlags::PrivateImport) || - desc.importOptions.contains(ImportFlags::Testable)); + (desc.options.contains(ImportFlags::PrivateImport) || + desc.options.contains(ImportFlags::Testable)); else if (queryKind == ImportQueryKind::TestableOnly) return desc.module.importedModule == module && - desc.importOptions.contains(ImportFlags::Testable); + desc.options.contains(ImportFlags::Testable); else { assert(queryKind == ImportQueryKind::PrivateOnly); return desc.module.importedModule == module && - desc.importOptions.contains(ImportFlags::PrivateImport); + desc.options.contains(ImportFlags::PrivateImport); } }); case AccessLevel::Open: @@ -1958,8 +1958,8 @@ bool SourceFile::hasTestableOrPrivateImport( return llvm::any_of(*Imports, [module, filename](AttributedImport desc) { return desc.module.importedModule == module && - desc.importOptions.contains(ImportFlags::PrivateImport) && - desc.filename == filename; + desc.options.contains(ImportFlags::PrivateImport) && + desc.sourceFileArg == filename; }); } @@ -1974,7 +1974,7 @@ bool SourceFile::isImportedImplementationOnly(const ModuleDecl *module) const { // Look at the imports of this source file. for (auto &desc : *Imports) { // Ignore implementation-only imports. - if (desc.importOptions.contains(ImportFlags::ImplementationOnly)) + if (desc.options.contains(ImportFlags::ImplementationOnly)) continue; // If the module is imported this way, it's not imported @@ -2012,7 +2012,7 @@ void SourceFile::lookupImportedSPIGroups( const ModuleDecl *importedModule, llvm::SmallSetVector &spiGroups) const { for (auto &import : *Imports) { - if (import.importOptions.contains(ImportFlags::SPIAccessControl) && + if (import.options.contains(ImportFlags::SPIAccessControl) && importedModule == import.module.importedModule) { auto importedSpis = import.spiGroups; spiGroups.insert(importedSpis.begin(), importedSpis.end()); diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 17cf849302574..b8ee1ff3f1c8c 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -290,7 +290,7 @@ static void recordShadowedDeclsAfterTypeMatch( auto file = dc->getParentSourceFile(); if (!file) return false; for (const auto &import : file->getImports()) { - if (import.importOptions.contains(ImportFlags::PrivateImport) + if (import.options.contains(ImportFlags::PrivateImport) && import.module.importedModule == module && import.module.accessPath.matches(name)) return true; diff --git a/lib/AST/OperatorNameLookup.cpp b/lib/AST/OperatorNameLookup.cpp index e05e3347de08b..63e64547b4ef9 100644 --- a/lib/AST/OperatorNameLookup.cpp +++ b/lib/AST/OperatorNameLookup.cpp @@ -83,7 +83,7 @@ static TinyPtrVector lookupOperatorImpl( if (!visitedModules.insert(mod).second) continue; - bool isExported = import.importOptions.contains(ImportFlags::Exported); + bool isExported = import.options.contains(ImportFlags::Exported); if (!includePrivate && !isExported) continue; diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index c03b927af3953..ec0b2e76a1408 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -61,7 +61,7 @@ struct UnboundImport { /// If \c options includes \c PrivateImport, the filename we should import /// private declarations from. - StringRef privateImportFileName; + StringRef sourceFileArg; /// The module names being imported. There will usually be just one for the /// top-level module, but a submodule import will have more. @@ -137,8 +137,8 @@ struct UnboundImport { /// UnboundImport. AttributedImport makeAttributedImport(ModuleDecl *module) const { - return AttributedImport({ accessPath, module }, options, - privateImportFileName, spiGroups); + return { ImportedModule{ accessPath, module }, + options, sourceFileArg, spiGroups }; } private: @@ -496,7 +496,7 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, /// Create an UnboundImport for a user-written import declaration. UnboundImport::UnboundImport(ImportDecl *ID) - : importLoc(ID->getLoc()), options(), privateImportFileName(), + : importLoc(ID->getLoc()), options(), sourceFileArg(), modulePath(ID->getModulePath()), accessPath(ID->getAccessPath()), importOrUnderlyingModuleDecl(ID) { @@ -512,7 +512,7 @@ UnboundImport::UnboundImport(ImportDecl *ID) if (auto *privateImportAttr = ID->getAttrs().getAttribute()) { options |= ImportFlags::PrivateImport; - privateImportFileName = privateImportAttr->getSourceFile(); + sourceFileArg = privateImportAttr->getSourceFile(); } SmallVector spiGroups; @@ -607,7 +607,7 @@ void UnboundImport::validatePrivate(ModuleDecl *topLevelModule) { diagnoseInvalidAttr(DAK_PrivateImport, ctx.Diags, diag::module_not_compiled_for_private_import); - privateImportFileName = StringRef(); + sourceFileArg = StringRef(); } void UnboundImport::validateImplementationOnly(ASTContext &ctx) { @@ -915,9 +915,9 @@ ScopedImportLookupRequest::evaluate(Evaluator &evaluator, //===----------------------------------------------------------------------===// static bool canCrossImport(const AttributedImport &import) { - if (import.importOptions.contains(ImportFlags::Testable)) + if (import.options.contains(ImportFlags::Testable)) return false; - if (import.importOptions.contains(ImportFlags::PrivateImport)) + if (import.options.contains(ImportFlags::PrivateImport)) return false; return true; @@ -928,7 +928,7 @@ UnboundImport::UnboundImport( ASTContext &ctx, const UnboundImport &base, Identifier overlayName, const AttributedImport &declaringImport, const AttributedImport &bystandingImport) - : importLoc(base.importLoc), options(), privateImportFileName(), + : importLoc(base.importLoc), options(), sourceFileArg(), // Cross-imports are not backed by an ImportDecl, so we need to provide // our own storage for their module paths. modulePath( @@ -944,8 +944,8 @@ UnboundImport::UnboundImport( assert(canCrossImport(declaringImport)); assert(canCrossImport(bystandingImport)); - auto &declaringOptions = declaringImport.importOptions; - auto &bystandingOptions = bystandingImport.importOptions; + auto &declaringOptions = declaringImport.options; + auto &bystandingOptions = bystandingImport.options; // If both are exported, the cross-import is exported. if (declaringOptions.contains(ImportFlags::Exported) && From 8070e8aaedf662bceea1483840cf0f61d5a8b8a4 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Fri, 25 Sep 2020 15:19:05 -0700 Subject: [PATCH 378/745] [NFC] Use AttributedImport in import resolution Removes what amount to redundant definitions from UnboundImport. --- include/swift/AST/Import.h | 70 ++++++++++++++++++-- lib/Sema/ImportResolution.cpp | 121 ++++++++++++++++------------------ 2 files changed, 123 insertions(+), 68 deletions(-) diff --git a/include/swift/AST/Import.h b/include/swift/AST/Import.h index 5ff068feb5bc5..1988460bc24ba 100644 --- a/include/swift/AST/Import.h +++ b/include/swift/AST/Import.h @@ -24,6 +24,7 @@ #include "swift/Basic/OptionSet.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" @@ -50,6 +51,10 @@ enum class ImportKind : uint8_t { Func }; +inline bool isScopedImportKind(ImportKind importKind) { + return importKind != ImportKind::Module; +} + /// Possible attributes for imports in source files. enum class ImportFlags { /// The imported module is exposed to anyone who imports the parent module. @@ -156,9 +161,17 @@ namespace detail { template class ImportPathBuilder { - llvm::SmallVector scratch; + using Scratch = llvm::SmallVector; + Scratch scratch; public: + using value_type = Scratch::value_type; + using reference = Scratch::reference; + using iterator = Scratch::iterator; + using const_iterator = Scratch::const_iterator; + using difference_type = Scratch::difference_type; + using size_type = Scratch::size_type; + Subclass get() const { return Subclass(scratch); } @@ -390,18 +403,67 @@ class ImportPath : public detail::ImportPathBase { /// including submodules, assuming the \c ImportDecl has the indicated /// \c importKind. Module getModulePath(ImportKind importKind) const { - return getModulePath(importKind != ImportKind::Module); + return getModulePath(isScopedImportKind(importKind)); } /// Extracts the portion of the \c ImportPath which represents a scope for the /// import, assuming the \c ImportDecl has the indicated \c importKind. Access getAccessPath(ImportKind importKind) const { - return getAccessPath(importKind != ImportKind::Module); + return getAccessPath(isScopedImportKind(importKind)); } }; // MARK: - Abstractions of imports +/// Convenience struct to keep track of an import path and whether or not it +/// is scoped. +class UnloadedImportedModule { + // This is basically an ArrayRef with a bit stolen from the pointer. + // FIXME: Extract an ArrayRefIntPair type from this. + llvm::PointerIntPair dataAndIsScoped; + ImportPath::Raw::size_type length; + + ImportPath::Raw::iterator data() const { + return dataAndIsScoped.getPointer(); + } + + bool isScoped() const { + return dataAndIsScoped.getInt(); + } + + ImportPath::Raw getRaw() const { + return ImportPath::Raw(data(), length); + } + + UnloadedImportedModule(ImportPath::Raw raw, bool isScoped) + : dataAndIsScoped(raw.data(), isScoped), length(raw.size()) { } + +public: + UnloadedImportedModule(ImportPath importPath, bool isScoped) + : UnloadedImportedModule(importPath.getRaw(), isScoped) { } + + UnloadedImportedModule(ImportPath importPath, ImportKind importKind) + : UnloadedImportedModule(importPath, isScopedImportKind(importKind)) { } + + ImportPath getImportPath() const { + return ImportPath(getRaw()); + } + + ImportPath::Module getModulePath() const { + return getImportPath().getModulePath(isScoped()); + } + + ImportPath::Access getAccessPath() const { + return getImportPath().getAccessPath(isScoped()); + } + + friend bool operator==(const UnloadedImportedModule &lhs, + const UnloadedImportedModule &rhs) { + return (lhs.getRaw() == rhs.getRaw()) && + (lhs.isScoped() == rhs.isScoped()); + } +}; + /// Convenience struct to keep track of a module along with its access path. struct alignas(uint64_t) ImportedModule { /// The access path from an import: `import Foo.Bar` -> `Foo.Bar`. @@ -472,8 +534,6 @@ struct AttributedImport { } }; -using ImportedModuleDesc = AttributedImport; - // MARK: - Implicit imports /// A module which has been implicitly imported. diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index ec0b2e76a1408..b6825c8ef9e2f 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -51,36 +51,17 @@ namespace { /// source, or it may represent a cross-import overlay that has been found and /// needs to be loaded. struct UnboundImport { + /// Information about the import. Use this field, not \c getImportDecl(), to + /// determine the behavior expected for this import. + AttributedImport import; + /// The source location to use when diagnosing errors for this import. SourceLoc importLoc; - /// The options for this import, such as "exported" or - /// "implementation-only". Use this field, not \c attrs, to determine the - /// behavior expected for this import. - ImportOptions options; - - /// If \c options includes \c PrivateImport, the filename we should import - /// private declarations from. - StringRef sourceFileArg; - - /// The module names being imported. There will usually be just one for the - /// top-level module, but a submodule import will have more. - ImportPath::Module modulePath; - - /// If this is a scoped import, the names of the declaration being imported; - /// otherwise empty. (Currently the compiler doesn't support nested scoped - /// imports, so there should always be zero or one elements, but - /// \c ImportPath::Access is the common currency type for this.) - ImportPath::Access accessPath; - - // Names of explicitly imported SPI groups via @_spi. - ArrayRef spiGroups; - /// If this UnboundImport directly represents an ImportDecl, contains the /// ImportDecl it represents. This should only be used for diagnostics and /// for updating the AST; if you want to read information about the import, - /// get it from the other fields in \c UnboundImport rather than from the - /// \c ImportDecl. + /// get it from the \c import field rather than from the \c ImportDecl. /// /// If this UnboundImport represents a cross-import, contains the declaring /// module's \c ModuleDecl. @@ -137,8 +118,8 @@ struct UnboundImport { /// UnboundImport. AttributedImport makeAttributedImport(ModuleDecl *module) const { - return { ImportedModule{ accessPath, module }, - options, sourceFileArg, spiGroups }; + return { ImportedModule{ import.module.getAccessPath(), module }, + import.options, import.sourceFileArg, import.spiGroups }; } private: @@ -326,7 +307,7 @@ void ImportResolver::bindImport(UnboundImport &&I) { return; } - ModuleDecl *M = getModule(I.modulePath); + ModuleDecl *M = getModule(I.import.module.getModulePath()); if (!I.checkModuleLoaded(M, SF)) { // Can't process further. checkModuleLoaded() will have diagnosed this. if (ID) @@ -392,11 +373,11 @@ ImportResolver::getModule(ImportPath::Module modulePath) { NullablePtr UnboundImport::getTopLevelModule(ModuleDecl *M, SourceFile &SF) { - if (modulePath.size() == 1) + if (import.module.getModulePath().size() == 1) return M; // If we imported a submodule, import the top-level module as well. - Identifier topLevelName = modulePath.front().Item; + Identifier topLevelName = import.module.getModulePath().front().Item; ModuleDecl *topLevelModule = SF.getASTContext().getLoadedModule(topLevelName); if (!topLevelModule) { @@ -496,39 +477,40 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, /// Create an UnboundImport for a user-written import declaration. UnboundImport::UnboundImport(ImportDecl *ID) - : importLoc(ID->getLoc()), options(), sourceFileArg(), - modulePath(ID->getModulePath()), accessPath(ID->getAccessPath()), - importOrUnderlyingModuleDecl(ID) + : import(UnloadedImportedModule(ID->getImportPath(), ID->getImportKind()), + {}), + importLoc(ID->getLoc()), importOrUnderlyingModuleDecl(ID) { if (ID->isExported()) - options |= ImportFlags::Exported; + import.options |= ImportFlags::Exported; if (ID->getAttrs().hasAttribute()) - options |= ImportFlags::Testable; + import.options |= ImportFlags::Testable; if (ID->getAttrs().hasAttribute()) - options |= ImportFlags::ImplementationOnly; + import.options |= ImportFlags::ImplementationOnly; if (auto *privateImportAttr = ID->getAttrs().getAttribute()) { - options |= ImportFlags::PrivateImport; - sourceFileArg = privateImportAttr->getSourceFile(); + import.options |= ImportFlags::PrivateImport; + import.sourceFileArg = privateImportAttr->getSourceFile(); } SmallVector spiGroups; for (auto attr : ID->getAttrs().getAttributes()) { - options |= ImportFlags::SPIAccessControl; + import.options |= ImportFlags::SPIAccessControl; auto attrSPIs = attr->getSPIGroups(); spiGroups.append(attrSPIs.begin(), attrSPIs.end()); } - this->spiGroups = ID->getASTContext().AllocateCopy(spiGroups); + import.spiGroups = ID->getASTContext().AllocateCopy(spiGroups); } bool UnboundImport::checkNotTautological(const SourceFile &SF) { // Exit early if this is not a self-import. + auto modulePath = import.module.getModulePath(); if (modulePath.front().Item != SF.getParentModule()->getName() || // Overlays use an @_exported self-import to load their clang module. - options.contains(ImportFlags::Exported) || + import.options.contains(ImportFlags::Exported) || // Imports of your own submodules are allowed in cross-language libraries. modulePath.size() != 1 || // SIL files self-import to get decls from the rest of the module. @@ -555,7 +537,8 @@ bool UnboundImport::checkModuleLoaded(ModuleDecl *M, SourceFile &SF) { ASTContext &ctx = SF.getASTContext(); SmallString<64> modulePathStr; - llvm::interleave(modulePath, [&](ImportPath::Element elem) { + llvm::interleave(import.module.getModulePath(), + [&](ImportPath::Element elem) { modulePathStr += elem.Item.str(); }, [&] { modulePathStr += "."; }); @@ -599,7 +582,7 @@ void UnboundImport::validatePrivate(ModuleDecl *topLevelModule) { assert(topLevelModule); ASTContext &ctx = topLevelModule->getASTContext(); - if (!options.contains(ImportFlags::PrivateImport)) + if (!import.options.contains(ImportFlags::PrivateImport)) return; if (topLevelModule->arePrivateImportsEnabled()) @@ -607,16 +590,16 @@ void UnboundImport::validatePrivate(ModuleDecl *topLevelModule) { diagnoseInvalidAttr(DAK_PrivateImport, ctx.Diags, diag::module_not_compiled_for_private_import); - sourceFileArg = StringRef(); + import.sourceFileArg = StringRef(); } void UnboundImport::validateImplementationOnly(ASTContext &ctx) { - if (!options.contains(ImportFlags::ImplementationOnly) || - !options.contains(ImportFlags::Exported)) + if (!import.options.contains(ImportFlags::ImplementationOnly) || + !import.options.contains(ImportFlags::Exported)) return; // Remove one flag to maintain the invariant. - options -= ImportFlags::ImplementationOnly; + import.options -= ImportFlags::ImplementationOnly; diagnoseInvalidAttr(DAK_ImplementationOnly, ctx.Diags, diag::import_implementation_cannot_be_exported); @@ -626,7 +609,7 @@ void UnboundImport::validateTestable(ModuleDecl *topLevelModule) { assert(topLevelModule); ASTContext &ctx = topLevelModule->getASTContext(); - if (!options.contains(ImportFlags::Testable) || + if (!import.options.contains(ImportFlags::Testable) || topLevelModule->isTestingEnabled() || topLevelModule->isNonSwiftModule() || !ctx.LangOpts.EnableTestableAttrRequiresTestableModule) @@ -637,7 +620,7 @@ void UnboundImport::validateTestable(ModuleDecl *topLevelModule) { void UnboundImport::validateResilience(NullablePtr topLevelModule, SourceFile &SF) { - if (options.contains(ImportFlags::ImplementationOnly)) + if (import.options.contains(ImportFlags::ImplementationOnly)) return; // Per getTopLevelModule(), we'll only get nullptr here for non-Swift modules, @@ -650,7 +633,7 @@ void UnboundImport::validateResilience(NullablePtr topLevelModule, return; ASTContext &ctx = SF.getASTContext(); - ctx.Diags.diagnose(modulePath.front().Loc, + ctx.Diags.diagnose(import.module.getModulePath().front().Loc, diag::module_not_compiled_with_library_evolution, topLevelModule.get()->getName(), SF.getParentModule()->getName()); @@ -661,8 +644,8 @@ void UnboundImport::validateResilience(NullablePtr topLevelModule, void UnboundImport::diagnoseInvalidAttr(DeclAttrKind attrKind, DiagnosticEngine &diags, Diag diagID) { - auto diag = diags.diagnose(modulePath.front().Loc, diagID, - modulePath.front().Item); + auto diag = diags.diagnose(import.module.getModulePath().front().Loc, diagID, + import.module.getModulePath().front().Item); auto *ID = getImportDecl().getPtrOrNull(); if (!ID) return; @@ -923,20 +906,32 @@ static bool canCrossImport(const AttributedImport &import) { return true; } +static UnloadedImportedModule makeUnimportedCrossImportOverlay( + ASTContext &ctx, + Identifier overlayName, + const UnboundImport &base, + const AttributedImport &declaringImport) { + ImportPath::Builder + builder(overlayName, base.import.module.getModulePath()[0].Loc); + + // If the declaring import was scoped, inherit that scope in the overlay's + // import. + llvm::copy(declaringImport.module.accessPath, std::back_inserter(builder)); + + // Cross-imports are not backed by an ImportDecl, so we need to provide + // our own storage for their module paths. + return UnloadedImportedModule(builder.copyTo(ctx), + /*isScoped=*/!declaringImport.module.accessPath.empty()); +} + /// Create an UnboundImport for a cross-import overlay. UnboundImport::UnboundImport( ASTContext &ctx, const UnboundImport &base, Identifier overlayName, const AttributedImport &declaringImport, const AttributedImport &bystandingImport) - : importLoc(base.importLoc), options(), sourceFileArg(), - // Cross-imports are not backed by an ImportDecl, so we need to provide - // our own storage for their module paths. - modulePath( - ImportPath::Module::Builder(overlayName, base.modulePath[0].Loc) - .copyTo(ctx)), - // If the declaring import was scoped, inherit that scope in the - // overlay's import. - accessPath(declaringImport.module.accessPath), + : import(makeUnimportedCrossImportOverlay(ctx, overlayName, base, + declaringImport), {}), + importLoc(base.importLoc), importOrUnderlyingModuleDecl(declaringImport.module.importedModule) { // A cross-import is never private or testable, and never comes from a private @@ -950,13 +945,13 @@ UnboundImport::UnboundImport( // If both are exported, the cross-import is exported. if (declaringOptions.contains(ImportFlags::Exported) && bystandingOptions.contains(ImportFlags::Exported)) - options |= ImportFlags::Exported; + import.options |= ImportFlags::Exported; // If either are implementation-only, the cross-import is // implementation-only. if (declaringOptions.contains(ImportFlags::ImplementationOnly) || bystandingOptions.contains(ImportFlags::ImplementationOnly)) - options |= ImportFlags::ImplementationOnly; + import.options |= ImportFlags::ImplementationOnly; } void ImportResolver::crossImport(ModuleDecl *M, UnboundImport &I) { @@ -1107,7 +1102,7 @@ void ImportResolver::findCrossImports( name); LLVM_DEBUG({ - auto &crossImportOptions = unboundImports.back().options; + auto &crossImportOptions = unboundImports.back().import.options; llvm::dbgs() << " "; if (crossImportOptions.contains(ImportFlags::Exported)) llvm::dbgs() << "@_exported "; From d55c5bd549fa17769c23be9708c33c8de9ed0ef1 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Fri, 25 Sep 2020 16:26:39 -0700 Subject: [PATCH 379/745] [NFC] Add type for ModuleDecl::getImplicitImports() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This doesn’t really change the design yet. --- include/swift/AST/ASTTypeIDZone.def | 2 +- include/swift/AST/ASTTypeIDs.h | 2 +- include/swift/AST/Import.h | 15 ++++++++++++ include/swift/AST/Module.h | 3 +-- include/swift/AST/TypeCheckRequests.h | 5 ++-- include/swift/AST/TypeCheckerTypeIDZone.def | 2 +- lib/AST/Module.cpp | 3 +-- lib/AST/TypeCheckRequests.cpp | 8 +++++++ lib/Sema/ImportResolution.cpp | 26 ++++++++++++--------- 9 files changed, 45 insertions(+), 21 deletions(-) diff --git a/include/swift/AST/ASTTypeIDZone.def b/include/swift/AST/ASTTypeIDZone.def index 6bbe1a459ceb5..d4ebede4903ec 100644 --- a/include/swift/AST/ASTTypeIDZone.def +++ b/include/swift/AST/ASTTypeIDZone.def @@ -22,7 +22,7 @@ SWIFT_TYPEID(BodyInitKindAndExpr) SWIFT_TYPEID(CtorInitializerKind) SWIFT_TYPEID(FunctionBuilderBodyPreCheck) SWIFT_TYPEID(GenericSignature) -SWIFT_TYPEID(ImplicitImport) +SWIFT_TYPEID(ImplicitImportList) SWIFT_TYPEID(ImplicitMemberAction) SWIFT_TYPEID(ParamSpecifier) SWIFT_TYPEID(PropertyWrapperBackingPropertyInfo) diff --git a/include/swift/AST/ASTTypeIDs.h b/include/swift/AST/ASTTypeIDs.h index 8bbb7366f965d..94323e9353913 100644 --- a/include/swift/AST/ASTTypeIDs.h +++ b/include/swift/AST/ASTTypeIDs.h @@ -42,7 +42,7 @@ class GenericTypeParamType; class InfixOperatorDecl; class IterableDeclContext; class ModuleDecl; -struct ImplicitImport; +struct ImplicitImportList; class NamedPattern; class NominalTypeDecl; class OperatorDecl; diff --git a/include/swift/AST/Import.h b/include/swift/AST/Import.h index 1988460bc24ba..e6b28b438c22f 100644 --- a/include/swift/AST/Import.h +++ b/include/swift/AST/Import.h @@ -565,6 +565,7 @@ enum class ImplicitStdlibKind { Stdlib }; +/// Represents unprocessed options for implicit imports. struct ImplicitImportInfo { /// The implicit stdlib to import. ImplicitStdlibKind StdlibKind; @@ -589,6 +590,20 @@ struct ImplicitImportInfo { ShouldImportUnderlyingModule(false) {} }; +/// Contains names of and pointers to modules that must be implicitly imported. +struct ImplicitImportList { + ArrayRef imports; + + friend bool operator==(const ImplicitImportList &lhs, + const ImplicitImportList &rhs) { + return lhs.imports == rhs.imports; + } +}; + +/// A list of modules to implicitly import. +void simple_display(llvm::raw_ostream &out, + const ImplicitImportList &importList); + } // MARK: - DenseMapInfo diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index 3c3dcb0485995..d9c6dc675b40d 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -56,7 +56,6 @@ namespace swift { class FuncDecl; class InfixOperatorDecl; class LinkLibrary; - struct ImplicitImport; class ModuleLoader; class NominalTypeDecl; class EnumElementDecl; @@ -278,7 +277,7 @@ class ModuleDecl : public DeclContext, public TypeDecl { /// Retrieve a list of modules that each file of this module implicitly /// imports. - ArrayRef getImplicitImports() const; + ImplicitImportList getImplicitImports() const; ArrayRef getFiles() { assert(!Files.empty() || failedToLoad()); diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 4d2b1a8acc57d..4926c75477fac 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -2498,7 +2498,7 @@ class SimpleDidSetRequest /// of a given module. class ModuleImplicitImportsRequest : public SimpleRequest(ModuleDecl *), + ImplicitImportList(ModuleDecl *), RequestFlags::Cached> { public: using SimpleRequest::SimpleRequest; @@ -2506,8 +2506,7 @@ class ModuleImplicitImportsRequest private: friend SimpleRequest; - ArrayRef - evaluate(Evaluator &evaluator, ModuleDecl *module) const; + ImplicitImportList evaluate(Evaluator &evaluator, ModuleDecl *module) const; public: // Cached. diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 7b3f269555979..446b8c1e1a39f 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -148,7 +148,7 @@ SWIFT_REQUEST(TypeChecker, ValidatePrecedenceGroupRequest, SWIFT_REQUEST(TypeChecker, MangleLocalTypeDeclRequest, std::string(const TypeDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ModuleImplicitImportsRequest, - ArrayRef(ModuleDecl *), Cached, NoLocationInfo) + ImplicitImportList(ModuleDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, NamingPatternRequest, NamedPattern *(VarDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, OpaqueReadOwnershipRequest, diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index e553925cf85c7..6095d2d8c0008 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -487,14 +487,13 @@ ModuleDecl::ModuleDecl(Identifier name, ASTContext &ctx, Bits.ModuleDecl.HasIncrementalInfo = 0; } -ArrayRef ModuleDecl::getImplicitImports() const { +ImplicitImportList ModuleDecl::getImplicitImports() const { auto &evaluator = getASTContext().evaluator; auto *mutableThis = const_cast(this); return evaluateOrDefault(evaluator, ModuleImplicitImportsRequest{mutableThis}, {}); } - void ModuleDecl::addFile(FileUnit &newFile) { // If this is a LoadedFile, make sure it loaded without error. assert(!(isa(newFile) && diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index da08ab9057d25..501499a62b8a9 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1397,6 +1397,14 @@ void swift::simple_display(llvm::raw_ostream &out, simple_display(out, import.Module); } +void swift::simple_display(llvm::raw_ostream &out, + const ImplicitImportList &importList) { + llvm::interleaveComma(importList.imports, out, + [&](const auto &import) { + simple_display(out, import); + }); +} + //----------------------------------------------------------------------------// // ResolveTypeRequest computation. //----------------------------------------------------------------------------// diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index b6825c8ef9e2f..68ad80ec9b3e0 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -174,15 +174,7 @@ class ImportResolver final : public DeclVisitor { addImplicitImports(); } - void addImplicitImports() { - // TODO: Support cross-module imports. - for (auto &import : SF.getParentModule()->getImplicitImports()) { - assert(!(SF.Kind == SourceFileKind::SIL && - import.Module->isStdlibModule())); - ImportedModule importedMod{ImportPath::Access(), import.Module}; - boundImports.emplace_back(importedMod, import.Options); - } - } + void addImplicitImports(); /// Retrieve the finalized imports. ArrayRef> getFinishedImports() const { @@ -400,7 +392,7 @@ UnboundImport::getTopLevelModule(ModuleDecl *M, SourceFile &SF) { // MARK: Implicit imports //===----------------------------------------------------------------------===// -ArrayRef +ImplicitImportList ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, ModuleDecl *module) const { SmallVector imports; @@ -468,7 +460,19 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, } } - return ctx.AllocateCopy(imports); + return { ctx.AllocateCopy(imports) }; +} + +void ImportResolver::addImplicitImports() { + auto implicitImports = SF.getParentModule()->getImplicitImports(); + + // TODO: Support cross-module imports. + for (auto &import : implicitImports.imports) { + assert(!(SF.Kind == SourceFileKind::SIL && + import.Module->isStdlibModule())); + ImportedModule importedMod{ImportPath::Access(), import.Module}; + boundImports.emplace_back(importedMod, import.Options); + } } //===----------------------------------------------------------------------===// From 563386b06d8c1cf863be71c38345fcaefa00e423 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Fri, 25 Sep 2020 16:34:33 -0700 Subject: [PATCH 380/745] [NFC] Eliminate ImplicitImport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead, we will use AttributedImport, a common currency type which supports a superset of ImplicitImport’s current behavior. --- include/swift/AST/Import.h | 29 ++++++++++++----------------- lib/AST/TypeCheckRequests.cpp | 34 +++++++++++++++++++++++++++++++--- lib/Sema/ImportResolution.cpp | 33 +++++++++++++++++++++------------ 3 files changed, 64 insertions(+), 32 deletions(-) diff --git a/include/swift/AST/Import.h b/include/swift/AST/Import.h index e6b28b438c22f..e94fd37189324 100644 --- a/include/swift/AST/Import.h +++ b/include/swift/AST/Import.h @@ -532,26 +532,21 @@ struct AttributedImport { options.contains(ImportFlags::ImplementationOnly)) || options.contains(ImportFlags::Reserved)); } -}; - -// MARK: - Implicit imports - -/// A module which has been implicitly imported. -struct ImplicitImport { - ModuleDecl *Module; - ImportOptions Options; - ImplicitImport(ModuleDecl *module, ImportOptions opts = {}) - : Module(module), Options(opts) {} - - friend bool operator==(const ImplicitImport &lhs, - const ImplicitImport &rhs) { - return lhs.Module == rhs.Module && - lhs.Options.toRaw() == rhs.Options.toRaw(); + friend bool operator==(const AttributedImport &lhs, + const AttributedImport &rhs) { + return lhs.module == rhs.module && + lhs.options.toRaw() == rhs.options.toRaw() && + lhs.sourceFileArg == rhs.sourceFileArg && + lhs.spiGroups == rhs.spiGroups; } }; -void simple_display(llvm::raw_ostream &out, const ImplicitImport &import); +/// A module which has been implicitly imported. +void simple_display(llvm::raw_ostream &out, + const AttributedImport &import); + +// MARK: - Implicit imports /// The kind of stdlib that should be imported. enum class ImplicitStdlibKind { @@ -592,7 +587,7 @@ struct ImplicitImportInfo { /// Contains names of and pointers to modules that must be implicitly imported. struct ImplicitImportList { - ArrayRef imports; + ArrayRef> imports; friend bool operator==(const ImplicitImportList &lhs, const ImplicitImportList &rhs) { diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 501499a62b8a9..feb1f4087388f 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1392,9 +1392,37 @@ TypeCheckFunctionBodyRequest::readDependencySource( //----------------------------------------------------------------------------// void swift::simple_display(llvm::raw_ostream &out, - const ImplicitImport &import) { - out << "implicit import of "; - simple_display(out, import.Module); + const AttributedImport &import) { + out << "import of "; + + if (!import.module.accessPath.empty()) { + simple_display(out, import.module.accessPath.front().Item); + out << " in "; + } + + simple_display(out, import.module.importedModule); + + out << " ["; + if (import.options.contains(ImportFlags::Exported)) + out << " exported"; + if (import.options.contains(ImportFlags::Testable)) + out << " testable"; + if (import.options.contains(ImportFlags::ImplementationOnly)) + out << " implementation-only"; + if (import.options.contains(ImportFlags::PrivateImport)) + out << " private(" << import.sourceFileArg << ")"; + + if (import.options.contains(ImportFlags::SPIAccessControl)) { + out << " spi("; + llvm::interleave(import.spiGroups, + [&out](Identifier name) { + simple_display(out, name); + }, + [&out]() { out << " "; }); + out << ")"; + } + + out << " ]"; } void swift::simple_display(llvm::raw_ostream &out, diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index 68ad80ec9b3e0..0c6b618fdcac7 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -395,26 +395,32 @@ UnboundImport::getTopLevelModule(ModuleDecl *M, SourceFile &SF) { ImplicitImportList ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, ModuleDecl *module) const { - SmallVector imports; + SmallVector, 4> imports; auto &ctx = module->getASTContext(); auto &importInfo = module->getImplicitImportInfo(); // Add an implicit stdlib if needed. + ModuleDecl *stdlib; switch (importInfo.StdlibKind) { case ImplicitStdlibKind::None: + stdlib = nullptr; break; case ImplicitStdlibKind::Builtin: - imports.emplace_back(ctx.TheBuiltinModule); + stdlib = ctx.TheBuiltinModule; break; case ImplicitStdlibKind::Stdlib: { - auto *stdlib = ctx.getStdlibModule(/*loadIfAbsent*/ true); + stdlib = ctx.getStdlibModule(/*loadIfAbsent*/ true); assert(stdlib && "Missing stdlib?"); - imports.emplace_back(stdlib); break; } } + if (stdlib) { + ImportedModule import(ImportPath::Access(), stdlib); + imports.emplace_back(import, ImportOptions()); + } + // Add any modules we were asked to implicitly import. for (auto moduleName : importInfo.ModuleNames) { auto *importModule = ctx.getModuleByIdentifier(moduleName); @@ -427,13 +433,15 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, } continue; } - imports.emplace_back(importModule); + ImportedModule import(ImportPath::Access(), importModule); + imports.emplace_back(import, ImportOptions()); } // Add any pre-loaded modules. for (auto &module : importInfo.AdditionalModules) { - imports.emplace_back(module.first, module.second ? ImportFlags::Exported - : ImportOptions()); + ImportedModule import(ImportPath::Access(), module.first); + imports.emplace_back(import, module.second ? ImportFlags::Exported + : ImportOptions()); } auto *clangImporter = @@ -445,7 +453,8 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, !clangImporter->importBridgingHeader(bridgingHeaderPath, module)) { auto *headerModule = clangImporter->getImportedHeaderModule(); assert(headerModule && "Didn't load bridging header?"); - imports.emplace_back(headerModule, ImportFlags::Exported); + ImportedModule import(ImportPath::Access(), headerModule); + imports.emplace_back(import, ImportFlags::Exported); } // Implicitly import the underlying Clang half of this module if needed. @@ -453,7 +462,8 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, auto *underlyingMod = clangImporter->loadModule( SourceLoc(), ImportPath::Module::Builder(module->getName()).get()); if (underlyingMod) { - imports.emplace_back(underlyingMod, ImportFlags::Exported); + ImportedModule import(ImportPath::Access(), underlyingMod); + imports.emplace_back(import, ImportFlags::Exported); } else { ctx.Diags.diagnose(SourceLoc(), diag::error_underlying_module_not_found, module->getName()); @@ -469,9 +479,8 @@ void ImportResolver::addImplicitImports() { // TODO: Support cross-module imports. for (auto &import : implicitImports.imports) { assert(!(SF.Kind == SourceFileKind::SIL && - import.Module->isStdlibModule())); - ImportedModule importedMod{ImportPath::Access(), import.Module}; - boundImports.emplace_back(importedMod, import.Options); + import.module.importedModule->isStdlibModule())); + boundImports.push_back(import); } } From c13067a0e0e9a1ea474a307c10d177c27e57fd58 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Fri, 25 Sep 2020 15:28:29 -0700 Subject: [PATCH 381/745] [NFC] Rework ImplicitImportInfo::ModuleNames Replace with an array of AttributedImport to add flexibility. --- include/swift/AST/Import.h | 9 +-- lib/Frontend/Frontend.cpp | 17 +++-- lib/FrontendTool/ScanDependencies.cpp | 8 +-- lib/IDE/REPLCodeCompletion.cpp | 9 +-- lib/Sema/ImportResolution.cpp | 67 +++++++++---------- lib/Serialization/ModuleDependencyScanner.cpp | 4 +- 6 files changed, 62 insertions(+), 52 deletions(-) diff --git a/include/swift/AST/Import.h b/include/swift/AST/Import.h index e94fd37189324..5f1b582157f20 100644 --- a/include/swift/AST/Import.h +++ b/include/swift/AST/Import.h @@ -572,13 +572,14 @@ struct ImplicitImportInfo { /// The bridging header path for this module, empty if there is none. StringRef BridgingHeaderPath; - /// The names of additional modules to be implicitly imported. - SmallVector ModuleNames; + /// The names of additional modules to be loaded and implicitly imported. + SmallVector, 4> + AdditionalUnloadedImports; /// An additional list of already-loaded modules which should be implicitly /// imported. - SmallVector, 4> - AdditionalModules; + SmallVector, 4> + AdditionalImports; ImplicitImportInfo() : StdlibKind(ImplicitStdlibKind::None), diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 8ca60ba422374..4d661e604c62c 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -740,11 +740,20 @@ ImplicitImportInfo CompilerInstance::getImplicitImportInfo() const { ImplicitImportInfo imports; imports.StdlibKind = Invocation.getImplicitStdlibKind(); - for (auto &moduleStr : frontendOpts.getImplicitImportModuleNames()) - imports.ModuleNames.push_back(Context->getIdentifier(moduleStr)); + auto pushImport = [&](StringRef moduleStr) { + ImportPath::Builder importPath(Context->getIdentifier(moduleStr)); + UnloadedImportedModule import(importPath.copyTo(*Context), + /*isScoped=*/false); + imports.AdditionalUnloadedImports.emplace_back(import, ImportOptions()); + }; + + for (auto &moduleStr : frontendOpts.getImplicitImportModuleNames()) { + pushImport(moduleStr); + } - if (Invocation.shouldImportSwiftONoneSupport()) - imports.ModuleNames.push_back(Context->getIdentifier(SWIFT_ONONE_SUPPORT)); + if (Invocation.shouldImportSwiftONoneSupport()) { + pushImport(SWIFT_ONONE_SUPPORT); + } imports.ShouldImportUnderlyingModule = frontendOpts.ImportUnderlyingModule; imports.BridgingHeaderPath = frontendOpts.ImplicitObjCHeaderPath; diff --git a/lib/FrontendTool/ScanDependencies.cpp b/lib/FrontendTool/ScanDependencies.cpp index a39468e13fb21..ede07f9064085 100644 --- a/lib/FrontendTool/ScanDependencies.cpp +++ b/lib/FrontendTool/ScanDependencies.cpp @@ -817,13 +817,13 @@ bool swift::scanDependencies(CompilerInstance &instance) { } // Add any implicit module names. - for (const auto &moduleName : importInfo.ModuleNames) { - mainDependencies.addModuleDependency(moduleName.str(), &alreadyAddedModules); + for (const auto &import : importInfo.AdditionalUnloadedImports) { + mainDependencies.addModuleDependency(import.module.getModulePath().front().Item.str(), &alreadyAddedModules); } // Already-loaded, implicitly imported module names. - for (const auto &module : importInfo.AdditionalModules) { - mainDependencies.addModuleDependency(module.first->getNameStr(), &alreadyAddedModules); + for (const auto &import : importInfo.AdditionalImports) { + mainDependencies.addModuleDependency(import.module.importedModule->getNameStr(), &alreadyAddedModules); } // Add the bridging header. diff --git a/lib/IDE/REPLCodeCompletion.cpp b/lib/IDE/REPLCodeCompletion.cpp index 1c7ffa17883b6..52566e24297a9 100644 --- a/lib/IDE/REPLCodeCompletion.cpp +++ b/lib/IDE/REPLCodeCompletion.cpp @@ -221,16 +221,17 @@ doCodeCompletion(SourceFile &SF, StringRef EnteredCode, unsigned *BufferID, auto *lastModule = SF.getParentModule(); ImplicitImportInfo implicitImports; - implicitImports.AdditionalModules.emplace_back(lastModule, - /*exported*/ false); + { + ImportedModule import(ImportPath::Access(), lastModule); + implicitImports.AdditionalImports.emplace_back(import, ImportOptions()); + } // Carry over the private imports from the last module. SmallVector imports; lastModule->getImportedModules(imports, ModuleDecl::ImportFilterKind::Default); for (auto &import : imports) { - implicitImports.AdditionalModules.emplace_back(import.importedModule, - /*exported*/ false); + implicitImports.AdditionalImports.emplace_back(import, ImportOptions()); } // Create a new module and file for the code completion buffer, similar to how diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index 0c6b618fdcac7..87aff0cb51056 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -392,6 +392,27 @@ UnboundImport::getTopLevelModule(ModuleDecl *M, SourceFile &SF) { // MARK: Implicit imports //===----------------------------------------------------------------------===// +static void diagnoseNoSuchModule(ASTContext &ctx, SourceLoc importLoc, + ImportPath::Module modulePath, + bool nonfatalInREPL) { + SmallString<64> modulePathStr; + llvm::interleave(modulePath, [&](ImportPath::Element elem) { + modulePathStr += elem.Item.str(); + }, + [&] { modulePathStr += "."; }); + + auto diagKind = diag::sema_no_import; + if (nonfatalInREPL && ctx.LangOpts.DebuggerSupport) + diagKind = diag::sema_no_import_repl; + ctx.Diags.diagnose(importLoc, diagKind, modulePathStr); + + if (ctx.SearchPathOpts.SDKPath.empty() && + llvm::Triple(llvm::sys::getProcessTriple()).isMacOSX()) { + ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import_no_sdk); + ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import_no_sdk_xcrun); + } +} + ImplicitImportList ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, ModuleDecl *module) const { @@ -422,27 +443,22 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, } // Add any modules we were asked to implicitly import. - for (auto moduleName : importInfo.ModuleNames) { - auto *importModule = ctx.getModuleByIdentifier(moduleName); + for (auto unloadedImport : importInfo.AdditionalUnloadedImports) { + auto *importModule = ctx.getModule(unloadedImport.module.getModulePath()); if (!importModule) { - ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import, moduleName.str()); - if (ctx.SearchPathOpts.SDKPath.empty() && - llvm::Triple(llvm::sys::getProcessTriple()).isMacOSX()) { - ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import_no_sdk); - ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import_no_sdk_xcrun); - } + diagnoseNoSuchModule(ctx, SourceLoc(), + unloadedImport.module.getModulePath(), + /*nonfatalInREPL=*/false); continue; } - ImportedModule import(ImportPath::Access(), importModule); - imports.emplace_back(import, ImportOptions()); + ImportedModule import(unloadedImport.module.getAccessPath(), importModule); + imports.emplace_back(import, unloadedImport.options, + unloadedImport.sourceFileArg, + unloadedImport.spiGroups); } // Add any pre-loaded modules. - for (auto &module : importInfo.AdditionalModules) { - ImportedModule import(ImportPath::Access(), module.first); - imports.emplace_back(import, module.second ? ImportFlags::Exported - : ImportOptions()); - } + llvm::copy(importInfo.AdditionalImports, std::back_inserter(imports)); auto *clangImporter = static_cast(ctx.getClangModuleLoader()); @@ -547,25 +563,8 @@ bool UnboundImport::checkModuleLoaded(ModuleDecl *M, SourceFile &SF) { if (M) return true; - ASTContext &ctx = SF.getASTContext(); - - SmallString<64> modulePathStr; - llvm::interleave(import.module.getModulePath(), - [&](ImportPath::Element elem) { - modulePathStr += elem.Item.str(); - }, - [&] { modulePathStr += "."; }); - - auto diagKind = diag::sema_no_import; - if (ctx.LangOpts.DebuggerSupport) - diagKind = diag::sema_no_import_repl; - ctx.Diags.diagnose(importLoc, diagKind, modulePathStr); - - if (ctx.SearchPathOpts.SDKPath.empty() && - llvm::Triple(llvm::sys::getProcessTriple()).isMacOSX()) { - ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import_no_sdk); - ctx.Diags.diagnose(SourceLoc(), diag::sema_no_import_no_sdk_xcrun); - } + diagnoseNoSuchModule(SF.getASTContext(), importLoc, + import.module.getModulePath(), /*nonfatalInREPL=*/true); return false; } diff --git a/lib/Serialization/ModuleDependencyScanner.cpp b/lib/Serialization/ModuleDependencyScanner.cpp index 600744840648e..f4041c1f0e85e 100644 --- a/lib/Serialization/ModuleDependencyScanner.cpp +++ b/lib/Serialization/ModuleDependencyScanner.cpp @@ -149,8 +149,8 @@ ErrorOr ModuleDependencyScanner::scanInterfaceFile( // Collect implicitly imported modules in case they are not explicitly // printed in the interface file, e.g. SwiftOnoneSupport. auto &imInfo = mainMod->getImplicitImportInfo(); - for (auto name: imInfo.ModuleNames) { - Result->addModuleDependency(name.str(), &alreadyAddedModules); + for (auto import: imInfo.AdditionalUnloadedImports) { + Result->addModuleDependency(import.module.getModulePath().front().Item.str(), &alreadyAddedModules); } return std::error_code(); }); From d02b34cccc3edf3fece4c112bc8637ed94b1d407 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Sat, 12 Sep 2020 23:26:21 -0700 Subject: [PATCH 382/745] [NFC] Add conveniences to clean up import code --- include/swift/AST/Import.h | 30 ++++++++++++++++++- include/swift/AST/ModuleDependencies.h | 7 +++++ lib/AST/ASTDumper.cpp | 6 +--- lib/AST/ModuleDependencies.cpp | 3 +- lib/AST/ModuleNameLookup.cpp | 3 +- lib/AST/TypeCheckRequests.cpp | 18 +++++------ lib/FrontendTool/ScanDependencies.cpp | 2 +- lib/IDE/REPLCodeCompletion.cpp | 7 ++--- lib/Sema/ImportResolution.cpp | 29 ++++++------------ lib/Serialization/ModuleDependencyScanner.cpp | 2 +- 10 files changed, 60 insertions(+), 47 deletions(-) diff --git a/include/swift/AST/Import.h b/include/swift/AST/Import.h index 5f1b582157f20..af88ac2760dbd 100644 --- a/include/swift/AST/Import.h +++ b/include/swift/AST/Import.h @@ -28,6 +28,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" #include namespace swift { @@ -151,6 +152,17 @@ namespace detail { if (empty()) return SourceRange(); return SourceRange(raw.front().Loc, raw.back().Loc); } + + void print(llvm::raw_ostream &os) const { + llvm::interleave(*this, + [&](Element elem) { os << elem.Item.str(); }, + [&]() { os << "."; }); + } + + void getString(SmallVectorImpl &modulePathStr) const { + llvm::raw_svector_ostream os(modulePathStr); + print(os); + } }; // These shims avoid circularity between ASTContext.h and Import.h. @@ -479,6 +491,9 @@ struct alignas(uint64_t) ImportedModule { assert(this->importedModule); } + explicit ImportedModule(ModuleDecl *importedModule) + : ImportedModule(ImportPath::Access(), importedModule) { } + bool operator==(const ImportedModule &other) const { return (this->importedModule == other.importedModule) && (this->accessPath == other.accessPath); @@ -490,6 +505,10 @@ struct alignas(uint64_t) ImportedModule { /// The order of items in \p imports is \e not preserved. static void removeDuplicates(SmallVectorImpl &imports); + // Purely here to allow ImportedModule and UnloadedImportedModule to + // substitute into the same templates. + ImportPath::Access getAccessPath() const { return accessPath; } + /// Arbitrarily orders ImportedModule records, for inclusion in sets and such. class Order { public: @@ -524,7 +543,7 @@ struct AttributedImport { /// Names of explicitly imported SPI groups. ArrayRef spiGroups; - AttributedImport(ModuleInfo module, ImportOptions options, + AttributedImport(ModuleInfo module, ImportOptions options = ImportOptions(), StringRef filename = {}, ArrayRef spiGroups = {}) : module(module), options(options), sourceFileArg(filename), spiGroups(spiGroups) { @@ -533,6 +552,11 @@ struct AttributedImport { options.contains(ImportFlags::Reserved)); } + template + AttributedImport(ModuleInfo module, AttributedImport other) + : AttributedImport(module, other.options, other.sourceFileArg, + other.spiGroups) { } + friend bool operator==(const AttributedImport &lhs, const AttributedImport &rhs) { return lhs.module == rhs.module && @@ -540,6 +564,10 @@ struct AttributedImport { lhs.sourceFileArg == rhs.sourceFileArg && lhs.spiGroups == rhs.spiGroups; } + + AttributedImport getLoaded(ModuleDecl *loadedModule) const { + return { ImportedModule(module.getAccessPath(), loadedModule), *this }; + } }; /// A module which has been implicitly imported. diff --git a/include/swift/AST/ModuleDependencies.h b/include/swift/AST/ModuleDependencies.h index 5eab3844b32d8..8d4af0907bf96 100644 --- a/include/swift/AST/ModuleDependencies.h +++ b/include/swift/AST/ModuleDependencies.h @@ -19,6 +19,7 @@ #define SWIFT_AST_MODULE_DEPENDENCIES_H #include "swift/Basic/LLVM.h" +#include "swift/AST/Import.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSet.h" @@ -330,6 +331,12 @@ class ModuleDependencies { void addModuleDependency(StringRef module, llvm::StringSet<> *alreadyAddedModules = nullptr); + /// Add a dependency on the given module, if it was not already in the set. + void addModuleDependency(ImportPath::Module module, + llvm::StringSet<> *alreadyAddedModules = nullptr) { + addModuleDependency(module.front().Item.str(), alreadyAddedModules); + } + /// Add all of the module dependencies for the imports in the given source /// file to the set of module dependencies. void addModuleDependencies(const SourceFile &sf, diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index aff01f211fce0..b466124e24034 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -620,11 +620,7 @@ namespace { OS << " kind=" << getImportKindString(ID->getImportKind()); OS << " '"; - llvm::interleave(ID->getImportPath(), - [&](const ImportPath::Element &Elem) { - OS << Elem.Item; - }, - [&] { OS << '.'; }); + ID->getImportPath().print(OS); OS << "')"; } diff --git a/lib/AST/ModuleDependencies.cpp b/lib/AST/ModuleDependencies.cpp index 45821a23424a8..c6b2bfaac066a 100644 --- a/lib/AST/ModuleDependencies.cpp +++ b/lib/AST/ModuleDependencies.cpp @@ -62,8 +62,7 @@ void ModuleDependencies::addModuleDependencies( if (!importDecl) continue; - addModuleDependency(importDecl->getModulePath().front().Item.str(), - &alreadyAddedModules); + addModuleDependency(importDecl->getModulePath(), &alreadyAddedModules); } auto fileName = sf.getFilename(); diff --git a/lib/AST/ModuleNameLookup.cpp b/lib/AST/ModuleNameLookup.cpp index 56ea9383c6d29..16c9d96a87c8e 100644 --- a/lib/AST/ModuleNameLookup.cpp +++ b/lib/AST/ModuleNameLookup.cpp @@ -202,8 +202,7 @@ void ModuleNameLookup::lookupInModule( if (auto *loader = ctx.getClangModuleLoader()) { headerImportModule = loader->getImportedHeaderModule(); if (headerImportModule) { - ImportedModule import{ImportPath::Access(), headerImportModule}; - visitImport(import, nullptr); + visitImport(ImportedModule(headerImportModule), nullptr); } } } diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index feb1f4087388f..2bbdbe27dca3c 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1395,14 +1395,14 @@ void swift::simple_display(llvm::raw_ostream &out, const AttributedImport &import) { out << "import of "; - if (!import.module.accessPath.empty()) { - simple_display(out, import.module.accessPath.front().Item); - out << " in "; - } - simple_display(out, import.module.importedModule); out << " ["; + if (!import.module.accessPath.empty()) { + out << " scoped("; + import.module.accessPath.print(out); + out << ")"; + } if (import.options.contains(ImportFlags::Exported)) out << " exported"; if (import.options.contains(ImportFlags::Testable)) @@ -1414,11 +1414,9 @@ void swift::simple_display(llvm::raw_ostream &out, if (import.options.contains(ImportFlags::SPIAccessControl)) { out << " spi("; - llvm::interleave(import.spiGroups, - [&out](Identifier name) { - simple_display(out, name); - }, - [&out]() { out << " "; }); + llvm::interleaveComma(import.spiGroups, out, [&out](Identifier name) { + simple_display(out, name); + }); out << ")"; } diff --git a/lib/FrontendTool/ScanDependencies.cpp b/lib/FrontendTool/ScanDependencies.cpp index ede07f9064085..8ab5f040d1a4e 100644 --- a/lib/FrontendTool/ScanDependencies.cpp +++ b/lib/FrontendTool/ScanDependencies.cpp @@ -818,7 +818,7 @@ bool swift::scanDependencies(CompilerInstance &instance) { // Add any implicit module names. for (const auto &import : importInfo.AdditionalUnloadedImports) { - mainDependencies.addModuleDependency(import.module.getModulePath().front().Item.str(), &alreadyAddedModules); + mainDependencies.addModuleDependency(import.module.getModulePath(), &alreadyAddedModules); } // Already-loaded, implicitly imported module names. diff --git a/lib/IDE/REPLCodeCompletion.cpp b/lib/IDE/REPLCodeCompletion.cpp index 52566e24297a9..02cdd4d82078b 100644 --- a/lib/IDE/REPLCodeCompletion.cpp +++ b/lib/IDE/REPLCodeCompletion.cpp @@ -221,17 +221,14 @@ doCodeCompletion(SourceFile &SF, StringRef EnteredCode, unsigned *BufferID, auto *lastModule = SF.getParentModule(); ImplicitImportInfo implicitImports; - { - ImportedModule import(ImportPath::Access(), lastModule); - implicitImports.AdditionalImports.emplace_back(import, ImportOptions()); - } + implicitImports.AdditionalImports.emplace_back(ImportedModule(lastModule)); // Carry over the private imports from the last module. SmallVector imports; lastModule->getImportedModules(imports, ModuleDecl::ImportFilterKind::Default); for (auto &import : imports) { - implicitImports.AdditionalImports.emplace_back(import, ImportOptions()); + implicitImports.AdditionalImports.emplace_back(import); } // Create a new module and file for the code completion buffer, similar to how diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index 87aff0cb51056..a406472748240 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -118,8 +118,7 @@ struct UnboundImport { /// UnboundImport. AttributedImport makeAttributedImport(ModuleDecl *module) const { - return { ImportedModule{ import.module.getAccessPath(), module }, - import.options, import.sourceFileArg, import.spiGroups }; + return import.getLoaded(module); } private: @@ -396,10 +395,7 @@ static void diagnoseNoSuchModule(ASTContext &ctx, SourceLoc importLoc, ImportPath::Module modulePath, bool nonfatalInREPL) { SmallString<64> modulePathStr; - llvm::interleave(modulePath, [&](ImportPath::Element elem) { - modulePathStr += elem.Item.str(); - }, - [&] { modulePathStr += "."; }); + modulePath.getString(modulePathStr); auto diagKind = diag::sema_no_import; if (nonfatalInREPL && ctx.LangOpts.DebuggerSupport) @@ -430,17 +426,14 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, case ImplicitStdlibKind::Builtin: stdlib = ctx.TheBuiltinModule; break; - case ImplicitStdlibKind::Stdlib: { + case ImplicitStdlibKind::Stdlib: stdlib = ctx.getStdlibModule(/*loadIfAbsent*/ true); assert(stdlib && "Missing stdlib?"); break; } - } - if (stdlib) { - ImportedModule import(ImportPath::Access(), stdlib); - imports.emplace_back(import, ImportOptions()); - } + if (stdlib) + imports.emplace_back(ImportedModule(stdlib)); // Add any modules we were asked to implicitly import. for (auto unloadedImport : importInfo.AdditionalUnloadedImports) { @@ -451,10 +444,7 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, /*nonfatalInREPL=*/false); continue; } - ImportedModule import(unloadedImport.module.getAccessPath(), importModule); - imports.emplace_back(import, unloadedImport.options, - unloadedImport.sourceFileArg, - unloadedImport.spiGroups); + imports.push_back(unloadedImport.getLoaded(importModule)); } // Add any pre-loaded modules. @@ -469,8 +459,7 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, !clangImporter->importBridgingHeader(bridgingHeaderPath, module)) { auto *headerModule = clangImporter->getImportedHeaderModule(); assert(headerModule && "Didn't load bridging header?"); - ImportedModule import(ImportPath::Access(), headerModule); - imports.emplace_back(import, ImportFlags::Exported); + imports.emplace_back(ImportedModule(headerModule), ImportFlags::Exported); } // Implicitly import the underlying Clang half of this module if needed. @@ -478,8 +467,8 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, auto *underlyingMod = clangImporter->loadModule( SourceLoc(), ImportPath::Module::Builder(module->getName()).get()); if (underlyingMod) { - ImportedModule import(ImportPath::Access(), underlyingMod); - imports.emplace_back(import, ImportFlags::Exported); + imports.emplace_back(ImportedModule(underlyingMod), + ImportFlags::Exported); } else { ctx.Diags.diagnose(SourceLoc(), diag::error_underlying_module_not_found, module->getName()); diff --git a/lib/Serialization/ModuleDependencyScanner.cpp b/lib/Serialization/ModuleDependencyScanner.cpp index f4041c1f0e85e..4e211d525dbe1 100644 --- a/lib/Serialization/ModuleDependencyScanner.cpp +++ b/lib/Serialization/ModuleDependencyScanner.cpp @@ -150,7 +150,7 @@ ErrorOr ModuleDependencyScanner::scanInterfaceFile( // printed in the interface file, e.g. SwiftOnoneSupport. auto &imInfo = mainMod->getImplicitImportInfo(); for (auto import: imInfo.AdditionalUnloadedImports) { - Result->addModuleDependency(import.module.getModulePath().front().Item.str(), &alreadyAddedModules); + Result->addModuleDependency(import.module.getModulePath(), &alreadyAddedModules); } return std::error_code(); }); From 7f14affda09fb57414fa7d0203a886845dbd47fc Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Fri, 25 Sep 2020 11:02:28 -0700 Subject: [PATCH 383/745] [NFC] Give NullablePtr some PointerLikeTypeTraits Allows it to be used in PointerIntPair and PointerUnion. --- include/swift/Basic/NullablePtr.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/swift/Basic/NullablePtr.h b/include/swift/Basic/NullablePtr.h index 316cfbb46251f..75a81eb9a1924 100644 --- a/include/swift/Basic/NullablePtr.h +++ b/include/swift/Basic/NullablePtr.h @@ -20,6 +20,7 @@ #include #include #include +#include "llvm/Support/PointerLikeTypeTraits.h" namespace swift { /// NullablePtr pointer wrapper - NullablePtr is used for APIs where a @@ -81,4 +82,19 @@ class NullablePtr { } // end namespace swift +namespace llvm { +template struct PointerLikeTypeTraits; +template struct PointerLikeTypeTraits> { +public: + static inline void *getAsVoidPointer(swift::NullablePtr ptr) { + return static_cast(ptr.getPtrOrNull()); + } + static inline swift::NullablePtr getFromVoidPointer(void *ptr) { + return swift::NullablePtr(static_cast(ptr)); + } + enum { NumLowBitsAvailable = PointerLikeTypeTraits::NumLowBitsAvailable }; +}; + +} + #endif // SWIFT_BASIC_NULLABLEPTR_H From bf074b093ea9b8ef4dcb90a5494343edbef39939 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Fri, 25 Sep 2020 17:32:16 -0700 Subject: [PATCH 384/745] Run some implicit imports through import resolution Unloaded implicit imports (e.g. the `-import-module` flag) will now be processed by import resolution, and will be fully validated and cross-imported. Preloaded imports, like the standard library import, are still not run through full import resolution, but this is a definite improvement over the status quo. This also folds `-import-underlying-module` and `@_exported import ` into a single code path, slightly changing the diagnostic for a failed overlay-style underlying module import. --- include/swift/AST/Import.h | 8 +- lib/AST/TypeCheckRequests.cpp | 38 +++++++ lib/Sema/ImportResolution.cpp | 102 +++++++++++------- .../mixed-target-using-module.swift | 8 ++ test/CrossImport/common-case.swift | 5 +- 5 files changed, 121 insertions(+), 40 deletions(-) diff --git a/include/swift/AST/Import.h b/include/swift/AST/Import.h index af88ac2760dbd..ebcfc38369e19 100644 --- a/include/swift/AST/Import.h +++ b/include/swift/AST/Import.h @@ -574,6 +574,10 @@ struct AttributedImport { void simple_display(llvm::raw_ostream &out, const AttributedImport &import); +/// A module which will be implicitly imported. +void simple_display(llvm::raw_ostream &out, + const AttributedImport &import); + // MARK: - Implicit imports /// The kind of stdlib that should be imported. @@ -617,10 +621,12 @@ struct ImplicitImportInfo { /// Contains names of and pointers to modules that must be implicitly imported. struct ImplicitImportList { ArrayRef> imports; + ArrayRef> unloadedImports; friend bool operator==(const ImplicitImportList &lhs, const ImplicitImportList &rhs) { - return lhs.imports == rhs.imports; + return lhs.imports == rhs.imports + && lhs.unloadedImports == rhs.unloadedImports; } }; diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 2bbdbe27dca3c..5087a6d5a4501 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1423,12 +1423,50 @@ void swift::simple_display(llvm::raw_ostream &out, out << " ]"; } +void swift::simple_display(llvm::raw_ostream &out, + const AttributedImport &import) { + out << "import of unloaded "; + + import.module.getModulePath().print(out); + + out << " ["; + if (!import.module.getAccessPath().empty()) { + out << " scoped("; + import.module.getAccessPath().print(out); + out << ")"; + } + if (import.options.contains(ImportFlags::Exported)) + out << " exported"; + if (import.options.contains(ImportFlags::Testable)) + out << " testable"; + if (import.options.contains(ImportFlags::ImplementationOnly)) + out << " implementation-only"; + if (import.options.contains(ImportFlags::PrivateImport)) + out << " private(" << import.sourceFileArg << ")"; + + if (import.options.contains(ImportFlags::SPIAccessControl)) { + out << " spi("; + llvm::interleaveComma(import.spiGroups, out, [&out](Identifier name) { + simple_display(out, name); + }); + out << ")"; + } + + out << " ]"; +} + void swift::simple_display(llvm::raw_ostream &out, const ImplicitImportList &importList) { llvm::interleaveComma(importList.imports, out, [&](const auto &import) { simple_display(out, import); }); + if (!importList.imports.empty() && !importList.unloadedImports.empty()) + out << ", "; + llvm::interleaveComma(importList.unloadedImports, out, + [&](const auto &import) { + simple_display(out, import); + }); } //----------------------------------------------------------------------------// diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index a406472748240..017fe84c4e5f6 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -65,11 +65,12 @@ struct UnboundImport { /// /// If this UnboundImport represents a cross-import, contains the declaring /// module's \c ModuleDecl. - PointerUnion importOrUnderlyingModuleDecl; + PointerUnion, ModuleDecl *> + importOrUnderlyingModuleDecl; NullablePtr getImportDecl() const { - return importOrUnderlyingModuleDecl.is() ? - importOrUnderlyingModuleDecl.get() : nullptr; + return importOrUnderlyingModuleDecl.is>() ? + importOrUnderlyingModuleDecl.get>() : nullptr; } NullablePtr getUnderlyingModule() const { @@ -80,6 +81,9 @@ struct UnboundImport { /// Create an UnboundImport for a user-written import declaration. explicit UnboundImport(ImportDecl *ID); + /// Create an UnboundImport for an unloaded implicit import. + explicit UnboundImport(AttributedImport implicit); + /// Create an UnboundImport for a cross-import overlay. explicit UnboundImport(ASTContext &ctx, const UnboundImport &base, Identifier overlayName, @@ -192,6 +196,9 @@ class ImportResolver final : public DeclVisitor { return ctx.Diags.diagnose(std::forward(Args)...); } + /// Calls \c bindImport() on unbound imports until \c boundImports is drained. + void bindPendingImports(); + /// Check a single unbound import, bind it, add it to \c boundImports, /// and add its cross-import overlays to \c unboundImports. void bindImport(UnboundImport &&I); @@ -284,6 +291,10 @@ void ImportResolver::visitImportDecl(ImportDecl *ID) { assert(unboundImports.empty()); unboundImports.emplace_back(ID); + bindPendingImports(); +} + +void ImportResolver::bindPendingImports() { while(!unboundImports.empty()) bindImport(unboundImports.pop_back_val()); } @@ -336,14 +347,16 @@ void ImportResolver::addImport(const UnboundImport &I, ModuleDecl *M) { // MARK: Import module loading //===----------------------------------------------------------------------===// -ModuleDecl * -ImportResolver::getModule(ImportPath::Module modulePath) { +static ModuleDecl * +getModuleImpl(ImportPath::Module modulePath, ModuleDecl *loadingModule, + bool canImportBuiltin) { + ASTContext &ctx = loadingModule->getASTContext(); + assert(!modulePath.empty()); auto moduleID = modulePath[0]; // The Builtin module cannot be explicitly imported unless we're a .sil file. - if (SF.Kind == SourceFileKind::SIL && - moduleID.Item == ctx.TheBuiltinModule->getName()) + if (canImportBuiltin && moduleID.Item == ctx.TheBuiltinModule->getName()) return ctx.TheBuiltinModule; // If the imported module name is the same as the current module, @@ -352,8 +365,7 @@ ImportResolver::getModule(ImportPath::Module modulePath) { // // FIXME: We'd like to only use this in SIL mode, but unfortunately we use it // for clang overlays as well. - if (moduleID.Item == SF.getParentModule()->getName() && - modulePath.size() == 1) { + if (moduleID.Item == loadingModule->getName() && modulePath.size() == 1) { if (auto importer = ctx.getClangModuleLoader()) return importer->loadModule(moduleID.Loc, modulePath); return nullptr; @@ -362,6 +374,12 @@ ImportResolver::getModule(ImportPath::Module modulePath) { return ctx.getModule(modulePath); } +ModuleDecl * +ImportResolver::getModule(ImportPath::Module modulePath) { + return getModuleImpl(modulePath, SF.getParentModule(), + /*canImportBuiltin=*/SF.Kind == SourceFileKind::SIL); +} + NullablePtr UnboundImport::getTopLevelModule(ModuleDecl *M, SourceFile &SF) { if (import.module.getModulePath().size() == 1) @@ -391,16 +409,25 @@ UnboundImport::getTopLevelModule(ModuleDecl *M, SourceFile &SF) { // MARK: Implicit imports //===----------------------------------------------------------------------===// -static void diagnoseNoSuchModule(ASTContext &ctx, SourceLoc importLoc, +static void diagnoseNoSuchModule(ModuleDecl *importingModule, + SourceLoc importLoc, ImportPath::Module modulePath, bool nonfatalInREPL) { - SmallString<64> modulePathStr; - modulePath.getString(modulePathStr); - - auto diagKind = diag::sema_no_import; - if (nonfatalInREPL && ctx.LangOpts.DebuggerSupport) - diagKind = diag::sema_no_import_repl; - ctx.Diags.diagnose(importLoc, diagKind, modulePathStr); + ASTContext &ctx = importingModule->getASTContext(); + + if (modulePath.size() == 1 && + importingModule->getName() == modulePath.front().Item) { + ctx.Diags.diagnose(importLoc, diag::error_underlying_module_not_found, + importingModule->getName()); + } else { + SmallString<64> modulePathStr; + modulePath.getString(modulePathStr); + + auto diagKind = diag::sema_no_import; + if (nonfatalInREPL && ctx.LangOpts.DebuggerSupport) + diagKind = diag::sema_no_import_repl; + ctx.Diags.diagnose(importLoc, diagKind, modulePathStr); + } if (ctx.SearchPathOpts.SDKPath.empty() && llvm::Triple(llvm::sys::getProcessTriple()).isMacOSX()) { @@ -413,6 +440,7 @@ ImplicitImportList ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, ModuleDecl *module) const { SmallVector, 4> imports; + SmallVector, 4> unloadedImports; auto &ctx = module->getASTContext(); auto &importInfo = module->getImplicitImportInfo(); @@ -436,16 +464,8 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, imports.emplace_back(ImportedModule(stdlib)); // Add any modules we were asked to implicitly import. - for (auto unloadedImport : importInfo.AdditionalUnloadedImports) { - auto *importModule = ctx.getModule(unloadedImport.module.getModulePath()); - if (!importModule) { - diagnoseNoSuchModule(ctx, SourceLoc(), - unloadedImport.module.getModulePath(), - /*nonfatalInREPL=*/false); - continue; - } - imports.push_back(unloadedImport.getLoaded(importModule)); - } + llvm::copy(importInfo.AdditionalUnloadedImports, + std::back_inserter(unloadedImports)); // Add any pre-loaded modules. llvm::copy(importInfo.AdditionalImports, std::back_inserter(imports)); @@ -464,18 +484,15 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, // Implicitly import the underlying Clang half of this module if needed. if (importInfo.ShouldImportUnderlyingModule) { - auto *underlyingMod = clangImporter->loadModule( - SourceLoc(), ImportPath::Module::Builder(module->getName()).get()); - if (underlyingMod) { - imports.emplace_back(ImportedModule(underlyingMod), - ImportFlags::Exported); - } else { - ctx.Diags.diagnose(SourceLoc(), diag::error_underlying_module_not_found, - module->getName()); - } + // An @_exported self-import is loaded from ClangImporter instead of being + // rejected; see the special case in getModuleImpl() for details. + ImportPath::Builder importPath(module->getName()); + unloadedImports.emplace_back(UnloadedImportedModule(importPath.copyTo(ctx), + /*isScoped=*/false), + ImportFlags::Exported); } - return { ctx.AllocateCopy(imports) }; + return { ctx.AllocateCopy(imports), ctx.AllocateCopy(unloadedImports) }; } void ImportResolver::addImplicitImports() { @@ -487,8 +504,17 @@ void ImportResolver::addImplicitImports() { import.module.importedModule->isStdlibModule())); boundImports.push_back(import); } + + for (auto &unloadedImport : implicitImports.unloadedImports) + unboundImports.emplace_back(unloadedImport); + + bindPendingImports(); } +UnboundImport::UnboundImport(AttributedImport implicit) + : import(implicit), importLoc(), + importOrUnderlyingModuleDecl(static_cast(nullptr)) {} + //===----------------------------------------------------------------------===// // MARK: Import validation (except for scoped imports) //===----------------------------------------------------------------------===// @@ -552,7 +578,7 @@ bool UnboundImport::checkModuleLoaded(ModuleDecl *M, SourceFile &SF) { if (M) return true; - diagnoseNoSuchModule(SF.getASTContext(), importLoc, + diagnoseNoSuchModule(SF.getParentModule(), importLoc, import.module.getModulePath(), /*nonfatalInREPL=*/true); return false; } diff --git a/test/ClangImporter/MixedSource/mixed-target-using-module.swift b/test/ClangImporter/MixedSource/mixed-target-using-module.swift index 0c007f6dd7895..7cacecc69c4fd 100644 --- a/test/ClangImporter/MixedSource/mixed-target-using-module.swift +++ b/test/ClangImporter/MixedSource/mixed-target-using-module.swift @@ -1,6 +1,14 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name Mixed -import-underlying-module -typecheck %s -verify -enable-objc-interop -disable-objc-attr-requires-foundation-module // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name Mixed -import-underlying-module -enable-objc-interop -emit-ir %S/../../Inputs/empty.swift | %FileCheck -check-prefix=CHECK-AUTOLINK %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name Mixed -DOVERLAY_STYLE_RIGHT -enable-objc-interop -emit-ir %S/../../Inputs/empty.swift | %FileCheck -check-prefix=CHECK-AUTOLINK %s // RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name WrongName -import-underlying-module -typecheck %s -enable-objc-interop -disable-objc-attr-requires-foundation-module 2>&1 | %FileCheck -check-prefix=CHECK-WRONG-NAME %s +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name WrongName -DOVERLAY_STYLE_WRONG -typecheck %s -enable-objc-interop -disable-objc-attr-requires-foundation-module 2>&1 | %FileCheck -check-prefix=CHECK-WRONG-NAME %s + +#if OVERLAY_STYLE_RIGHT +@_exported import Mixed +#elseif OVERLAY_STYLE_WRONG +@_exported import WrongName +#endif // CHECK-AUTOLINK: !llvm.linker.options = !{ // CHECK-AUTOLINK-NOT: !"-framework" diff --git a/test/CrossImport/common-case.swift b/test/CrossImport/common-case.swift index f29a547e6004e..7ac70d60823cf 100644 --- a/test/CrossImport/common-case.swift +++ b/test/CrossImport/common-case.swift @@ -5,10 +5,13 @@ // RUN: cp -r %S/Inputs/lib-templates/* %t/ // RUN: %{python} %S/Inputs/rewrite-module-triples.py %t %module-target-triple -// RUN: %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks +// RUN: %target-typecheck-verify-swift -enable-cross-import-overlays -DIMPORT_BYSTANDING_LIBRARY -I %t/include -I %t/lib/swift -F %t/Frameworks +// RUN: %target-typecheck-verify-swift -enable-cross-import-overlays -import-module BystandingLibrary -I %t/include -I %t/lib/swift -F %t/Frameworks // Each framework has a cross-import overlay with this library: +#if IMPORT_BYSTANDING_LIBRARY import BystandingLibrary +#endif // 1. A Swift framework From f71923778b6ddbb50500744e1460bca96ea35ff5 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Sun, 13 Sep 2020 17:05:44 -0700 Subject: [PATCH 385/745] Add -testable-import-module frontend flag This is just like -import-module, but it does an @testable import. Simple, right? Fixes rdar://66544654. --- include/swift/Frontend/FrontendOptions.h | 6 ++++-- include/swift/Option/FrontendOptions.td | 3 +++ lib/Frontend/ArgsToFrontendOptionsConverter.cpp | 10 ++++++---- lib/Frontend/ArgsToFrontendOptionsConverter.h | 3 ++- lib/Frontend/Frontend.cpp | 11 +++++++---- test/ImportResolution/import-command-line.swift | 3 +++ .../lib/SwiftLang/CodeCompletionOrganizer.cpp | 4 ++-- 7 files changed, 27 insertions(+), 13 deletions(-) diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index dc0dff3c449f2..66fdcc94afefd 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -34,7 +34,8 @@ class FrontendOptions { friend class ArgsToFrontendOptionsConverter; /// A list of arbitrary modules to import and make implicitly visible. - std::vector ImplicitImportModuleNames; + std::vector> + ImplicitImportModuleNames; public: FrontendInputsAndOutputs InputsAndOutputs; @@ -351,7 +352,8 @@ class FrontendOptions { /// Retrieves the list of arbitrary modules to import and make implicitly /// visible. - ArrayRef getImplicitImportModuleNames() const { + ArrayRef> + getImplicitImportModuleNames() const { return ImplicitImportModuleNames; } diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index fe6e53afe9c98..8c2a727c4947b 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -512,6 +512,9 @@ def enable_throw_without_try : Flag<["-"], "enable-throw-without-try">, def import_module : Separate<["-"], "import-module">, HelpText<"Implicitly import the specified module">; +def testable_import_module : Separate<["-"], "testable-import-module">, + HelpText<"Implicitly import the specified module with @testable">; + def print_stats : Flag<["-"], "print-stats">, HelpText<"Print various statistics">; diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 8634d26296f1c..43c131981211a 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -215,7 +215,8 @@ bool ArgsToFrontendOptionsConverter::convert( Opts.DisableBuildingInterface = Args.hasArg(OPT_disable_building_interface); computeImportObjCHeaderOptions(); - computeImplicitImportModuleNames(); + computeImplicitImportModuleNames(OPT_import_module, /*isTestable=*/false); + computeImplicitImportModuleNames(OPT_testable_import_module, /*isTestable=*/true); computeLLVMArgs(); return false; @@ -586,16 +587,17 @@ void ArgsToFrontendOptionsConverter::computeImportObjCHeaderOptions() { Opts.SerializeBridgingHeader |= !Opts.InputsAndOutputs.hasPrimaryInputs(); } } -void ArgsToFrontendOptionsConverter::computeImplicitImportModuleNames() { +void ArgsToFrontendOptionsConverter:: +computeImplicitImportModuleNames(OptSpecifier id, bool isTestable) { using namespace options; - for (const Arg *A : Args.filtered(OPT_import_module)) { + for (const Arg *A : Args.filtered(id)) { auto *moduleStr = A->getValue(); if (!Lexer::isIdentifier(moduleStr)) { Diags.diagnose(SourceLoc(), diag::error_bad_module_name, moduleStr, /*suggestModuleNameFlag*/ false); continue; } - Opts.ImplicitImportModuleNames.push_back(moduleStr); + Opts.ImplicitImportModuleNames.emplace_back(moduleStr, isTestable); } } void ArgsToFrontendOptionsConverter::computeLLVMArgs() { diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.h b/lib/Frontend/ArgsToFrontendOptionsConverter.h index 6e20e3688a8fa..66d85512ff068 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.h +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.h @@ -40,7 +40,8 @@ class ArgsToFrontendOptionsConverter { bool computeMainAndSupplementaryOutputFilenames(); void computeDumpScopeMapLocations(); void computeHelpOptions(); - void computeImplicitImportModuleNames(); + void computeImplicitImportModuleNames(llvm::opt::OptSpecifier id, + bool isTestable); void computeImportObjCHeaderOptions(); void computeLLVMArgs(); void computePlaygroundOptions(); diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 4d661e604c62c..9601547de0193 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -740,15 +740,18 @@ ImplicitImportInfo CompilerInstance::getImplicitImportInfo() const { ImplicitImportInfo imports; imports.StdlibKind = Invocation.getImplicitStdlibKind(); - auto pushImport = [&](StringRef moduleStr) { + auto pushImport = [&](StringRef moduleStr, + ImportOptions options = ImportOptions()) { ImportPath::Builder importPath(Context->getIdentifier(moduleStr)); UnloadedImportedModule import(importPath.copyTo(*Context), /*isScoped=*/false); - imports.AdditionalUnloadedImports.emplace_back(import, ImportOptions()); + imports.AdditionalUnloadedImports.emplace_back(import, options); }; - for (auto &moduleStr : frontendOpts.getImplicitImportModuleNames()) { - pushImport(moduleStr); + for (auto &moduleStrAndTestable : frontendOpts.getImplicitImportModuleNames()) { + pushImport(moduleStrAndTestable.first, + moduleStrAndTestable.second ? ImportFlags::Testable + : ImportOptions()); } if (Invocation.shouldImportSwiftONoneSupport()) { diff --git a/test/ImportResolution/import-command-line.swift b/test/ImportResolution/import-command-line.swift index 8a305450578b5..1f0012bdefd4e 100644 --- a/test/ImportResolution/import-command-line.swift +++ b/test/ImportResolution/import-command-line.swift @@ -7,5 +7,8 @@ // RUN: not %target-swift-frontend -typecheck %s -enable-source-import -I %S/Inputs -sdk "" -import-module NON_EXISTENT 2>&1 | %FileCheck -check-prefix=NON-EXISTENT %s // NON-EXISTENT: error: no such module 'NON_EXISTENT' +// RUN: not %target-swift-frontend -typecheck %s -enable-source-import -I %S/Inputs -sdk "" -testable-import-module abcde 2>&1 | %FileCheck -check-prefix=NON-TESTABLE %s +// NON-TESTABLE: error: module 'abcde' was not compiled for testing + var a: A? // expected-error {{cannot find type 'A' in scope}} var qA: abcde.A? // expected-error {{cannot find type 'abcde' in scope}} diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp index 731ba21165ffa..0268272f6e1d5 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp @@ -347,9 +347,9 @@ ImportDepth::ImportDepth(ASTContext &context, // Imports from -import-name such as Playground auxiliary sources are treated // specially by applying import depth 0. llvm::StringSet<> auxImports; - for (StringRef moduleName : + for (const auto &pair : invocation.getFrontendOptions().getImplicitImportModuleNames()) - auxImports.insert(moduleName); + auxImports.insert(pair.first); // Private imports from this module. // FIXME: only the private imports from the current source file. From 48b866b8fe179faa49f2c407a9920f28539c496c Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Fri, 9 Oct 2020 17:47:40 -0700 Subject: [PATCH 386/745] [NFC] Compare spiGroups in DenseMapInfo This ought to result in more consistent behavior. --- include/swift/AST/Import.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/include/swift/AST/Import.h b/include/swift/AST/Import.h index ebcfc38369e19..1ee68596eb7f4 100644 --- a/include/swift/AST/Import.h +++ b/include/swift/AST/Import.h @@ -691,17 +691,20 @@ struct DenseMapInfo> { using ModuleInfoDMI = DenseMapInfo; using ImportOptionsDMI = DenseMapInfo; using StringRefDMI = DenseMapInfo; - // FIXME: SPI groups not used by DenseMapInfo??? + // We can't include spiGroups in the hash because ArrayRef is not + // DenseMapInfo-able, but we do check that the spiGroups match in isEqual(). static inline AttributedImport getEmptyKey() { return AttributedImport(ModuleInfoDMI::getEmptyKey(), ImportOptionsDMI::getEmptyKey(), - StringRefDMI::getEmptyKey()); + StringRefDMI::getEmptyKey(), + {}); } static inline AttributedImport getTombstoneKey() { return AttributedImport(ModuleInfoDMI::getTombstoneKey(), ImportOptionsDMI::getTombstoneKey(), - StringRefDMI::getTombstoneKey()); + StringRefDMI::getTombstoneKey(), + {}); } static inline unsigned getHashValue(const AttributedImport &import) { return detail::combineHashValue( @@ -714,7 +717,8 @@ struct DenseMapInfo> { const AttributedImport &b) { return ModuleInfoDMI::isEqual(a.module, b.module) && ImportOptionsDMI::isEqual(a.options, b.options) && - StringRefDMI::isEqual(a.sourceFileArg, b.sourceFileArg); + StringRefDMI::isEqual(a.sourceFileArg, b.sourceFileArg) && + a.spiGroups == b.spiGroups; } }; } From 944ad8845582e9de58745a99cf98dd9ced60864c Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Fri, 9 Oct 2020 18:10:40 -0700 Subject: [PATCH 387/745] [NFC] Improve simple_display for AttributedImport --- include/swift/AST/Import.h | 22 ++++++++++++--- lib/AST/TypeCheckRequests.cpp | 51 +++++++++++------------------------ 2 files changed, 34 insertions(+), 39 deletions(-) diff --git a/include/swift/AST/Import.h b/include/swift/AST/Import.h index 1ee68596eb7f4..b6a1f7a2d352a 100644 --- a/include/swift/AST/Import.h +++ b/include/swift/AST/Import.h @@ -570,13 +570,27 @@ struct AttributedImport { } }; -/// A module which has been implicitly imported. void simple_display(llvm::raw_ostream &out, - const AttributedImport &import); + const ImportedModule &import); -/// A module which will be implicitly imported. void simple_display(llvm::raw_ostream &out, - const AttributedImport &import); + const UnloadedImportedModule &import); + +// This is a quasi-implementation detail of the template version below. +void simple_display(llvm::raw_ostream &out, + const AttributedImport> &import); + +template +void simple_display(llvm::raw_ostream &out, + const AttributedImport &import) { + // Print the module. + simple_display(out, import.module); + + // Print the other details of the import, using the std::tuple<> + // specialization. + AttributedImport> importWithoutModule({}, import); + simple_display(out, importWithoutModule); +} // MARK: - Implicit imports diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 5087a6d5a4501..fee1a29ea9d51 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1392,49 +1392,30 @@ TypeCheckFunctionBodyRequest::readDependencySource( //----------------------------------------------------------------------------// void swift::simple_display(llvm::raw_ostream &out, - const AttributedImport &import) { + const ImportedModule &module) { out << "import of "; - - simple_display(out, import.module.importedModule); - - out << " ["; - if (!import.module.accessPath.empty()) { - out << " scoped("; - import.module.accessPath.print(out); - out << ")"; + if (!module.accessPath.empty()) { + module.accessPath.print(out); + out << " in "; } - if (import.options.contains(ImportFlags::Exported)) - out << " exported"; - if (import.options.contains(ImportFlags::Testable)) - out << " testable"; - if (import.options.contains(ImportFlags::ImplementationOnly)) - out << " implementation-only"; - if (import.options.contains(ImportFlags::PrivateImport)) - out << " private(" << import.sourceFileArg << ")"; + simple_display(out, module.importedModule); +} - if (import.options.contains(ImportFlags::SPIAccessControl)) { - out << " spi("; - llvm::interleaveComma(import.spiGroups, out, [&out](Identifier name) { - simple_display(out, name); - }); - out << ")"; +void swift::simple_display(llvm::raw_ostream &out, + const UnloadedImportedModule &module) { + out << "import of "; + if (!module.getAccessPath().empty()) { + module.getAccessPath().print(out); + out << " in "; } - - out << " ]"; + out << "unloaded "; + module.getModulePath().print(out); } void swift::simple_display(llvm::raw_ostream &out, - const AttributedImport &import) { - out << "import of unloaded "; - - import.module.getModulePath().print(out); - + const AttributedImport> &import) { out << " ["; - if (!import.module.getAccessPath().empty()) { - out << " scoped("; - import.module.getAccessPath().print(out); - out << ")"; - } + if (import.options.contains(ImportFlags::Exported)) out << " exported"; if (import.options.contains(ImportFlags::Testable)) From 4481e3bf358ca06702900ae3b95f25b1e5c43afe Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 9 Oct 2020 23:51:24 -0400 Subject: [PATCH 388/745] Generalize SWIFT_RUNTIME_EXPORT to work for other runtime libraries. I intend to use this in the _Concurrency library. --- include/swift/Basic/LLVM.h | 24 ++++-- include/swift/Runtime/Metadata.h | 6 +- stdlib/public/SwiftShims/Visibility.h | 107 +++++++++++++++++++------- 3 files changed, 102 insertions(+), 35 deletions(-) diff --git a/include/swift/Basic/LLVM.h b/include/swift/Basic/LLVM.h index df5db366409dd..40ab0fe664509 100644 --- a/include/swift/Basic/LLVM.h +++ b/include/swift/Basic/LLVM.h @@ -26,6 +26,14 @@ // without a definition of NoneType. #include "llvm/ADT/None.h" +// Don't pre-declare certain LLVM types in the runtime, which must +// not put things in namespace llvm for ODR reasons. +#if !defined(swiftCore_EXPORTS) +#define SWIFT_LLVM_ODR_SAFE 1 +#else +#define SWIFT_LLVM_ODR_SAFE 0 +#endif + // Forward declarations. namespace llvm { // Containers. @@ -34,18 +42,18 @@ namespace llvm { class Twine; template class SmallPtrSetImpl; template class SmallPtrSet; -#if !defined(swiftCore_EXPORTS) +#if SWIFT_LLVM_ODR_SAFE template class SmallVectorImpl; template class SmallVector; #endif template class SmallString; template class SmallSetVector; -#if !defined(swiftCore_EXPORTS) +#if SWIFT_LLVM_ODR_SAFE template class ArrayRef; template class MutableArrayRef; #endif template class TinyPtrVector; -#if !defined(swiftCore_EXPORTS) +#if SWIFT_LLVM_ODR_SAFE template class Optional; #endif template class PointerUnion; @@ -56,7 +64,7 @@ namespace llvm { class raw_ostream; class APInt; class APFloat; -#if !defined(swiftCore_EXPORTS) +#if SWIFT_LLVM_ODR_SAFE template class function_ref; #endif } // end namespace llvm @@ -71,13 +79,13 @@ namespace swift { using llvm::cast_or_null; // Containers. -#if !defined(swiftCore_EXPORTS) +#if SWIFT_LLVM_ODR_SAFE using llvm::ArrayRef; using llvm::MutableArrayRef; #endif using llvm::iterator_range; using llvm::None; -#if !defined(swiftCore_EXPORTS) +#if SWIFT_LLVM_ODR_SAFE using llvm::Optional; #endif using llvm::PointerUnion; @@ -86,7 +94,7 @@ namespace swift { using llvm::SmallPtrSetImpl; using llvm::SmallSetVector; using llvm::SmallString; -#if !defined(swiftCore_EXPORTS) +#if SWIFT_LLVM_ODR_SAFE using llvm::SmallVector; using llvm::SmallVectorImpl; #endif @@ -98,7 +106,7 @@ namespace swift { // Other common classes. using llvm::APFloat; using llvm::APInt; -#if !defined(swiftCore_EXPORTS) +#if SWIFT_LLVM_ODR_SAFE using llvm::function_ref; #endif using llvm::NoneType; diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index 8d5537ed2e94b..8333cbf8457b5 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -878,7 +878,10 @@ const TypeContextDescriptor *swift_getTypeContextDescriptor(const Metadata *type SWIFT_RUNTIME_EXPORT const HeapObject *swift_getKeyPath(const void *pattern, const void *arguments); +// For some reason, MSVC doesn't accept these declarations outside of +// swiftCore. TODO: figure out a reasonable way to declare them. #if defined(swiftCore_EXPORTS) + /// Given a pointer to a borrowed value of type `Root` and a /// `KeyPath`, project a pointer to a borrowed value of type /// `Value`. @@ -900,7 +903,8 @@ swift_modifyAtWritableKeyPath; SWIFT_RUNTIME_EXPORT YieldOnceCoroutine::type swift_modifyAtReferenceWritableKeyPath; -#endif + +#endif // swiftCore_EXPORTS SWIFT_RUNTIME_EXPORT void swift_enableDynamicReplacementScope(const DynamicReplacementScope *scope); diff --git a/stdlib/public/SwiftShims/Visibility.h b/stdlib/public/SwiftShims/Visibility.h index 708e595975502..d98425af300a3 100644 --- a/stdlib/public/SwiftShims/Visibility.h +++ b/stdlib/public/SwiftShims/Visibility.h @@ -52,6 +52,12 @@ # define SWIFT_END_NULLABILITY_ANNOTATIONS #endif +#define SWIFT_MACRO_CONCAT(A, B) A ## B +#define SWIFT_MACRO_IF_0(IF_TRUE, IF_FALSE) IF_FALSE +#define SWIFT_MACRO_IF_1(IF_TRUE, IF_FALSE) IF_TRUE +#define SWIFT_MACRO_IF(COND, IF_TRUE, IF_FALSE) \ + SWIFT_MACRO_CONCAT(SWIFT_MACRO_IF_, COND)(IF_TRUE, IF_FALSE) + #if __has_attribute(pure) #define SWIFT_READONLY __attribute__((__pure__)) #else @@ -94,48 +100,97 @@ #define SWIFT_ATTRIBUTE_UNAVAILABLE #endif -// TODO: support using shims headers in overlays by parameterizing -// SWIFT_RUNTIME_EXPORT on the library it's exported from. - -/// Attribute used to export symbols from the runtime. +// Define the appropriate attributes for sharing symbols across +// image (executable / shared-library) boundaries. +// +// SWIFT_ATTRIBUTE_FOR_EXPORTS will be placed on declarations that +// are known to be exported from the current image. Typically, they +// are placed on header declarations and then inherited by the actual +// definitions. +// +// SWIFT_ATTRIBUTE_FOR_IMPORTS will be placed on declarations that +// are known to be exported from a different image. This never +// includes a definition. +// +// Getting the right attribute on a declaratioon can be pretty awkward, +// but it's necessary under the C translation model. All of this +// ceremony is familiar to Windows programmers; C/C++ programmers +// everywhere else usually don't bother, but since we have to get it +// right for Windows, we have everything set up to get it right on +// other targets as well, and doing so lets the compiler use more +// efficient symbol access patterns. #if defined(__MACH__) || defined(__wasi__) -# define SWIFT_EXPORT_ATTRIBUTE __attribute__((__visibility__("default"))) +// On Mach-O and WebAssembly, we use non-hidden visibility. We just use +// default visibility on both imports and exports, both because these +// targets don't support protected visibility but because they don't +// need it: symbols are not interposable outside the current image +// by default. +# define SWIFT_ATTRIBUTE_FOR_EXPORTS __attribute__((__visibility__("default"))) +# define SWIFT_ATTRIBUTE_FOR_IMPORTS __attribute__((__visibility__("default"))) #elif defined(__ELF__) -// We make assumptions that the runtime and standard library can refer to each -// other's symbols as DSO-local, which means we can't allow the dynamic linker -// to relocate these symbols. We must give them protected visibility while -// building the standard library and runtime. -# if defined(swiftCore_EXPORTS) -# define SWIFT_EXPORT_ATTRIBUTE __attribute__((__visibility__("protected"))) -# else -# define SWIFT_EXPORT_ATTRIBUTE __attribute__((__visibility__("default"))) -# endif +// On ELF, we use non-hidden visibility. For exports, we must use +// protected visibility to tell the compiler and linker that the symbols +// can't be interposed outside the current image. For imports, we must +// use default visibility because protected visibility guarantees that +// the symbol is defined in the current library, which isn't true for +// an import. +// +// The compiler does assume that the runtime and standard library can +// refer to each other's symbols as DSO-local, so it's important that +// we get this right or we can get linker errors. +# define SWIFT_ATTRIBUTE_FOR_EXPORTS __attribute__((__visibility__("protected"))) +# define SWIFT_ATTRIBUTE_FOR_IMPORTS __attribute__((__visibility__("default"))) + +#elif defined(__CYGWIN__) + +// For now, we ignore all this on Cygwin. +# define SWIFT_ATTRIBUTE_FOR_EXPORTS +# define SWIFT_ATTRIBUTE_FOR_IMPORTS // FIXME: this #else should be some sort of #elif Windows #else // !__MACH__ && !__ELF__ -# if defined(__CYGWIN__) -# define SWIFT_EXPORT_ATTRIBUTE -# else +// On PE/COFF, we use dllimport and dllexport. +# define SWIFT_ATTRIBUTE_FOR_EXPORTS __declspec(dllexport) +# define SWIFT_ATTRIBUTE_FOR_IMPORTS __declspec(dllimport) -# if defined(swiftCore_EXPORTS) -# define SWIFT_EXPORT_ATTRIBUTE __declspec(dllexport) -# else -# define SWIFT_EXPORT_ATTRIBUTE __declspec(dllimport) -# endif - -# endif +#endif +// CMake conventionally passes -DlibraryName_EXPORTS when building +// code that goes into libraryName. This isn't the best macro name, +// but it's conventional. We do have to pass it explicitly in a few +// places in the build system for a variety of reasons. +// +// Unfortunately, defined(D) is a special function you can use in +// preprocessor conditions, not a macro you can use anywhere, so we +// need to manually check for all the libraries we know about so that +// we can use them in our condition below.s +#if defined(swiftCore_EXPORTS) +#define SWIFT_IMAGE_EXPORTS_swiftCore 1 +#else +#define SWIFT_IMAGE_EXPORTS_swiftCore 0 #endif +#define SWIFT_EXPORT_FROM_ATTRIBUTE(LIBRARY) \ + SWIFT_MACRO_IF(SWIFT_IMAGE_EXPORTS_##LIBRARY, \ + SWIFT_ATTRIBUTE_FOR_EXPORTS, \ + SWIFT_ATTRIBUTE_FOR_IMPORTS) + +// SWIFT_EXPORT_FROM(LIBRARY) declares something to be a C-linkage +// entity exported by the given library. +// +// SWIFT_RUNTIME_EXPORT is just SWIFT_EXPORT_FROM(swiftCore). +// +// TODO: use this in shims headers in overlays. #if defined(__cplusplus) -#define SWIFT_RUNTIME_EXPORT extern "C" SWIFT_EXPORT_ATTRIBUTE +#define SWIFT_EXPORT_FROM(LIBRARY) extern "C" SWIFT_EXPORT_FROM_ATTRIBUTE(LIBRARY) #else -#define SWIFT_RUNTIME_EXPORT SWIFT_EXPORT_ATTRIBUTE +#define SWIFT_EXPORT_FROM(LIBRARY) SWIFT_EXPORT_FROM_ATTRIBUTE(LIBRARY) #endif +#define SWIFT_RUNTIME_EXPORT SWIFT_EXPORT_FROM(swiftCore) #if __cplusplus > 201402l && __has_cpp_attribute(fallthrough) #define SWIFT_FALLTHROUGH [[fallthrough]] From 2324aceec85904e20c5a5fd6ca097c87b854673e Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Sat, 10 Oct 2020 00:15:00 -0700 Subject: [PATCH 389/745] Update the Apple Watch Simulator to Series 5 --- test/lit.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lit.cfg b/test/lit.cfg index 53b3e05dbff63..d734663b61cbf 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -94,7 +94,7 @@ def get_simulator_command(run_os, run_cpu, sdk_path): elif run_os == 'tvos': return "simctl spawn --standalone 'Apple TV 4K'" elif run_os == 'watchos': - return "simctl spawn --standalone 'Apple Watch Series 4 - 44mm'" + return "simctl spawn --standalone 'Apple Watch Series 5 - 44mm'" else: lit_config.fatal("Unknown simulator OS %r" % run_os) From ec9f5a2311345ab82664512e1f52fdd8adca088d Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Wed, 16 Sep 2020 20:05:39 +0300 Subject: [PATCH 390/745] WinSDK: extract WinNT into a separate submodule --- stdlib/public/Platform/winsdk.modulemap | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/stdlib/public/Platform/winsdk.modulemap b/stdlib/public/Platform/winsdk.modulemap index 0eabe169bb150..068d1daf612a7 100644 --- a/stdlib/public/Platform/winsdk.modulemap +++ b/stdlib/public/Platform/winsdk.modulemap @@ -337,6 +337,11 @@ module WinSDK [system] { link "Gdi32.Lib" } + module WinNT { + header "winnt.h" + export * + } + module WinReg { header "winreg.h" export * From 2a7a3eb436c710ff891fbbf7cb33250edeb9c0bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Sat, 10 Oct 2020 13:34:24 -0700 Subject: [PATCH 391/745] [windows] Use extended length paths for os.walk invocation. In the Windows VS 2017 CI machines, the path for certain tests end up being very long. rewrite-module-triples.py was aware and was using extended length paths for the copy and remove operations in Windows but it was not using it for the os.walk invocation, so when a folder that was longer than MAX_PATH was encountered, the walk stopped drilling into it, missing some module-triple-here substitutions and making some test fail. The changes remove the special treatment of the copy and remove operations and move it into os.walk, which carries the prefix to every path. This should fix the problems seen in the SourceKit test in the Windows VS 2017 machine: - SourceKit/InterfaceGen/gen_swift_module_cross_import_common.swift - SourceKit/DocSupport/doc_cross_import_common.swift - SourceKit/CursorInfo/cursor_info_cross_import_common_case.swift PD: Misc/stats_dir_profiler.swift is something else. It seems as if an glob was not being expanded by lit for some reason. --- .../CrossImport/Inputs/rewrite-module-triples.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/test/CrossImport/Inputs/rewrite-module-triples.py b/test/CrossImport/Inputs/rewrite-module-triples.py index f76ef435e8466..2a6482b56ed29 100644 --- a/test/CrossImport/Inputs/rewrite-module-triples.py +++ b/test/CrossImport/Inputs/rewrite-module-triples.py @@ -17,6 +17,9 @@ sys.exit(1) root_dir = sys.argv[1] +if platform.system() == 'Windows': + root_dir = "\\\\?\\" + os.path.abspath(root_dir) + triples = sys.argv[2:] @@ -37,16 +40,9 @@ def rewrite(parent, names, copy_fn, rm_fn): for new_name in new_names: new_path = os.path.join(parent, new_name) - if platform.system() == 'Windows': - copy_fn(u'\\'.join([u'\\\\?', os.path.normpath(path)]), - u'\\'.join([u'\\\\?', os.path.normpath(new_path)])) - else: - copy_fn(path, new_path) - - if platform.system() == 'Windows': - rm_fn(u'\\'.join([u'\\\\?', os.path.normpath(path)])) - else: - rm_fn(path) + copy_fn(path, new_path) + + rm_fn(path) for parent, dirs, files in os.walk(root_dir, topdown=False): From 4cb337a41fbe5bda820a2fa6cdb1170b1a848529 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sat, 10 Oct 2020 10:47:34 -0700 Subject: [PATCH 392/745] [cxx-interop] Generate memberwise initializers for non-C++ types. Previously, when we checked for `!hasUserDeclaredConstructor` rather than `isAggregate`, we would always generate memberwise initializers for C record types. This updates the isAggregate to be true for non-C++ types so we will generate memberwise initializers for them as well. --- lib/ClangImporter/ImportDecl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 3e82783ef4b02..2e05ff0f8190e 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3452,7 +3452,7 @@ namespace { ctors.push_back(createDefaultConstructor(Impl, result)); } - bool isAggregate = cxxRecordDecl && cxxRecordDecl->isAggregate(); + bool isAggregate = !cxxRecordDecl || cxxRecordDecl->isAggregate(); if (hasReferenceableFields && hasMemberwiseInitializer && isAggregate) { // The default zero initializer suppresses the implicit value // constructor that would normally be formed, so we have to add that From df94c4f46414937856386d0e300f0348176197ba Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 9 Oct 2020 14:44:42 -0700 Subject: [PATCH 393/745] [CodeCompletion] Remove parser hacks regarding type body fingerprints Take type body fingerprints into account for inteface hash checking. Since `SourceFile.getInterfacehash()` doesn't digest the type body fingerprints in the file, enabling type body fingerprints regressed fast-completion. rdar://problem/69890297 --- lib/IDE/CompletionInstance.cpp | 53 ++++++++++++++++++++++++++++++++-- lib/Parse/ParseDecl.cpp | 10 +------ 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index eaad0b6499293..355713a970591 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -276,6 +276,51 @@ static bool areAnyDependentFilesInvalidated( }); } +/// Get interface hash of \p SF including the type members in the file. +/// +/// See if the inteface of the function and types visible from a function body +/// has changed since the last completion. If they haven't changed, completion +/// can reuse the existing AST of the source file. \c SF->getInterfaceHash() is +/// not enough because it doesn't take the interface of the type members into +/// account. For example: +/// +/// struct S { +/// func foo() {} +/// } +/// func main(val: S) { +/// val. +/// } +/// +/// In this case, we need to ensure that the interface of \c S hasn't changed. +/// Note that we don't care about local types (i.e. type declarations inside +/// function bodies, closures, or top level statement bodies) because they are +/// not visible from other functions where the completion is happening. +void getInterfaceHashIncludingTypeMembers(SourceFile *SF, + llvm::SmallString<32> &str) { + /// FIXME: Gross. Hashing multiple "hash" values. + llvm::MD5 hash; + SF->getInterfaceHash(str); + hash.update(str); + + std::function hashTypeBodyFingerprints = + [&](IterableDeclContext *IDC) { + if (auto fp = IDC->getBodyFingerprint()) + hash.update(*fp); + for (auto *member : IDC->getParsedMembers()) + if (auto *childIDC = dyn_cast(member)) + hashTypeBodyFingerprints(childIDC); + }; + + for (auto *D : SF->getTopLevelDecls()) { + if (auto IDC = dyn_cast(D)) + hashTypeBodyFingerprints(IDC); + } + + llvm::MD5::MD5Result result; + hash.final(result); + str = result.digest(); +} + } // namespace bool CompletionInstance::performCachedOperationIfPossible( @@ -355,8 +400,8 @@ bool CompletionInstance::performCachedOperationIfPossible( // If the interface has changed, AST must be refreshed. llvm::SmallString<32> oldInterfaceHash{}; llvm::SmallString<32> newInterfaceHash{}; - oldSF->getInterfaceHash(oldInterfaceHash); - tmpSF->getInterfaceHash(newInterfaceHash); + getInterfaceHashIncludingTypeMembers(oldSF, oldInterfaceHash); + getInterfaceHashIncludingTypeMembers(tmpSF, newInterfaceHash); if (oldInterfaceHash != newInterfaceHash) return false; @@ -406,6 +451,10 @@ bool CompletionInstance::performCachedOperationIfPossible( Scope Top(SI, ScopeKind::TopLevel); Scope Body(SI, ScopeKind::FunctionBody); + assert(oldInfo.Kind == CodeCompletionDelayedDeclKind::FunctionBody && + "If the interface hash is the same as old one, the previous kind " + "must be FunctionBody too. Otherwise, hashing is too weak"); + oldInfo.Kind = CodeCompletionDelayedDeclKind::FunctionBody; oldInfo.ParentContext = DC; oldInfo.StartOffset = newInfo.StartOffset; oldInfo.EndOffset = newInfo.EndOffset; diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index b8d19723dc113..e8d8fb04d4aae 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -4672,13 +4672,8 @@ Parser::parseDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, Diag<> ErrorDiag, // If we're hashing the type body separately, record the curly braces but // nothing inside for the interface hash. - // - // FIXME: There's no real reason code completion cannot also use this code - // path. But it seems to cause lazy parsing in contexts that the current - // implementation does not expect. Optional>> MemberHashingScope; - if (IDC->areTokensHashedForThisBodyInsteadOfInterfaceHash() && - !L->isCodeCompletion()) { + if (IDC->areTokensHashedForThisBodyInsteadOfInterfaceHash()) { recordTokenHash("{"); recordTokenHash("}"); MemberHashingScope.emplace(CurrentTokenHash, llvm::MD5()); @@ -4713,9 +4708,6 @@ Parser::parseDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, Diag<> ErrorDiag, if (RBLoc.isInvalid()) hadError = true; - if (L->isCodeCompletion()) - return std::make_pair(decls, None); - llvm::MD5::MD5Result result; auto declListHash = MemberHashingScope ? *CurrentTokenHash : llvm::MD5(); declListHash.final(result); From 7bc5c5ecb12a81d5bd7bce9fadb7a8b1e142a55f Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 9 Oct 2020 15:08:08 -0400 Subject: [PATCH 394/745] SIL: Fix ownership verifier to handle some missing interior pointer projections My upcoming SILGen change introduces more stores directly into the result of a ref_element_addr or struct_element_addr in some cases. Make sure we handle the failing combinations flagged by the ownership verifier. --- lib/SIL/Utils/OwnershipUtils.cpp | 8 ++-- lib/SIL/Verifier/SILOwnershipVerifier.cpp | 2 +- .../ownership-verifier/interior_pointer.sil | 47 +++++++++++++++++++ 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/lib/SIL/Utils/OwnershipUtils.cpp b/lib/SIL/Utils/OwnershipUtils.cpp index 7cd18ae2376e1..3e6457c77d98a 100644 --- a/lib/SIL/Utils/OwnershipUtils.cpp +++ b/lib/SIL/Utils/OwnershipUtils.cpp @@ -543,7 +543,7 @@ bool InteriorPointerOperand::getImplicitUses( isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || - isa(user)) { + isa(user) || isa(user)) { continue; } @@ -551,8 +551,10 @@ bool InteriorPointerOperand::getImplicitUses( if (Projection::isAddressProjection(user) || isa(user) || isa(user) || - isa(user) || isa(user) || - isa(user) || isa(user)) { + isa(user) || + isa(user) || isa(user) || + isa(user) || isa(user) || + isa(user)) { for (SILValue r : user->getResults()) { llvm::copy(r->getUses(), std::back_inserter(worklist)); } diff --git a/lib/SIL/Verifier/SILOwnershipVerifier.cpp b/lib/SIL/Verifier/SILOwnershipVerifier.cpp index ef6c73efe433e..c738568f4ca15 100644 --- a/lib/SIL/Verifier/SILOwnershipVerifier.cpp +++ b/lib/SIL/Verifier/SILOwnershipVerifier.cpp @@ -391,7 +391,7 @@ bool SILValueOwnershipChecker::gatherUsers( llvm::errs() << "Could not recognize address user of interior " "pointer operand!\n" << "Interior Pointer Operand: " - << interiorPointerOperand->operand->getUser() + << *interiorPointerOperand->operand->getUser() << "Address User: " << *op->getUser(); }); }; diff --git a/test/SIL/ownership-verifier/interior_pointer.sil b/test/SIL/ownership-verifier/interior_pointer.sil index fcad0d144e2a5..4139920c5d4f0 100644 --- a/test/SIL/ownership-verifier/interior_pointer.sil +++ b/test/SIL/ownership-verifier/interior_pointer.sil @@ -1,6 +1,8 @@ // RUN: %target-sil-opt -sil-ownership-verifier-enable-testing -ownership-verifier-textual-error-dumper -enable-sil-verify-all=0 %s -o /dev/null 2>&1 | %FileCheck %s // REQUIRES: asserts +import Swift + sil_stage canonical class Klass {} @@ -40,3 +42,48 @@ bb0(%0 : @owned $KlassUser): %3 = load [copy] %2 : $*Klass return %3 : $Klass } + +class OptionalBox { + var t: T? +} + +// CHECK-NOT: Function: 'inject_enum_addr_test' +sil [ossa] @inject_enum_addr_test : $@convention(thin) (@owned OptionalBox) -> () { +bb0(%0 : @owned $OptionalBox): + %1 = begin_borrow %0 : $OptionalBox + %2 = ref_element_addr %1 : $OptionalBox, #OptionalBox.t + inject_enum_addr %2 : $*Optional, #Optional.none!enumelt + end_borrow %1 : $OptionalBox + destroy_value %0 : $OptionalBox + %3 = tuple () + return %3 : $() +} + +// CHECK-NOT: Function: 'init_enum_data_addr_test' +sil [ossa] @init_enum_data_addr_test : $@convention(thin) (@owned OptionalBox, @in_guaranteed T) -> () { +bb0(%0 : @owned $OptionalBox, %1 : $*T): + %2 = begin_borrow %0 : $OptionalBox + %3 = ref_element_addr %2 : $OptionalBox, #OptionalBox.t + %4 = init_enum_data_addr %3 : $*Optional, #Optional.some!enumelt + copy_addr %1 to [initialization] %4 : $*T + end_borrow %2 : $OptionalBox + destroy_value %0 : $OptionalBox + %5 = tuple () + return %5 : $() +} + +class Box { + var t: T +} + +// CHECK-NOT: Function: 'unconditional_cast_test' +sil [ossa] @unconditional_cast_test : $@convention(thin) (@owned Box, @in Int) -> () { +bb0(%0 : @owned $Box, %1 : $*Int): + %2 = begin_borrow %0 : $Box + %3 = ref_element_addr %2 : $Box, #Box.t + unconditional_checked_cast_addr Int in %1 : $*Int to T in %3 : $*T + end_borrow %2 : $Box + destroy_value %0 : $Box + %4 = tuple () + return %4 : $() +} \ No newline at end of file From 50963451b89785fcd392c9d287d3edd0385673a3 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sat, 10 Oct 2020 17:39:47 -0400 Subject: [PATCH 395/745] DI: Handle instructions that initialize multiple tuple elements DI had trouble with this pattern: %s = struct_element_addr ... %t0 = tuple_element_addr %s, 0 %t1 = tuple_element_addr %s, 1 %f = function_ref ... apply %f(%t0, %t1) This is because the NonLoadUses map only stored a single use of a tuple element per instruction, preventing instructions such as 'apply' from being able to use multiple tuple elements. In other cases where this comes up, such as with 'assign' and 'copy_addr', DI scalarizes the tuple operation by projecting each component, however clearly this can't be easily done with an 'apply'. Instead, we can get DI out of the business of scalarization, at least for instructions which are known to perform an unconditional initialization. We do this by changing the NonLoadUses map to store a vector of DIMemoryUse IDs instead of a single value. --- .../Mandatory/DefiniteInitialization.cpp | 58 ++++++++++--------- test/SILOptimizer/definite_init_tuple.sil | 27 +++++++++ 2 files changed, 58 insertions(+), 27 deletions(-) create mode 100644 test/SILOptimizer/definite_init_tuple.sil diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index 28135f2625196..07744108077c8 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -394,7 +394,7 @@ namespace { /// This is a map of uses that are not loads (i.e., they are Stores, /// InOutUses, and Escapes), to their entry in Uses. - llvm::SmallDenseMap NonLoadUses; + llvm::SmallDenseMap, 16> NonLoadUses; /// This is true when there is an ambiguous store, which may be an init or /// assign, depending on the CFG path. @@ -472,7 +472,7 @@ namespace { void handleSelfInitUse(unsigned UseID); - void updateInstructionForInitState(DIMemoryUse &Use); + void updateInstructionForInitState(unsigned UseID); void processUninitializedRelease(SILInstruction *Release, @@ -544,7 +544,7 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory, break; } - NonLoadUses[Use.Inst] = ui; + NonLoadUses[Use.Inst].push_back(ui); auto &BBInfo = getBlockInfo(Use.Inst->getParent()); BBInfo.HasNonLoadUse = true; @@ -562,9 +562,8 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory, getBlockInfo(bb).markStoreToSelf(); } - // If isn't really a use, but we account for the mark_uninitialized or + // It isn't really a use, but we account for the mark_uninitialized or // project_box as a use so we see it in our dataflow walks. - NonLoadUses[TheMemory.getUninitializedValue()] = ~0U; auto &MemBBInfo = getBlockInfo(TheMemory.getParentBlock()); MemBBInfo.HasNonLoadUse = true; @@ -826,7 +825,7 @@ void LifetimeChecker::doIt() { // postpone lowering of assignment instructions to avoid deleting // instructions that still appear in the Uses list. for (unsigned UseID : NeedsUpdateForInitState) - updateInstructionForInitState(Uses[UseID]); + updateInstructionForInitState(UseID); } void LifetimeChecker::handleLoadUse(const DIMemoryUse &Use) { @@ -1911,7 +1910,8 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { /// from being InitOrAssign to some concrete state, update it for that state. /// This includes rewriting them from assign instructions into their composite /// operations. -void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) { +void LifetimeChecker::updateInstructionForInitState(unsigned UseID) { + DIMemoryUse &Use = Uses[UseID]; SILInstruction *Inst = Use.Inst; IsInitialization_t InitKind; @@ -1945,10 +1945,11 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) { // If this is an assign, rewrite it based on whether it is an initialization // or not. if (auto *AI = dyn_cast(Inst)) { + // Remove this instruction from our data structures, since we will be // removing it. Use.Inst = nullptr; - NonLoadUses.erase(Inst); + llvm::erase_if(NonLoadUses[Inst], [&](unsigned id) { return id == UseID; }); if (TheMemory.isClassInitSelf() && Use.Kind == DIUseKind::SelfInit) { @@ -1966,7 +1967,7 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) { // Remove this instruction from our data structures, since we will be // removing it. Use.Inst = nullptr; - NonLoadUses.erase(Inst); + llvm::erase_if(NonLoadUses[Inst], [&](unsigned id) { return id == UseID; }); switch (Use.Kind) { case DIUseKind::Initialization: @@ -2819,17 +2820,17 @@ LifetimeChecker::getLivenessAtNonTupleInst(swift::SILInstruction *Inst, --BBI; SILInstruction *TheInst = &*BBI; - // If this instruction is unrelated to the memory, ignore it. - if (!NonLoadUses.count(TheInst)) - continue; + if (TheInst == TheMemory.getUninitializedValue()) { + Result.set(0, DIKind::No); + return Result; + } - // If we found the allocation itself, then we are loading something that - // is not defined at all yet. Otherwise, we've found a definition, or - // something else that will require that the memory is initialized at - // this point. - Result.set(0, TheInst == TheMemory.getUninitializedValue() ? DIKind::No - : DIKind::Yes); - return Result; + if (NonLoadUses.count(TheInst)) { + // We've found a definition, or something else that will require that + // the memory is initialized at this point. + Result.set(0, DIKind::Yes); + return Result; + } } } @@ -2882,11 +2883,6 @@ AvailabilitySet LifetimeChecker::getLivenessAtInst(SILInstruction *Inst, --BBI; SILInstruction *TheInst = &*BBI; - // If this instruction is unrelated to the memory, ignore it. - auto It = NonLoadUses.find(TheInst); - if (It == NonLoadUses.end()) - continue; - // If we found the allocation itself, then we are loading something that // is not defined at all yet. Scan no further. if (TheInst == TheMemory.getUninitializedValue()) { @@ -2896,11 +2892,19 @@ AvailabilitySet LifetimeChecker::getLivenessAtInst(SILInstruction *Inst, return Result; } + // If this instruction is unrelated to the memory, ignore it. + auto It = NonLoadUses.find(TheInst); + if (It == NonLoadUses.end()) + continue; + // Check to see which tuple elements this instruction defines. Clear them // from the set we're scanning from. - auto &TheInstUse = Uses[It->second]; - NeededElements.reset(TheInstUse.FirstElement, - TheInstUse.FirstElement+TheInstUse.NumElements); + for (unsigned TheUse : It->second) { + auto &TheInstUse = Uses[TheUse]; + NeededElements.reset(TheInstUse.FirstElement, + TheInstUse.FirstElement+TheInstUse.NumElements); + } + // If that satisfied all of the elements we're looking for, then we're // done. Otherwise, keep going. if (NeededElements.none()) { diff --git a/test/SILOptimizer/definite_init_tuple.sil b/test/SILOptimizer/definite_init_tuple.sil new file mode 100644 index 0000000000000..fe0df54d6db75 --- /dev/null +++ b/test/SILOptimizer/definite_init_tuple.sil @@ -0,0 +1,27 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -definite-init -raw-sil-inst-lowering + +import Builtin +import Swift +import SwiftShims + +struct S { + var x: (T, T) +} + +sil [ossa] @$s19definite_init_tuple1SV1xx_xtvpfi : $@convention(thin) () -> (@out T, @out T) + +sil [ossa] @$s19definite_init_tuple1SVACyxGycfC : $@convention(method) (@thin S.Type) -> @out S { +bb0(%0 : $*S, %1 : $@thin S.Type): + %2 = alloc_box $<τ_0_0> { var S<τ_0_0> } , var, name "self" + %3 = mark_uninitialized [rootself] %2 : $<τ_0_0> { var S<τ_0_0> } + %4 = project_box %3 : $<τ_0_0> { var S<τ_0_0> } , 0 + %5 = struct_element_addr %4 : $*S, #S.x + %6 = function_ref @$s19definite_init_tuple1SV1xx_xtvpfi : $@convention(thin) <τ_0_0> () -> (@out τ_0_0, @out τ_0_0) + %7 = tuple_element_addr %5 : $*(T, T), 0 + %8 = tuple_element_addr %5 : $*(T, T), 1 + %9 = apply %6(%7, %8) : $@convention(thin) <τ_0_0> () -> (@out τ_0_0, @out τ_0_0) + copy_addr %4 to [initialization] %0 : $*S + destroy_value %3 : $<τ_0_0> { var S<τ_0_0> } + %12 = tuple () + return %12 : $() +} \ No newline at end of file From 53b91da82f0c58b3c4dc65696055caf68977fc68 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sat, 10 Oct 2020 17:54:39 -0400 Subject: [PATCH 396/745] DI: Remove scalarization 'pass' All the tests seem to pass without it now. --- .../Mandatory/DIMemoryUseCollector.cpp | 148 +----------- .../definite_init_scalarization_test.sil | 222 ------------------ 2 files changed, 1 insertion(+), 369 deletions(-) delete mode 100644 test/SILOptimizer/definite_init_scalarization_test.sil diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 789ed908d3c35..45cda4cee9f0f 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -466,48 +466,6 @@ void DIElementUseInfo::trackStoreToSelf(SILInstruction *I) { StoresToSelf.push_back(I); } -//===----------------------------------------------------------------------===// -// Scalarization Logic -//===----------------------------------------------------------------------===// - -/// Given a pointer to a tuple type, compute the addresses of each element and -/// add them to the ElementAddrs vector. -static void -getScalarizedElementAddresses(SILValue Pointer, SILBuilder &B, SILLocation Loc, - SmallVectorImpl &ElementAddrs) { - TupleType *TT = Pointer->getType().castTo(); - for (auto Index : indices(TT->getElements())) { - ElementAddrs.push_back(B.createTupleElementAddr(Loc, Pointer, Index)); - } -} - -/// Given an RValue of aggregate type, compute the values of the elements by -/// emitting a destructure. -static void getScalarizedElements(SILValue V, - SmallVectorImpl &ElementVals, - SILLocation Loc, SILBuilder &B) { - auto *DTI = B.createDestructureTuple(Loc, V); - llvm::copy(DTI->getResults(), std::back_inserter(ElementVals)); -} - -/// Scalarize a load down to its subelements. If NewLoads is specified, this -/// can return the newly generated sub-element loads. -static SILValue scalarizeLoad(LoadInst *LI, - SmallVectorImpl &ElementAddrs) { - SILBuilderWithScope B(LI); - SmallVector ElementTmps; - - for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) { - auto *SubLI = B.createTrivialLoadOr(LI->getLoc(), ElementAddrs[i], - LI->getOwnershipQualifier()); - ElementTmps.push_back(SubLI); - } - - if (LI->getType().is()) - return B.createTuple(LI->getLoc(), LI->getType(), ElementTmps); - return B.createStruct(LI->getLoc(), LI->getType(), ElementTmps); -} - //===----------------------------------------------------------------------===// // ElementUseCollector Implementation //===----------------------------------------------------------------------===// @@ -671,11 +629,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { "Walked through the pointer to the value?"); SILType PointeeType = Pointer->getType().getObjectType(); - // This keeps track of instructions in the use list that touch multiple tuple - // elements and should be scalarized. This is done as a second phase to - // avoid invalidating the use iterator. - SmallVector UsesToScalarize; - for (auto *Op : Pointer->getUses()) { auto *User = Op->getUser(); @@ -705,10 +658,7 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { // Loads are a use of the value. if (isa(User)) { - if (PointeeType.is()) - UsesToScalarize.push_back(User); - else - addElementUses(BaseEltNo, PointeeType, User, DIUseKind::Load); + addElementUses(BaseEltNo, PointeeType, User, DIUseKind::Load); continue; } @@ -730,13 +680,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { if ((isa(User) || isa(User) || isa(User)) && Op->getOperandNumber() == 1) { - if (PointeeType.is()) { - assert(!isa(User) && - "cannot assign a typle with assign_by_wrapper"); - UsesToScalarize.push_back(User); - continue; - } - // Coming out of SILGen, we assume that raw stores are initializations, // unless they have trivial type (which we classify as InitOrAssign). DIUseKind Kind; @@ -769,13 +712,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { #include "swift/AST/ReferenceStorage.def" if (auto *CAI = dyn_cast(User)) { - // If this is a copy of a tuple, we should scalarize it so that we don't - // have an access that crosses elements. - if (PointeeType.is()) { - UsesToScalarize.push_back(CAI); - continue; - } - // If this is the source of the copy_addr, then this is a load. If it is // the destination, then this is an unknown assignment. Note that we'll // revisit this instruction and add it to Uses twice if it is both a load @@ -988,88 +924,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { // Otherwise, the use is something complicated, it escapes. addElementUses(BaseEltNo, PointeeType, User, DIUseKind::Escape); } - - // Now that we've walked all of the immediate uses, scalarize any operations - // working on tuples if we need to for canonicalization or analysis reasons. - if (!UsesToScalarize.empty()) { - SILInstruction *PointerInst = Pointer->getDefiningInstruction(); - SmallVector ElementAddrs; - SILBuilderWithScope AddrBuilder(++SILBasicBlock::iterator(PointerInst), - PointerInst); - getScalarizedElementAddresses(Pointer, AddrBuilder, PointerInst->getLoc(), - ElementAddrs); - - SmallVector ElementTmps; - for (auto *User : UsesToScalarize) { - ElementTmps.clear(); - - LLVM_DEBUG(llvm::errs() << " *** Scalarizing: " << *User << "\n"); - - // Scalarize LoadInst - if (auto *LI = dyn_cast(User)) { - SILValue Result = scalarizeLoad(LI, ElementAddrs); - LI->replaceAllUsesWith(Result); - LI->eraseFromParent(); - continue; - } - - // Scalarize AssignInst - if (auto *AI = dyn_cast(User)) { - SILBuilderWithScope B(User, AI); - getScalarizedElements(AI->getOperand(0), ElementTmps, AI->getLoc(), B); - - for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) - B.createAssign(AI->getLoc(), ElementTmps[i], ElementAddrs[i], - AssignOwnershipQualifier::Unknown); - AI->eraseFromParent(); - continue; - } - - // Scalarize StoreInst - if (auto *SI = dyn_cast(User)) { - SILBuilderWithScope B(User, SI); - getScalarizedElements(SI->getOperand(0), ElementTmps, SI->getLoc(), B); - - for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) - B.createTrivialStoreOr(SI->getLoc(), ElementTmps[i], ElementAddrs[i], - SI->getOwnershipQualifier()); - SI->eraseFromParent(); - continue; - } - - // Scalarize CopyAddrInst. - auto *CAI = cast(User); - SILBuilderWithScope B(User, CAI); - - // Determine if this is a copy *from* or *to* "Pointer". - if (CAI->getSrc() == Pointer) { - // Copy from pointer. - getScalarizedElementAddresses(CAI->getDest(), B, CAI->getLoc(), - ElementTmps); - for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) - B.createCopyAddr(CAI->getLoc(), ElementAddrs[i], ElementTmps[i], - CAI->isTakeOfSrc(), CAI->isInitializationOfDest()); - - } else { - getScalarizedElementAddresses(CAI->getSrc(), B, CAI->getLoc(), - ElementTmps); - for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) - B.createCopyAddr(CAI->getLoc(), ElementTmps[i], ElementAddrs[i], - CAI->isTakeOfSrc(), CAI->isInitializationOfDest()); - } - CAI->eraseFromParent(); - } - - // Now that we've scalarized some stuff, recurse down into the newly created - // element address computations to recursively process it. This can cause - // further scalarization. - for (SILValue EltPtr : ElementAddrs) { - if (auto *TEAI = dyn_cast(EltPtr)) { - collectTupleElementUses(TEAI, BaseEltNo); - continue; - } - } - } } /// collectClassSelfUses - Collect all the uses of a 'self' pointer in a class diff --git a/test/SILOptimizer/definite_init_scalarization_test.sil b/test/SILOptimizer/definite_init_scalarization_test.sil deleted file mode 100644 index a2f4a88d2367d..0000000000000 --- a/test/SILOptimizer/definite_init_scalarization_test.sil +++ /dev/null @@ -1,222 +0,0 @@ -// RUN: %target-sil-opt -enable-sil-verify-all %s -definite-init -raw-sil-inst-lowering | %FileCheck %s -// -// Make sure that we properly scalarize tuples. -// -// TODO: This should be split out into its own pass /before/ DI rather than -// running during DI. Even once that happens, it is important that we update -// this test so that the test is in its post scalarization state so we can -// verify that DI can properly look through destructures. - -sil_stage raw - -import Builtin - -struct Int { - var _value: Builtin.Int64 -} - -struct TripleInt { - var a, b, c: Int -} - -struct ObjectValue { - var a : Builtin.NativeObject -} - -// CHECK-LABEL: sil [ossa] @test_store_trivial : $@convention(thin) (TripleInt, TripleInt, TripleInt) -> () { -// CHECK: bb0([[ARG0:%.*]] : $TripleInt, [[ARG1:%.*]] : $TripleInt, [[ARG2:%.*]] : $TripleInt): -// CHECK: [[BOX:%.*]] = alloc_box -// CHECK: [[PB_BOX:%.*]] = project_box [[BOX]] -// CHECK: [[ELT0:%.*]] = tuple_element_addr [[PB_BOX]] : $*(TripleInt, (TripleInt, TripleInt)), 0 -// CHECK: [[ELT1:%.*]] = tuple_element_addr [[PB_BOX]] : $*(TripleInt, (TripleInt, TripleInt)), 1 -// CHECK: [[ELT10:%.*]] = tuple_element_addr [[ELT1]] : $*(TripleInt, TripleInt), 0 -// CHECK: [[ELT11:%.*]] = tuple_element_addr [[ELT1]] : $*(TripleInt, TripleInt), 1 -// CHECK: [[RHS_TUP:%.*]] = tuple ([[ARG0]] : ${{.*}}, [[ARG1:%.*]] : ${{.*}}) -// CHECK: [[TUP:%.*]] = tuple ([[ARG2:%.*]] : ${{.*}}, [[RHS_TUP]] : ${{.*}}) -// CHECK: ([[DESTRUCTURE_TUP_LHS:%.*]], [[DESTRUCTURE_TUP_RHS:%.*]]) = destructure_tuple [[TUP]] : $(TripleInt, (TripleInt, TripleInt)) -// CHECK: store [[DESTRUCTURE_TUP_LHS]] to [trivial] [[ELT0]] -// CHECK: ([[DESTRUCTURE_TUP_RHS_LHS:%.*]], [[DESTRUCTURE_TUP_RHS_RHS:%.*]]) = destructure_tuple [[DESTRUCTURE_TUP_RHS]] : $(TripleInt, TripleInt) -// CHECK: store [[DESTRUCTURE_TUP_RHS_LHS]] to [trivial] [[ELT10]] -// CHECK: store [[DESTRUCTURE_TUP_RHS_RHS]] to [trivial] [[ELT11]] -// CHECK: destroy_value [[BOX]] -// CHECK: } // end sil function 'test_store_trivial' -sil [ossa] @test_store_trivial : $@convention(thin) (TripleInt, TripleInt, TripleInt) -> () { -bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): - %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 - %5 = tuple(%0 : $TripleInt, %1 : $TripleInt) - %6 = tuple(%1a : $TripleInt, %5 : $(TripleInt, TripleInt)) - store %6 to [trivial] %4 : $*(TripleInt, (TripleInt, TripleInt)) - destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [ossa] @test_store_owned : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { -// CHECK: bb0([[ARG0:%.*]] : @owned $Builtin.NativeObject, [[ARG1:%.*]] : @owned $Builtin.NativeObject, [[ARG2:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[BOX:%.*]] = alloc_box -// CHECK: [[PB_BOX:%.*]] = project_box [[BOX]] -// CHECK: [[ELT0:%.*]] = tuple_element_addr [[PB_BOX]] : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)), 0 -// CHECK: [[ELT1:%.*]] = tuple_element_addr [[PB_BOX]] : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)), 1 -// CHECK: [[ELT10:%.*]] = tuple_element_addr [[ELT1]] : $*(Builtin.NativeObject, Builtin.NativeObject), 0 -// CHECK: [[ELT11:%.*]] = tuple_element_addr [[ELT1]] : $*(Builtin.NativeObject, Builtin.NativeObject), 1 -// CHECK: [[RHS_TUP:%.*]] = tuple ([[ARG0]] : ${{.*}}, [[ARG1:%.*]] : ${{.*}}) -// CHECK: [[TUP:%.*]] = tuple ([[ARG2:%.*]] : ${{.*}}, [[RHS_TUP]] : ${{.*}}) -// CHECK: ([[DESTRUCTURE_TUP_LHS:%.*]], [[DESTRUCTURE_TUP_RHS:%.*]]) = destructure_tuple [[TUP]] : $(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) -// CHECK: store [[DESTRUCTURE_TUP_LHS]] to [init] [[ELT0]] -// CHECK: ([[DESTRUCTURE_TUP_RHS_LHS:%.*]], [[DESTRUCTURE_TUP_RHS_RHS:%.*]]) = destructure_tuple [[DESTRUCTURE_TUP_RHS]] : $(Builtin.NativeObject, Builtin.NativeObject) -// CHECK: store [[DESTRUCTURE_TUP_RHS_LHS]] to [init] [[ELT10]] -// CHECK: store [[DESTRUCTURE_TUP_RHS_RHS]] to [init] [[ELT11]] -// CHECK: destroy_value [[BOX]] -// CHECK: } // end sil function 'test_store_owned' -sil [ossa] @test_store_owned : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { -bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : @owned $Builtin.NativeObject): - %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 - %5 = tuple(%0 : $Builtin.NativeObject, %1 : $Builtin.NativeObject) - %6 = tuple(%1a : $Builtin.NativeObject, %5 : $(Builtin.NativeObject, Builtin.NativeObject)) - store %6 to [init] %4 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) - destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [ossa] @test_assign_trivial : $@convention(thin) (TripleInt, TripleInt, TripleInt) -> () { -// CHECK: bb0([[ARG0:%.*]] : $TripleInt, [[ARG1:%.*]] : $TripleInt, [[ARG2:%.*]] : $TripleInt): -// CHECK: [[BOX:%.*]] = alloc_box -// CHECK: [[PB_BOX:%.*]] = project_box [[BOX]] -// CHECK: [[ELT0:%.*]] = tuple_element_addr [[PB_BOX]] : $*(TripleInt, (TripleInt, TripleInt)), 0 -// CHECK: [[ELT1:%.*]] = tuple_element_addr [[PB_BOX]] : $*(TripleInt, (TripleInt, TripleInt)), 1 -// CHECK: [[ELT10:%.*]] = tuple_element_addr [[ELT1]] : $*(TripleInt, TripleInt), 0 -// CHECK: [[ELT11:%.*]] = tuple_element_addr [[ELT1]] : $*(TripleInt, TripleInt), 1 -// CHECK: [[RHS_TUP:%.*]] = tuple ([[ARG0]] : ${{.*}}, [[ARG1:%.*]] : ${{.*}}) -// CHECK: [[TUP:%.*]] = tuple ([[ARG2:%.*]] : ${{.*}}, [[RHS_TUP]] : ${{.*}}) -// CHECK: ([[DESTRUCTURE_TUP_LHS:%.*]], [[DESTRUCTURE_TUP_RHS:%.*]]) = destructure_tuple [[TUP]] : $(TripleInt, (TripleInt, TripleInt)) -// CHECK: store [[DESTRUCTURE_TUP_LHS]] to [trivial] [[ELT0]] -// CHECK: ([[DESTRUCTURE_TUP_RHS_LHS:%.*]], [[DESTRUCTURE_TUP_RHS_RHS:%.*]]) = destructure_tuple [[DESTRUCTURE_TUP_RHS]] : $(TripleInt, TripleInt) -// CHECK: store [[DESTRUCTURE_TUP_RHS_LHS]] to [trivial] [[ELT10]] -// CHECK: store [[DESTRUCTURE_TUP_RHS_RHS]] to [trivial] [[ELT11]] -// CHECK: destroy_value [[BOX]] -// CHECK: } // end sil function 'test_assign_trivial' -sil [ossa] @test_assign_trivial : $@convention(thin) (TripleInt, TripleInt, TripleInt) -> () { -bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): - %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 - %5 = tuple(%0 : $TripleInt, %1 : $TripleInt) - %6 = tuple(%1a : $TripleInt, %5 : $(TripleInt, TripleInt)) - assign %6 to %4 : $*(TripleInt, (TripleInt, TripleInt)) - destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [ossa] @test_assign_trivial_2 : $@convention(thin) (TripleInt, TripleInt, TripleInt) -> () { -// CHECK: bb0([[ARG0:%.*]] : $TripleInt, [[ARG1:%.*]] : $TripleInt, [[ARG2:%.*]] : $TripleInt): -// CHECK: [[BOX:%.*]] = alloc_box $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> -// CHECK: [[PROJ_BOX:%.*]] = project_box [[BOX]] : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 -// CHECK: [[PROJ_BOX_0:%.*]] = tuple_element_addr [[PROJ_BOX]] : $*(TripleInt, (TripleInt, TripleInt)), 0 -// CHECK: [[PROJ_BOX_1:%.*]] = tuple_element_addr [[PROJ_BOX]] : $*(TripleInt, (TripleInt, TripleInt)), 1 -// CHECK: [[PROJ_BOX_10:%.*]] = tuple_element_addr %6 : $*(TripleInt, TripleInt), 0 -// CHECK: [[PROJ_BOX_11:%.*]] = tuple_element_addr %6 : $*(TripleInt, TripleInt), 1 -// CHECK: [[TUP11:%.*]] = tuple ([[ARG0]] : $TripleInt, [[ARG1]] : $TripleInt) -// CHECK: [[TUP1:%.*]] = tuple ([[ARG2]] : $TripleInt, [[TUP11]] : $(TripleInt, TripleInt)) -// CHECK: [[TUP1_2nd:%.*]] = tuple ([[ARG2]] : $TripleInt, [[TUP11]] : $(TripleInt, TripleInt)) -// CHECK: ([[TUP1_D_0:%.*]], [[TUP1_D_1:%.*]]) = destructure_tuple [[TUP1]] : $(TripleInt, (TripleInt, TripleInt)) -// CHECK: store [[TUP1_D_0]] to [trivial] [[PROJ_BOX_0]] : $*TripleInt -// CHECK: ([[TUP1_D_1_0:%.*]], [[TUP1_D_1_1:%.*]]) = destructure_tuple [[TUP1_D_1]] : $(TripleInt, TripleInt) -// CHECK: store [[TUP1_D_1_0]] to [trivial] [[PROJ_BOX_10]] : $*TripleInt -// CHECK: store [[TUP1_D_1_1]] to [trivial] [[PROJ_BOX_11]] : $*TripleInt -// CHECK: ([[TUP1_2nd_0:%.*]], [[TUP1_2nd_1:%.*]]) = destructure_tuple [[TUP1_2nd]] : $(TripleInt, (TripleInt, TripleInt)) -// CHECK: store [[TUP1_2nd_0]] to [trivial] [[PROJ_BOX_0]] : $*TripleInt -// CHECK: ([[TUP1_2nd_10:%.*]], [[TUP1_2nd_11:%.*]]) = destructure_tuple [[TUP1_2nd_1]] : $(TripleInt, TripleInt) -// CHECK: store [[TUP1_2nd_10]] to [trivial] [[PROJ_BOX_10]] : $*TripleInt -// CHECK: store [[TUP1_2nd_11]] to [trivial] [[PROJ_BOX_11]] : $*TripleInt -// CHECK: destroy_value [[BOX]] : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> -// CHECK: } // end sil function 'test_assign_trivial_2' -sil [ossa] @test_assign_trivial_2 : $@convention(thin) (TripleInt, TripleInt, TripleInt) -> () { -bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): - %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 - %5 = tuple(%0 : $TripleInt, %1 : $TripleInt) - %6 = tuple(%1a : $TripleInt, %5 : $(TripleInt, TripleInt)) - %7 = tuple(%1a : $TripleInt, %5 : $(TripleInt, TripleInt)) - assign %6 to %4 : $*(TripleInt, (TripleInt, TripleInt)) - assign %7 to %4 : $*(TripleInt, (TripleInt, TripleInt)) - destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [ossa] @test_assign_owned : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { -// CHECK: bb0([[ARG0:%.*]] : @owned $Builtin.NativeObject, [[ARG1:%.*]] : @owned $Builtin.NativeObject, [[ARG2:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[BOX:%.*]] = alloc_box $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> -// CHECK: [[PROJ_BOX:%.*]] = project_box [[BOX]] : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 -// CHECK: [[PROJ_BOX_0:%.*]] = tuple_element_addr [[PROJ_BOX]] : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)), 0 -// CHECK: [[PROJ_BOX_1:%.*]] = tuple_element_addr [[PROJ_BOX]] : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)), 1 -// CHECK: [[PROJ_BOX_10:%.*]] = tuple_element_addr [[PROJ_BOX_1]] : $*(Builtin.NativeObject, Builtin.NativeObject), 0 -// CHECK: [[PROJ_BOX_11:%.*]] = tuple_element_addr [[PROJ_BOX_1]] : $*(Builtin.NativeObject, Builtin.NativeObject), 1 -// CHECK: [[TUP_1:%.*]] = tuple ([[ARG0]] : $Builtin.NativeObject, [[ARG1]] : $Builtin.NativeObject) -// CHECK: [[TUP:%.*]] = tuple ([[ARG2]] : $Builtin.NativeObject, [[TUP_1]] : $(Builtin.NativeObject, Builtin.NativeObject)) -// CHECK: ([[TUP_D_0:%.*]], [[TUP_D_1:%.*]]) = destructure_tuple [[TUP]] : $(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) -// CHECK: store [[TUP_D_0]] to [init] [[PROJ_BOX_0]] : $*Builtin.NativeObject -// CHECK: ([[TUP_D_10:%.*]], [[TUP_D_11:%.*]]) = destructure_tuple [[TUP_D_1]] : $(Builtin.NativeObject, Builtin.NativeObject) -// CHECK: store [[TUP_D_10]] to [init] [[PROJ_BOX_10]] : $*Builtin.NativeObject -// CHECK: store [[TUP_D_11]] to [init] [[PROJ_BOX_11]] : $*Builtin.NativeObject -// CHECK: destroy_value [[BOX]] : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> -// CHECK: } // end sil function 'test_assign_owned' -sil [ossa] @test_assign_owned : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { -bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : @owned $Builtin.NativeObject): - %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 - %5 = tuple(%0 : $Builtin.NativeObject, %1 : $Builtin.NativeObject) - %6 = tuple(%1a : $Builtin.NativeObject, %5 : $(Builtin.NativeObject, Builtin.NativeObject)) - assign %6 to %4 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) - destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [ossa] @test_assigned_owned_2 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { -// CHECK: bb0([[ARG0:%.*]] : @owned $Builtin.NativeObject, [[ARG1:%.*]] : @owned $Builtin.NativeObject, [[ARG2:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[BOX:%.*]] = alloc_box $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> -// CHECK: [[PROJ_BOX:%.*]] = project_box [[BOX]] : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 -// CHECK: [[PROJ_BOX_0:%.*]] = tuple_element_addr [[PROJ_BOX]] : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)), 0 -// CHECK: [[PROJ_BOX_1:%.*]] = tuple_element_addr [[PROJ_BOX]] : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)), 1 -// CHECK: [[PROJ_BOX_10:%.*]] = tuple_element_addr [[PROJ_BOX_1]] : $*(Builtin.NativeObject, Builtin.NativeObject), 0 -// CHECK: [[PROJ_BOX_11:%.*]] = tuple_element_addr [[PROJ_BOX_1]] : $*(Builtin.NativeObject, Builtin.NativeObject), 1 -// CHECK: [[TUP_1:%.*]] = tuple ([[ARG0]] : $Builtin.NativeObject, [[ARG1]] : $Builtin.NativeObject) -// CHECK: [[TUP:%.*]] = tuple ([[ARG2]] : $Builtin.NativeObject, [[TUP_1]] : $(Builtin.NativeObject, Builtin.NativeObject)) -// CHECK: [[TUP_COPY:%.*]] = copy_value [[TUP]] -// CHECK: ([[TUP_D_0:%.*]], [[TUP_D_1:%.*]]) = destructure_tuple [[TUP]] : $(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) -// CHECK: store [[TUP_D_0]] to [init] [[PROJ_BOX_0]] : $*Builtin.NativeObject -// CHECK: ([[TUP_D_10:%.*]], [[TUP_D_11:%.*]]) = destructure_tuple [[TUP_D_1]] : $(Builtin.NativeObject, Builtin.NativeObject) -// CHECK: store [[TUP_D_10]] to [init] [[PROJ_BOX_10]] : $*Builtin.NativeObject -// CHECK: store [[TUP_D_11]] to [init] [[PROJ_BOX_11]] : $*Builtin.NativeObject - -// CHECK: ([[TUP_D_0:%.*]], [[TUP_D_1:%.*]]) = destructure_tuple [[TUP_COPY]] : $(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) -// CHECK: store [[TUP_D_0]] to [assign] [[PROJ_BOX_0]] : $*Builtin.NativeObject -// CHECK: ([[TUP_D_10:%.*]], [[TUP_D_11:%.*]]) = destructure_tuple [[TUP_D_1]] : $(Builtin.NativeObject, Builtin.NativeObject) -// CHECK: store [[TUP_D_10]] to [assign] [[PROJ_BOX_10]] : $*Builtin.NativeObject -// CHECK: store [[TUP_D_11]] to [assign] [[PROJ_BOX_11]] : $*Builtin.NativeObject - -// CHECK: destroy_value [[BOX]] : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> -// CHECK: } // end sil function 'test_assigned_owned_2' -sil [ossa] @test_assigned_owned_2 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { -bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : @owned $Builtin.NativeObject): - %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 - %5 = tuple(%0 : $Builtin.NativeObject, %1 : $Builtin.NativeObject) - %6 = tuple(%1a : $Builtin.NativeObject, %5 : $(Builtin.NativeObject, Builtin.NativeObject)) - %7 = copy_value %6 : $(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) - assign %6 to %4 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) - assign %7 to %4 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) - destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %9999 = tuple() - return %9999 : $() -} From 681805ddca749f721454a9124d204e94a4c73936 Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Sun, 11 Oct 2020 16:57:37 +0300 Subject: [PATCH 397/745] WinSDK: extract Networking submodule Currently winnetwk.h gets included via windows.h --- stdlib/public/Platform/winsdk.modulemap | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stdlib/public/Platform/winsdk.modulemap b/stdlib/public/Platform/winsdk.modulemap index 068d1daf612a7..a44f8de118874 100644 --- a/stdlib/public/Platform/winsdk.modulemap +++ b/stdlib/public/Platform/winsdk.modulemap @@ -217,6 +217,13 @@ module WinSDK [system] { link "WinMM.Lib" } + module Networking { + header "winnetwk.h" + export * + + link "Mpr.Lib" + } + module Security { module AuthZ { header "AuthZ.h" From a2ca44b4a76c81d89f82d4e8def8441c1520ab26 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 9 Oct 2020 00:10:02 -0400 Subject: [PATCH 398/745] SILGen: Pass the correct abstraction pattern to emitApplyOfStoredPropertyInitializer() --- lib/SILGen/SILGenConstructor.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index d38ce95f27825..33eb95e803e03 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -966,7 +966,8 @@ static void emitMemberInit(SILGenFunction &SGF, VarDecl *selfDecl, } } -static Type getInitializationTypeInContext( +static std::pair +getInitializationTypeInContext( DeclContext *fromDC, DeclContext *toDC, Pattern *pattern) { auto interfaceType = pattern->getType()->mapTypeOutOfContext(); @@ -981,9 +982,13 @@ static Type getInitializationTypeInContext( } } - auto resultType = toDC->mapTypeIntoContext(interfaceType); + AbstractionPattern origType( + fromDC->getGenericSignatureOfContext().getCanonicalSignature(), + interfaceType->getCanonicalType()); - return resultType; + auto substType = toDC->mapTypeIntoContext(interfaceType)->getCanonicalType(); + + return std::make_pair(origType, substType); } void SILGenFunction::emitMemberInitializers(DeclContext *dc, @@ -1006,15 +1011,16 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, // Get the type of the initialization result, in terms // of the constructor context's archetypes. - CanType resultType = getInitializationTypeInContext( - pbd->getDeclContext(), dc, varPattern)->getCanonicalType(); - AbstractionPattern origResultType(resultType); + auto resultType = getInitializationTypeInContext( + pbd->getDeclContext(), dc, varPattern); + AbstractionPattern origType = resultType.first; + CanType substType = resultType.second; // FIXME: Can emitMemberInit() share code with // InitializationForPattern in SILGenDecl.cpp? RValue result = emitApplyOfStoredPropertyInitializer( init, pbd->getAnchoringVarDecl(i), subs, - resultType, origResultType, + substType, origType, SGFContext()); // If we have the backing storage for a property with an attached From 5d9e704854ca83fdae3a7d429bc2372bc056f6af Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 9 Oct 2020 01:41:41 -0400 Subject: [PATCH 399/745] SILGen: Move BlackHoleInitialization to Initialization.h --- lib/SILGen/Initialization.h | 33 +++++++++++++++++++++++++++++++++ lib/SILGen/SILGenDecl.cpp | 35 ----------------------------------- 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/lib/SILGen/Initialization.h b/lib/SILGen/Initialization.h index 60ddc9c893a3f..022eab9b5f1bf 100644 --- a/lib/SILGen/Initialization.h +++ b/lib/SILGen/Initialization.h @@ -307,6 +307,39 @@ class TupleInitialization : public Initialization { void finishUninitialized(SILGenFunction &SGF) override; }; +/// A "null" initialization that indicates that any value being initialized +/// into this initialization should be discarded. This represents AnyPatterns +/// (that is, 'var (_)') that bind to values without storing them. +class BlackHoleInitialization : public Initialization { +public: + BlackHoleInitialization() {} + + bool canSplitIntoTupleElements() const override { + return true; + } + + MutableArrayRef + splitIntoTupleElements(SILGenFunction &SGF, SILLocation loc, + CanType type, + SmallVectorImpl &buf) override { + // "Destructure" an ignored binding into multiple ignored bindings. + for (auto fieldType : cast(type)->getElementTypes()) { + (void) fieldType; + buf.push_back(InitializationPtr(new BlackHoleInitialization())); + } + return buf; + } + + void copyOrInitValueInto(SILGenFunction &SGF, SILLocation loc, + ManagedValue value, bool isInit) override { + /// This just ignores the provided value. + } + + void finishUninitialized(SILGenFunction &SGF) override { + // do nothing + } +}; + } // end namespace Lowering } // end namespace swift diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index 9e53c5babd0fd..57be614349c97 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -39,41 +39,6 @@ using namespace Lowering; void Initialization::_anchor() {} void SILDebuggerClient::anchor() {} -namespace { - /// A "null" initialization that indicates that any value being initialized - /// into this initialization should be discarded. This represents AnyPatterns - /// (that is, 'var (_)') that bind to values without storing them. - class BlackHoleInitialization : public Initialization { - public: - BlackHoleInitialization() {} - - bool canSplitIntoTupleElements() const override { - return true; - } - - MutableArrayRef - splitIntoTupleElements(SILGenFunction &SGF, SILLocation loc, - CanType type, - SmallVectorImpl &buf) override { - // "Destructure" an ignored binding into multiple ignored bindings. - for (auto fieldType : cast(type)->getElementTypes()) { - (void) fieldType; - buf.push_back(InitializationPtr(new BlackHoleInitialization())); - } - return buf; - } - - void copyOrInitValueInto(SILGenFunction &SGF, SILLocation loc, - ManagedValue value, bool isInit) override { - /// This just ignores the provided value. - } - - void finishUninitialized(SILGenFunction &SGF) override { - // do nothing - } - }; -} // end anonymous namespace - static void copyOrInitValueIntoHelper( SILGenFunction &SGF, SILLocation loc, ManagedValue value, bool isInit, ArrayRef subInitializations, From 47e32d1e0f9fcfc535d4111943251e18ac8090da Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 9 Oct 2020 01:51:45 -0400 Subject: [PATCH 400/745] SILGen: Peephole Conversion::OrigToSubst / Conversion::SubstToOrig --- lib/SILGen/SILGenConvert.cpp | 20 +++++++++++++++++++- lib/SILGen/SILGenDecl.cpp | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/SILGen/SILGenConvert.cpp b/lib/SILGen/SILGenConvert.cpp index 953c3ae518556..a75fdb01c9a3e 100644 --- a/lib/SILGen/SILGenConvert.cpp +++ b/lib/SILGen/SILGenConvert.cpp @@ -1354,7 +1354,25 @@ Lowering::canPeepholeConversions(SILGenFunction &SGF, switch (outerConversion.getKind()) { case Conversion::OrigToSubst: case Conversion::SubstToOrig: - // TODO: peephole these when the abstraction patterns are the same! + switch (innerConversion.getKind()) { + case Conversion::OrigToSubst: + case Conversion::SubstToOrig: + if (innerConversion.getKind() == outerConversion.getKind()) + break; + + if (innerConversion.getReabstractionOrigType().getCachingKey() != + outerConversion.getReabstractionOrigType().getCachingKey() || + innerConversion.getReabstractionSubstType() != + outerConversion.getReabstractionSubstType()) { + break; + } + + return ConversionPeepholeHint(ConversionPeepholeHint::Identity, false); + + default: + break; + } + return None; case Conversion::AnyErasure: diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index 57be614349c97..41de4a930cd88 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -1255,7 +1255,7 @@ void SILGenFunction::emitStmtCondition(StmtCondition Cond, JumpDest FalseDest, switch (elt.getKind()) { case StmtConditionElement::CK_PatternBinding: { InitializationPtr initialization = - InitializationForPattern(*this, FalseDest).visit(elt.getPattern()); + emitPatternBindingInitialization(elt.getPattern(), FalseDest); // Emit the initial value into the initialization. FullExpr Scope(Cleanups, CleanupLocation(elt.getInitializer())); From 85b94a4300c152276c3d3f3b360c1c1c1aa754d7 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 9 Oct 2020 13:57:53 -0400 Subject: [PATCH 401/745] SILGen: Refactor emitMemberInitializers() to use Initialization Instead of constructing an LValue and storing the result of the stored property initializer to the lvalue, let's emit the call of the stored property initializer directly into an initialization pointing at the stored properties named by the pattern. --- lib/SILGen/SILGenConstructor.cpp | 136 ++++++++++-------- test/IRGen/generic_structs.swift | 12 +- test/SILGen/constrained_extensions.swift | 2 +- test/SILGen/default_constructor.swift | 22 ++- test/SILGen/extensions.swift | 7 +- test/SILGen/initializers.swift | 16 +-- test/SILGen/lifetime.swift | 30 ++-- test/SILGen/objc_dealloc.swift | 8 +- test/SILGen/properties.swift | 4 +- .../stored_property_initial_value.swift | 29 ++++ 10 files changed, 148 insertions(+), 118 deletions(-) create mode 100644 test/SILOptimizer/stored_property_initial_value.swift diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 33eb95e803e03..8954a391e272e 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -901,68 +901,66 @@ static ManagedValue emitSelfForMemberInit(SILGenFunction &SGF, SILLocation loc, SGFAccessKind::Write); } -static LValue emitLValueForMemberInit(SILGenFunction &SGF, SILLocation loc, - VarDecl *selfDecl, - VarDecl *property) { - CanType selfFormalType = selfDecl->getType()->getCanonicalType(); - auto self = emitSelfForMemberInit(SGF, loc, selfDecl); - return SGF.emitPropertyLValue(loc, self, selfFormalType, property, - LValueOptions(), SGFAccessKind::Write, - AccessSemantics::DirectToStorage); -} - -/// Emit a member initialization for the members described in the -/// given pattern from the given source value. -static void emitMemberInit(SILGenFunction &SGF, VarDecl *selfDecl, - Pattern *pattern, RValue &&src) { +// FIXME: Can emitMemberInit() share code with InitializationForPattern in +// SILGenDecl.cpp? Note that this version operates on stored properties of +// types, whereas the former only knows how to handle local bindings, but +// we could generalize it. +static InitializationPtr +emitMemberInit(SILGenFunction &SGF, VarDecl *selfDecl, Pattern *pattern) { switch (pattern->getKind()) { case PatternKind::Paren: return emitMemberInit(SGF, selfDecl, - cast(pattern)->getSubPattern(), - std::move(src)); + cast(pattern)->getSubPattern()); case PatternKind::Tuple: { + TupleInitialization *init = new TupleInitialization(); auto tuple = cast(pattern); - auto fields = tuple->getElements(); - - SmallVector elements; - std::move(src).extractElements(elements); - for (unsigned i = 0, n = fields.size(); i != n; ++i) { - emitMemberInit(SGF, selfDecl, fields[i].getPattern(), - std::move(elements[i])); + for (auto &elt : tuple->getElements()) { + init->SubInitializations.push_back( + emitMemberInit(SGF, selfDecl, elt.getPattern())); } - break; + return InitializationPtr(init); } case PatternKind::Named: { auto named = cast(pattern); - // Form the lvalue referencing this member. - FormalEvaluationScope scope(SGF); - LValue memberRef = emitLValueForMemberInit(SGF, pattern, selfDecl, - named->getDecl()); - // Assign to it. - SGF.emitAssignToLValue(pattern, std::move(src), std::move(memberRef)); - return; + auto self = emitSelfForMemberInit(SGF, pattern, selfDecl); + + auto *field = named->getDecl(); + + auto selfTy = self.getType(); + auto fieldTy = + selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext()); + SILValue slot; + + if (auto *structDecl = dyn_cast(field->getDeclContext())) { + slot = SGF.B.createStructElementAddr(pattern, self.forward(SGF), field, + fieldTy.getAddressType()); + } else { + assert(isa(field->getDeclContext())); + slot = SGF.B.createRefElementAddr(pattern, self.forward(SGF), field, + fieldTy.getAddressType()); + } + + return InitializationPtr(new KnownAddressInitialization(slot)); } case PatternKind::Any: - return; + return InitializationPtr(new BlackHoleInitialization());; case PatternKind::Typed: return emitMemberInit(SGF, selfDecl, - cast(pattern)->getSubPattern(), - std::move(src)); + cast(pattern)->getSubPattern()); case PatternKind::Binding: return emitMemberInit(SGF, selfDecl, - cast(pattern)->getSubPattern(), - std::move(src)); + cast(pattern)->getSubPattern()); #define PATTERN(Name, Parent) #define REFUTABLE_PATTERN(Name, Parent) case PatternKind::Name: #include "swift/AST/PatternNodes.def" - llvm_unreachable("Refutable pattern in pattern binding"); + llvm_unreachable("Refutable pattern in stored property pattern binding"); } } @@ -991,6 +989,46 @@ getInitializationTypeInContext( return std::make_pair(origType, substType); } +static void +emitAndStoreInitialValueInto(SILGenFunction &SGF, + SILLocation loc, + PatternBindingDecl *pbd, unsigned i, + SubstitutionMap subs, + AbstractionPattern origType, + CanType substType, + Initialization *init) { + bool injectIntoWrapper = false; + if (auto singleVar = pbd->getSingleVar()) { + auto originalVar = singleVar->getOriginalWrappedProperty(); + if (originalVar && + originalVar->isPropertyMemberwiseInitializedWithWrappedType()) { + injectIntoWrapper = true; + } + } + + SGFContext C = (injectIntoWrapper ? SGFContext() : SGFContext(init)); + + RValue result = SGF.emitApplyOfStoredPropertyInitializer( + pbd->getExecutableInit(i), + pbd->getAnchoringVarDecl(i), + subs, substType, origType, C); + + // need to store result into the init if its in context + + // If we have the backing storage for a property with an attached + // property wrapper initialized with `=`, inject the value into an + // instance of the wrapper. + if (injectIntoWrapper) { + auto *singleVar = pbd->getSingleVar(); + result = maybeEmitPropertyWrapperInitFromValue( + SGF, pbd->getExecutableInit(i), + singleVar, subs, std::move(result)); + } + + if (!result.isInContext()) + std::move(result).forwardInto(SGF, loc, init); +} + void SILGenFunction::emitMemberInitializers(DeclContext *dc, VarDecl *selfDecl, NominalTypeDecl *nominal) { @@ -1006,6 +1044,7 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, if (!init) continue; auto *varPattern = pbd->getPattern(i); + // Cleanup after this initialization. FullExpr scope(Cleanups, varPattern); @@ -1016,26 +1055,11 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, AbstractionPattern origType = resultType.first; CanType substType = resultType.second; - // FIXME: Can emitMemberInit() share code with - // InitializationForPattern in SILGenDecl.cpp? - RValue result = emitApplyOfStoredPropertyInitializer( - init, pbd->getAnchoringVarDecl(i), subs, - substType, origType, - SGFContext()); - - // If we have the backing storage for a property with an attached - // property wrapper initialized with `=`, inject the value into an - // instance of the wrapper. - if (auto singleVar = pbd->getSingleVar()) { - auto originalVar = singleVar->getOriginalWrappedProperty(); - if (originalVar && - originalVar->isPropertyMemberwiseInitializedWithWrappedType()) { - result = maybeEmitPropertyWrapperInitFromValue( - *this, init, singleVar, subs, std::move(result)); - } - } + // Figure out what we're initializing. + auto memberInit = emitMemberInit(*this, selfDecl, varPattern); - emitMemberInit(*this, selfDecl, varPattern, std::move(result)); + emitAndStoreInitialValueInto(*this, varPattern, pbd, i, subs, + origType, substType, memberInit.get()); } } } diff --git a/test/IRGen/generic_structs.swift b/test/IRGen/generic_structs.swift index 1eaf654c42c19..b8439fad0bf98 100644 --- a/test/IRGen/generic_structs.swift +++ b/test/IRGen/generic_structs.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -disable-type-layout -primary-file %s -emit-ir | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize -DINT=i%target-ptrsize -DINT_32=i32 +// RUN: %target-swift-frontend -disable-type-layout -primary-file %s -emit-ir struct A { @@ -37,13 +37,3 @@ public struct GenericStruct { public init() {} } - -// CHECK-LABEL: define{{.*}} swiftcc void @"$s15generic_structs13GenericStructVACyxGycfC" -// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s15generic_structs13GenericStructVMa"([[INT]] 0, %swift.type* %T, i8** %T.Proto) -// CHECK: [[TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 -// CHECK: [[PTR:%.*]] = bitcast %swift.type* [[TYPE]] to [[INT_32]]* -// CHECK: [[FIELDOFFSET:%.*]] = getelementptr inbounds [[INT_32]], [[INT_32]]* [[PTR]], [[INT]] [[IDX:6|10]] -// CHECK: [[OFFSET:%.*]] = load [[INT_32]], [[INT_32]]* [[FIELDOFFSET]] -// CHECK: [[ADDROFOPT:%.*]] = getelementptr inbounds i8, i8* {{.*}}, [[INT_32]] [[OFFSET]] -// CHECK: [[OPTPTR:%.*]] = bitcast i8* [[ADDROFOPT]] to %TSq* -// CHECK: call %TSq* @"$sxSg15generic_structs5ProtoRzlWOb"(%TSq* {{.*}}, %TSq* [[OPTPTR]] diff --git a/test/SILGen/constrained_extensions.swift b/test/SILGen/constrained_extensions.swift index ecc0a6b4d7f0b..5ba8fdb40a577 100644 --- a/test/SILGen/constrained_extensions.swift +++ b/test/SILGen/constrained_extensions.swift @@ -177,8 +177,8 @@ struct AnythingGoes { extension AnythingGoes where T : VeryConstrained { // CHECK-LABEL: sil hidden [ossa] @$s22constrained_extensions12AnythingGoesVA2A15VeryConstrainedRzlE13fromExtensionACyxGyt_tcfC : $@convention(method) (@thin AnythingGoes.Type) -> @out AnythingGoes { + // CHECK: [[RESULT:%.*]] = struct_element_addr {{%.*}} : $*AnythingGoes, #AnythingGoes.meaningOfLife // CHECK: [[INIT:%.*]] = function_ref @$s22constrained_extensions12AnythingGoesV13meaningOfLifexSgvpfi : $@convention(thin) <τ_0_0> () -> @out Optional<τ_0_0> - // CHECK: [[RESULT:%.*]] = alloc_stack $Optional // CHECK: apply [[INIT]]([[RESULT]]) : $@convention(thin) <τ_0_0> () -> @out Optional<τ_0_0> // CHECK: return init(fromExtension: ()) {} diff --git a/test/SILGen/default_constructor.swift b/test/SILGen/default_constructor.swift index 7f2b1fb933556..2689d0dc2985c 100644 --- a/test/SILGen/default_constructor.swift +++ b/test/SILGen/default_constructor.swift @@ -31,13 +31,13 @@ struct D { // CHECK: [[THISBOX:%[0-9]+]] = alloc_box ${ var D } // CHECK: [[THIS:%[0-9]+]] = mark_uninit // CHECK: [[PB_THIS:%.*]] = project_box [[THIS]] +// CHECK: [[IADDR:%[0-9]+]] = struct_element_addr [[PB_THIS]] : $*D, #D.i +// CHECK: [[JADDR:%[0-9]+]] = struct_element_addr [[PB_THIS]] : $*D, #D.j // CHECK: [[INIT:%[0-9]+]] = function_ref @$s19default_constructor1DV1iSivpfi // CHECK: [[RESULT:%[0-9]+]] = apply [[INIT]]() // CHECK: ([[INTVAL:%[0-9]+]], [[FLOATVAL:%[0-9]+]]) = destructure_tuple [[RESULT]] -// CHECK: [[IADDR:%[0-9]+]] = struct_element_addr [[PB_THIS]] : $*D, #D.i -// CHECK: assign [[INTVAL]] to [[IADDR]] -// CHECK: [[JADDR:%[0-9]+]] = struct_element_addr [[PB_THIS]] : $*D, #D.j -// CHECK: assign [[FLOATVAL]] to [[JADDR]] +// CHECK: store [[INTVAL]] to [trivial] [[IADDR]] +// CHECK: store [[FLOATVAL]] to [trivial] [[JADDR]] class E { var i = Int64() @@ -55,13 +55,11 @@ class E { // CHECK-LABEL: sil hidden [ossa] @$s19default_constructor1EC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (@owned E) -> @owned E // CHECK: bb0([[SELFIN:%[0-9]+]] : @owned $E) // CHECK: [[SELF:%[0-9]+]] = mark_uninitialized -// CHECK: [[INIT:%[0-9]+]] = function_ref @$s19default_constructor1EC1is5Int64Vvpfi : $@convention(thin) () -> Int64 -// CHECK-NEXT: [[VALUE:%[0-9]+]] = apply [[INIT]]() : $@convention(thin) () -> Int64 // CHECK-NEXT: [[BORROWED_SELF:%.*]] = begin_borrow [[SELF]] // CHECK-NEXT: [[IREF:%[0-9]+]] = ref_element_addr [[BORROWED_SELF]] : $E, #E.i -// CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [dynamic] [[IREF]] : $*Int64 -// CHECK-NEXT: assign [[VALUE]] to [[WRITE]] : $*Int64 -// CHECK-NEXT: end_access [[WRITE]] : $*Int64 +// CHECK: [[INIT:%[0-9]+]] = function_ref @$s19default_constructor1EC1is5Int64Vvpfi : $@convention(thin) () -> Int64 +// CHECK-NEXT: [[VALUE:%[0-9]+]] = apply [[INIT]]() : $@convention(thin) () -> Int64 +// CHECK-NEXT: store [[VALUE]] to [trivial] [[IREF]] : $*Int64 // CHECK-NEXT: end_borrow [[BORROWED_SELF]] // CHECK-NEXT: [[SELF_COPY:%.*]] = copy_value [[SELF]] // CHECK-NEXT: destroy_value [[SELF]] @@ -103,8 +101,8 @@ struct H { var opt: T? // CHECK-LABEL: sil hidden [ossa] @$s19default_constructor1HVyACyxGqd__clufC : $@convention(method) (@in U, @thin H.Type) -> @out H { + // CHECK: [[OPT_T:%[0-9]+]] = struct_element_addr {{%.*}} : $*H, #H.opt // CHECK: [[INIT_FN:%[0-9]+]] = function_ref @$s19default_constructor1HV3optxSgvpfi : $@convention(thin) <τ_0_0> () -> @out Optional<τ_0_0> - // CHECK-NEXT: [[OPT_T:%[0-9]+]] = alloc_stack $Optional // CHECK-NEXT: apply [[INIT_FN]]([[OPT_T]]) : $@convention(thin) <τ_0_0> () -> @out Optional<τ_0_0> init(_: U) { } } @@ -115,10 +113,10 @@ struct I { var x: Int = 0 // CHECK-LABEL: sil hidden [ossa] @$s19default_constructor1IVyACxclufC : $@convention(method) (@in T, @thin I.Type) -> I { + // CHECK: [[X_ADDR:%[0-9]+]] = struct_element_addr {{.*}} : $*I, #I.x // CHECK: [[INIT_FN:%[0-9]+]] = function_ref @$s19default_constructor1IV1xSivpfi : $@convention(thin) () -> Int // CHECK: [[RESULT:%[0-9]+]] = apply [[INIT_FN]]() : $@convention(thin) () -> Int - // CHECK: [[X_ADDR:%[0-9]+]] = struct_element_addr {{.*}} : $*I, #I.x - // CHECK: assign [[RESULT]] to [[X_ADDR]] : $*Int + // CHECK: store [[RESULT]] to [trivial] [[X_ADDR]] : $*Int init(_: T) {} } diff --git a/test/SILGen/extensions.swift b/test/SILGen/extensions.swift index 6ceeae836e541..f862523a92395 100644 --- a/test/SILGen/extensions.swift +++ b/test/SILGen/extensions.swift @@ -53,15 +53,12 @@ struct Box { // CHECK: [[SELF_BOX:%.*]] = alloc_box $<τ_0_0> { var Box<τ_0_0> } // CHECK-NEXT: [[UNINIT_SELF_BOX:%.*]] = mark_uninitialized [rootself] [[SELF_BOX]] // CHECK-NEXT: [[SELF_ADDR:%.*]] = project_box [[UNINIT_SELF_BOX]] : $<τ_0_0> { var Box<τ_0_0> } +// CHECK: [[RESULT:%.*]] = struct_element_addr [[SELF_ADDR]] : $*Box, #Box.t // CHECK: [[INIT:%.*]] = function_ref @$s10extensions3BoxV1txSgvpfi : $@convention(thin) <τ_0_0> () -> @out Optional<τ_0_0> -// CHECK-NEXT: [[RESULT:%.*]] = alloc_stack $Optional // CHECK-NEXT: apply [[INIT]]([[RESULT]]) : $@convention(thin) <τ_0_0> () -> @out Optional<τ_0_0> -// CHECK-NEXT: [[T_ADDR:%.*]] = struct_element_addr [[SELF_ADDR]] : $*Box, #Box.t -// CHECK-NEXT: copy_addr [take] [[RESULT]] to [[T_ADDR]] : $*Optional -// CHECK-NEXT: dealloc_stack [[RESULT]] : $*Optional // CHECK-NEXT: [[RESULT:%.*]] = alloc_stack $Optional // CHECK-NEXT: [[RESULT_ADDR:%.*]] = init_enum_data_addr [[RESULT]] : $*Optional, #Optional.some!enumelt -// CHECK-NEXT: copy_addr %1 to [initialization] %14 : $*T +// CHECK-NEXT: copy_addr %1 to [initialization] [[RESULT_ADDR]] : $*T // CHECK-NEXT: inject_enum_addr [[RESULT]] : $*Optional, #Optional.some!enumelt // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] [[SELF_ADDR]] : $*Box // CHECK-NEXT: [[T_ADDR:%.*]] = struct_element_addr [[WRITE]] : $*Box, #Box.t diff --git a/test/SILGen/initializers.swift b/test/SILGen/initializers.swift index 48f19f15ce347..a317a9edd202f 100644 --- a/test/SILGen/initializers.swift +++ b/test/SILGen/initializers.swift @@ -580,13 +580,11 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: store {{%.*}} to [init] [[PROJ]] // // Then initialize the canary with nil. We are able to borrow the initialized self to avoid retain/release overhead. - // CHECK: [[CANARY_FUNC:%.*]] = function_ref @$s21failable_initializers17ThrowDerivedClassC6canaryAA6CanaryCSgvpfi : - // CHECK: [[OPT_CANARY:%.*]] = apply [[CANARY_FUNC]]() // CHECK: [[SELF:%.*]] = load_borrow [[PROJ]] // CHECK: [[CANARY_ADDR:%.*]] = ref_element_addr [[SELF]] - // CHECK: [[CANARY_ACCESS:%.*]] = begin_access [modify] [dynamic] [[CANARY_ADDR]] - // CHECK: assign [[OPT_CANARY]] to [[CANARY_ACCESS]] - // CHECK: end_access [[CANARY_ACCESS]] + // CHECK: [[CANARY_FUNC:%.*]] = function_ref @$s21failable_initializers17ThrowDerivedClassC6canaryAA6CanaryCSgvpfi : + // CHECK: [[OPT_CANARY:%.*]] = apply [[CANARY_FUNC]]() + // CHECK: store [[OPT_CANARY]] to [init] [[CANARY_ADDR]] // CHECK: end_borrow [[SELF]] // // Now we perform the unwrap. @@ -624,13 +622,11 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: store {{%.*}} to [init] [[PROJ]] // // Then initialize the canary with nil. We are able to borrow the initialized self to avoid retain/release overhead. - // CHECK: [[CANARY_FUNC:%.*]] = function_ref @$s21failable_initializers17ThrowDerivedClassC6canaryAA6CanaryCSgvpfi : - // CHECK: [[OPT_CANARY:%.*]] = apply [[CANARY_FUNC]]() // CHECK: [[SELF:%.*]] = load_borrow [[PROJ]] // CHECK: [[CANARY_ADDR:%.*]] = ref_element_addr [[SELF]] - // CHECK: [[CANARY_ACCESS:%.*]] = begin_access [modify] [dynamic] [[CANARY_ADDR]] - // CHECK: assign [[OPT_CANARY]] to [[CANARY_ACCESS]] - // CHECK: end_access [[CANARY_ACCESS]] + // CHECK: [[CANARY_FUNC:%.*]] = function_ref @$s21failable_initializers17ThrowDerivedClassC6canaryAA6CanaryCSgvpfi : + // CHECK: [[OPT_CANARY:%.*]] = apply [[CANARY_FUNC]]() + // CHECK: store [[OPT_CANARY]] to [init] [[CANARY_ADDR]] // CHECK: end_borrow [[SELF]] // // Now we begin argument emission where we perform the unwrap. diff --git a/test/SILGen/lifetime.swift b/test/SILGen/lifetime.swift index 5668b55f19daf..ebc931a7f7a48 100644 --- a/test/SILGen/lifetime.swift +++ b/test/SILGen/lifetime.swift @@ -398,26 +398,23 @@ class Foo { // CHECK: [[THIS:%[0-9]+]] = mark_uninitialized // -- initialization for y + // CHECK: [[BORROWED_THIS:%.*]] = begin_borrow [[THIS]] + // CHECK: [[THIS_Y:%.*]] = ref_element_addr [[BORROWED_THIS]] : {{.*}}, #Foo.y // CHECK: [[Y_INIT:%[0-9]+]] = function_ref @$s8lifetime3FooC1ySi_AA3RefCtvpfi : $@convention(thin) <τ_0_0> () -> (Int, @owned Ref) + // CHECK: [[THIS_Y_0:%.*]] = tuple_element_addr [[THIS_Y]] : $*(Int, Ref), 0 + // CHECK: [[THIS_Y_1:%.*]] = tuple_element_addr [[THIS_Y]] : $*(Int, Ref), 1 // CHECK: [[Y_VALUE:%[0-9]+]] = apply [[Y_INIT]]() // CHECK: ([[Y_EXTRACTED_0:%.*]], [[Y_EXTRACTED_1:%.*]]) = destructure_tuple - // CHECK: [[BORROWED_THIS:%.*]] = begin_borrow [[THIS]] - // CHECK: [[THIS_Y:%.*]] = ref_element_addr [[BORROWED_THIS]] : {{.*}}, #Foo.y - // CHECK: [[WRITE:%.*]] = begin_access [modify] [dynamic] [[THIS_Y]] : $*(Int, Ref) - // CHECK: [[THIS_Y_0:%.*]] = tuple_element_addr [[WRITE]] : $*(Int, Ref), 0 - // CHECK: assign [[Y_EXTRACTED_0]] to [[THIS_Y_0]] - // CHECK: [[THIS_Y_1:%.*]] = tuple_element_addr [[WRITE]] : $*(Int, Ref), 1 - // CHECK: assign [[Y_EXTRACTED_1]] to [[THIS_Y_1]] - // CHECK: end_access [[WRITE]] : $*(Int, Ref) + // CHECK: store [[Y_EXTRACTED_0]] to [trivial] [[THIS_Y_0]] + // CHECK: store [[Y_EXTRACTED_1]] to [init] [[THIS_Y_1]] // CHECK: end_borrow [[BORROWED_THIS]] // -- Initialization for w - // CHECK: [[Z_FUNC:%.*]] = function_ref @$s{{.*}}8lifetime3FooC1wAA3RefCvpfi : $@convention(thin) <τ_0_0> () -> @owned Ref - // CHECK: [[Z_RESULT:%.*]] = apply [[Z_FUNC]]() // CHECK: [[BORROWED_THIS:%.*]] = begin_borrow [[THIS]] // CHECK: [[THIS_Z:%.*]] = ref_element_addr [[BORROWED_THIS]] - // CHECK: [[WRITE:%.*]] = begin_access [modify] [dynamic] [[THIS_Z]] : $*Ref - // CHECK: assign [[Z_RESULT]] to [[WRITE]] + // CHECK: [[Z_FUNC:%.*]] = function_ref @$s{{.*}}8lifetime3FooC1wAA3RefCvpfi : $@convention(thin) <τ_0_0> () -> @owned Ref + // CHECK: [[Z_RESULT:%.*]] = apply [[Z_FUNC]]() + // CHECK: store [[Z_RESULT]] to [init] [[THIS_Z]] // CHECK: end_borrow [[BORROWED_THIS]] // -- Initialization for x @@ -462,11 +459,10 @@ class Foo { // -- First we initialize #Foo.y. // CHECK: [[BORROWED_THIS:%.*]] = begin_borrow [[THIS]] // CHECK: [[THIS_Y:%.*]] = ref_element_addr [[BORROWED_THIS]] : $Foo, #Foo.y - // CHECK: [[WRITE:%.*]] = begin_access [modify] [dynamic] [[THIS_Y]] : $*(Int, Ref) - // CHECK: [[THIS_Y_1:%.*]] = tuple_element_addr [[WRITE]] : $*(Int, Ref), 0 - // CHECK: assign {{.*}} to [[THIS_Y_1]] : $*Int - // CHECK: [[THIS_Y_2:%.*]] = tuple_element_addr [[WRITE]] : $*(Int, Ref), 1 - // CHECK: assign {{.*}} to [[THIS_Y_2]] : $*Ref + // CHECK: [[THIS_Y_1:%.*]] = tuple_element_addr [[THIS_Y]] : $*(Int, Ref), 0 + // CHECK: [[THIS_Y_2:%.*]] = tuple_element_addr [[THIS_Y]] : $*(Int, Ref), 1 + // CHECK: store {{.*}} to [trivial] [[THIS_Y_1]] : $*Int + // CHECK: store {{.*}} to [init] [[THIS_Y_2]] : $*Ref // CHECK: end_borrow [[BORROWED_THIS]] // -- Then we create a box that we will use to perform a copy_addr into #Foo.x a bit later. diff --git a/test/SILGen/objc_dealloc.swift b/test/SILGen/objc_dealloc.swift index c8a779978612b..85961b351b4d1 100644 --- a/test/SILGen/objc_dealloc.swift +++ b/test/SILGen/objc_dealloc.swift @@ -66,13 +66,11 @@ class SwiftGizmo : Gizmo { // CHECK: bb0([[SELF_PARAM:%[0-9]+]] : @owned $SwiftGizmo): // CHECK-NEXT: debug_value [[SELF_PARAM]] : $SwiftGizmo, let, name "self" // CHECK-NEXT: [[SELF:%[0-9]+]] = mark_uninitialized [rootself] [[SELF_PARAM]] : $SwiftGizmo + // CHECK: [[BORROWED_SELF:%.*]] = begin_borrow [[SELF]] + // CHECK-NEXT: [[X:%[0-9]+]] = ref_element_addr [[BORROWED_SELF]] : $SwiftGizmo, #SwiftGizmo.x // CHECK: [[XINIT:%[0-9]+]] = function_ref @$s12objc_dealloc10SwiftGizmoC1xAA1XCvpfi // CHECK-NEXT: [[XOBJ:%[0-9]+]] = apply [[XINIT]]() : $@convention(thin) () -> @owned X - // CHECK-NEXT: [[BORROWED_SELF:%.*]] = begin_borrow [[SELF]] - // CHECK-NEXT: [[X:%[0-9]+]] = ref_element_addr [[BORROWED_SELF]] : $SwiftGizmo, #SwiftGizmo.x - // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [dynamic] [[X]] : $*X - // CHECK-NEXT: assign [[XOBJ]] to [[WRITE]] : $*X - // CHECK-NEXT: end_access [[WRITE]] : $*X + // CHECK-NEXT: store [[XOBJ]] to [init] [[X]] : $*X // CHECK-NEXT: end_borrow [[BORROWED_SELF]] // CHECK-NEXT: return [[SELF]] : $SwiftGizmo diff --git a/test/SILGen/properties.swift b/test/SILGen/properties.swift index 90890a41048cc..02d4e2de10429 100644 --- a/test/SILGen/properties.swift +++ b/test/SILGen/properties.swift @@ -670,7 +670,9 @@ class r19254812Derived: r19254812Base{ // Initialization of the pi field: no copy_values/releases. // CHECK: [[SELF:%[0-9]+]] = load_borrow [[PB_BOX]] : $*r19254812Derived // CHECK-NEXT: [[PIPTR:%[0-9]+]] = ref_element_addr [[SELF]] : $r19254812Derived, #r19254812Derived.pi -// CHECK-NEXT: assign {{.*}} to [[PIPTR]] : $*Double +// CHECK: [[FN:%[0-9]+]] = function_ref @$s10properties16r19254812DerivedC2piSdvpfi : $@convention(thin) () -> Double +// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[FN]]() : $@convention(thin) () -> Double +// CHECK-NEXT: store [[RESULT]] to [trivial] [[PIPTR]] : $*Double // CHECK-NOT: destroy_value // CHECK-NOT: copy_value diff --git a/test/SILOptimizer/stored_property_initial_value.swift b/test/SILOptimizer/stored_property_initial_value.swift new file mode 100644 index 0000000000000..5d6954c55bb35 --- /dev/null +++ b/test/SILOptimizer/stored_property_initial_value.swift @@ -0,0 +1,29 @@ +// RUN: %target-swift-frontend -emit-sil %s + +// This is an integration test to ensure that SILGen, DI and the ownership +// verifier support the SIL we generate for stored properties with initial +// values. + +enum E { + case foo(Any) +} + +struct S { + var x1: T? = nil + var x2 = 0 as! T + var x3 = E.foo(0) + var x4: (Int, Int, Int, Int) = (0, 0, 0, 0) + var x5: (U, U, U, U) = (0, 0, 0, 0) + + init() {} +} + +class C { + var x1: T? = nil + var x2 = 0 as! T + var x3 = E.foo(0) + var x4: (Int, Int, Int, Int) = (0, 0, 0, 0) + var x5: (U, U, U, U) = (0, 0, 0, 0) + + init() {} +} From 7fd461570bf126095e5e660c7d9c3514bdfce8df Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 9 Oct 2020 13:58:57 -0400 Subject: [PATCH 402/745] SILGen: Fix crash when a stored property with an initial value has a reabstractable type A constructor can constrain generic parameters more than the type itself, either because there is a 'where' clause on the constructor, or because the constructor is defined inside an extension with a 'where' clause. In this case, when the constructor calls a stored property initializer to implicitly initialize a stored property with an initial value, we must apply substitutions to get the correct result type for the call. If the original type of the stored property lowers differently based on the abstraction pattern, for example if it is a function type, then emitApply() would by default re-abstract the result to the most specific abstraction pattern possible. However, this was wrong because we store the result value into the stored property, and a stored property's type should always be lowered with the most generic abstraction pattern. In practice, this meant if you have a stored property of type (T) -> (), and an initializer with 'where T == String' for example, we would call the initializer, to produce a value with lowered type (@in_guaranteed String) -> (), then thunk it to a (@guaranteed String) -> (), and try to store the thunk into the stored property -- which has type (@in_guaranteed String) -> (). This would either miscompile or trigger an assertion. To get around this, we want to bypass the orig-to-subst conversion performed in emitApply(). My chosen solution is to have emitApply() emit the result into a ConvertingInitialization set up to perform a subst-to-orig conversion. Now that ConvertingInitialization is smart enough to peephole away matched pairs of orig-to-subst and subst-to-orig conversions, this always reduces to a no-op, and the emitApply() call produces and stores a value with the correct abstraction pattern. Fixes . --- lib/SILGen/SILGenConstructor.cpp | 34 +++++++++++++++- .../stored_property_init_reabstraction.swift | 39 +++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 test/SILGen/stored_property_init_reabstraction.swift diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 8954a391e272e..84cd1816d8924 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "ArgumentSource.h" +#include "Conversion.h" #include "Initialization.h" #include "LValue.h" #include "RValue.h" @@ -1058,8 +1059,37 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, // Figure out what we're initializing. auto memberInit = emitMemberInit(*this, selfDecl, varPattern); - emitAndStoreInitialValueInto(*this, varPattern, pbd, i, subs, - origType, substType, memberInit.get()); + // This whole conversion thing is about eliminating the + // paired orig-to-subst subst-to-orig conversions that + // will happen if the storage is at a different abstraction + // level than the constructor. When emitApply() is used + // to call the stored property initializer, it naturally + // wants to convert the result back to the most substituted + // abstraction level. To undo this, we use a converting + // initialization and rely on the peephole that optimizes + // out the redundant conversion. + auto loweredResultTy = getLoweredType(origType, substType); + auto loweredSubstTy = getLoweredType(substType); + + if (loweredResultTy != loweredSubstTy) { + Conversion conversion = Conversion::getSubstToOrig( + origType, substType, + loweredResultTy); + + ConvertingInitialization convertingInit(conversion, + SGFContext(memberInit.get())); + + emitAndStoreInitialValueInto(*this, varPattern, pbd, i, subs, + origType, substType, &convertingInit); + + auto finalValue = convertingInit.finishEmission( + *this, varPattern, ManagedValue::forInContext()); + if (!finalValue.isInContext()) + finalValue.forwardInto(*this, varPattern, memberInit.get()); + } else { + emitAndStoreInitialValueInto(*this, varPattern, pbd, i, subs, + origType, substType, memberInit.get()); + } } } } diff --git a/test/SILGen/stored_property_init_reabstraction.swift b/test/SILGen/stored_property_init_reabstraction.swift new file mode 100644 index 0000000000000..dabd407683435 --- /dev/null +++ b/test/SILGen/stored_property_init_reabstraction.swift @@ -0,0 +1,39 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +// rdar://problem/67419937 + +class Class { + var fn: ((T) -> ())? = nil + + init(_: T) where T == Int {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s34stored_property_init_reabstraction5ClassCyACySiGSicSiRszlufc : $@convention(method) (Int, @owned Class) -> @owned Class { +// CHECK: [[SELF:%.*]] = mark_uninitialized [rootself] %1 : $Class +// CHECK: [[BORROW:%.*]] = begin_borrow [[SELF]] : $Class +// CHECK: [[ADDR:%.*]] = ref_element_addr [[BORROW]] : $Class, #Class.fn +// CHECK: [[INIT:%.*]] = function_ref @$s34stored_property_init_reabstraction5ClassC2fnyxcSgvpfi : $@convention(thin) <τ_0_0> () -> @owned Optional<@callee_guaranteed @substituted <τ_0_0> (@in_guaranteed τ_0_0) -> () for <τ_0_0>> +// CHECK: [[VALUE:%.*]] = apply [[INIT]]() : $@convention(thin) <τ_0_0> () -> @owned Optional<@callee_guaranteed @substituted <τ_0_0> (@in_guaranteed τ_0_0) -> () for <τ_0_0>> +// CHECK: store [[VALUE]] to [init] [[ADDR]] : $*Optional<@callee_guaranteed @substituted <τ_0_0> (@in_guaranteed τ_0_0) -> () for > +// CHECK: end_borrow [[BORROW]] : $Class + +struct Struct { + var fn: ((T) -> ())? = nil + + init(_: T) where T == Int {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s34stored_property_init_reabstraction6StructVyACySiGSicSiRszlufC : $@convention(method) (Int, @thin Struct.Type) -> @owned Struct { +// CHECK: [[SELF_BOX:%.*]] = mark_uninitialized [rootself] {{%.*}} : ${ var Struct } +// CHECK: [[SELF:%.*]] = project_box [[SELF_BOX]] : ${ var Struct }, 0 +// CHECK: [[ADDR:%.*]] = struct_element_addr [[SELF]] : $*Struct, #Struct.fn +// CHECK: [[INIT:%.*]] = function_ref @$s34stored_property_init_reabstraction6StructV2fnyxcSgvpfi : $@convention(thin) <τ_0_0> () -> @owned Optional<@callee_guaranteed @substituted <τ_0_0> (@in_guaranteed τ_0_0) -> () for <τ_0_0>> +// CHECK: [[VALUE:%.*]] = apply [[INIT]]() : $@convention(thin) <τ_0_0> () -> @owned Optional<@callee_guaranteed @substituted <τ_0_0> (@in_guaranteed τ_0_0) -> () for <τ_0_0>> +// CHECK: store [[VALUE]] to [init] [[ADDR]] : $*Optional<@callee_guaranteed @substituted <τ_0_0> (@in_guaranteed τ_0_0) -> () for > + +struct ComplexExample { + var (fn, value): (((T) -> ())?, U?) = (nil, nil) + var anotherValue: (((T) -> ())?, U?) = (nil, nil) + + init(_: T) where T == String {} +} \ No newline at end of file From 3d160d956a69551deb5173f308d19527f7dd6c70 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 9 Oct 2020 18:16:18 -0400 Subject: [PATCH 403/745] Sema: Move SimpleDidSetRequest::evaluate() to TypeCheckStorage.cpp I think TypeCheckStorage.cpp is a better place for this than TypeCheckDecl.cpp, since all the other requests for storage and accessor declarations appear in the former. --- lib/Sema/TypeCheckDecl.cpp | 67 ---------------------------------- lib/Sema/TypeCheckStorage.cpp | 68 +++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 67 deletions(-) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 3e21327297eba..66240cc334e70 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -1273,73 +1273,6 @@ EnumRawValuesRequest::evaluate(Evaluator &eval, EnumDecl *ED, return std::make_tuple<>(); } -bool SimpleDidSetRequest::evaluate(Evaluator &evaluator, - AccessorDecl *decl) const { - - class OldValueFinder : public ASTWalker { - const ParamDecl *OldValueParam; - bool foundOldValueRef = false; - - public: - OldValueFinder(const ParamDecl *param) : OldValueParam(param) {} - - virtual std::pair walkToExprPre(Expr *E) override { - if (!E) - return {true, E}; - if (auto DRE = dyn_cast(E)) { - if (auto decl = DRE->getDecl()) { - if (decl == OldValueParam) { - foundOldValueRef = true; - return {false, nullptr}; - } - } - } - - return {true, E}; - } - - bool didFindOldValueRef() { return foundOldValueRef; } - }; - - // If this is not a didSet accessor, bail out. - if (decl->getAccessorKind() != AccessorKind::DidSet) { - return false; - } - - // Always assume non-simple 'didSet' in code completion mode. - if (decl->getASTContext().SourceMgr.hasCodeCompletionBuffer()) - return false; - - // didSet must have a single parameter. - if (decl->getParameters()->size() != 1) { - return false; - } - - auto param = decl->getParameters()->get(0); - // If this parameter is not implicit, then it means it has been explicitly - // provided by the user (i.e. 'didSet(oldValue)'). This means we cannot - // consider this a "simple" didSet because we have to fetch the oldValue - // regardless of whether it's referenced in the body or not. - if (!param->isImplicit()) { - return false; - } - - // If we find a reference to the implicit 'oldValue' parameter, then it is - // not a "simple" didSet because we need to fetch it. - auto walker = OldValueFinder(param); - decl->getTypecheckedBody()->walk(walker); - auto hasOldValueRef = walker.didFindOldValueRef(); - if (!hasOldValueRef) { - // If the body does not refer to implicit 'oldValue', it means we can - // consider this as a "simple" didSet. Let's also erase the implicit - // oldValue as it is never used. - auto &ctx = decl->getASTContext(); - decl->setParameters(ParameterList::createEmpty(ctx)); - return true; - } - return false; -} - const ConstructorDecl * swift::findNonImplicitRequiredInit(const ConstructorDecl *CD) { while (CD->isImplicit()) { diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index c9a4c5a2cf9f1..e507d3e3322a1 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -3186,3 +3186,71 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator, return info; } + +bool SimpleDidSetRequest::evaluate(Evaluator &evaluator, + AccessorDecl *decl) const { + + class OldValueFinder : public ASTWalker { + const ParamDecl *OldValueParam; + bool foundOldValueRef = false; + + public: + OldValueFinder(const ParamDecl *param) : OldValueParam(param) {} + + virtual std::pair walkToExprPre(Expr *E) override { + if (!E) + return {true, E}; + if (auto DRE = dyn_cast(E)) { + if (auto decl = DRE->getDecl()) { + if (decl == OldValueParam) { + foundOldValueRef = true; + return {false, nullptr}; + } + } + } + + return {true, E}; + } + + bool didFindOldValueRef() { return foundOldValueRef; } + }; + + // If this is not a didSet accessor, bail out. + if (decl->getAccessorKind() != AccessorKind::DidSet) { + return false; + } + + // Always assume non-simple 'didSet' in code completion mode. + if (decl->getASTContext().SourceMgr.hasCodeCompletionBuffer()) + return false; + + // didSet must have a single parameter. + if (decl->getParameters()->size() != 1) { + return false; + } + + auto param = decl->getParameters()->get(0); + // If this parameter is not implicit, then it means it has been explicitly + // provided by the user (i.e. 'didSet(oldValue)'). This means we cannot + // consider this a "simple" didSet because we have to fetch the oldValue + // regardless of whether it's referenced in the body or not. + if (!param->isImplicit()) { + return false; + } + + // If we find a reference to the implicit 'oldValue' parameter, then it is + // not a "simple" didSet because we need to fetch it. + auto walker = OldValueFinder(param); + decl->getTypecheckedBody()->walk(walker); + auto hasOldValueRef = walker.didFindOldValueRef(); + if (!hasOldValueRef) { + // If the body does not refer to implicit 'oldValue', it means we can + // consider this as a "simple" didSet. Let's also erase the implicit + // oldValue as it is never used. + auto &ctx = decl->getASTContext(); + decl->setParameters(ParameterList::createEmpty(ctx)); + return true; + } + return false; +} + From b047f9e25277f3180b3d844b975b10023556bc45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Sun, 11 Oct 2020 12:47:09 -0700 Subject: [PATCH 404/745] [windows] Provide %long-tmp in Windows for extended length temp paths. In Windows %t might end up very close to the maximum path length of 260 characters. It is possible to use extended lenght paths with a \\?\ prefix. The changes introduce %long-tmp for cases that the test suite is going over those limits and need a hint in Windows that the path might be long. It expands to the same as %t in other platforms. This should fix the test/Misc/stats_dir_profiler.swift in the Windows VS 2017 CI machines and hopefully not affect anything else. --- test/Misc/stats_dir_profiler.swift | 7 +++++-- test/lit.cfg | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/test/Misc/stats_dir_profiler.swift b/test/Misc/stats_dir_profiler.swift index 2fb630e9345f1..991ccd43253b3 100644 --- a/test/Misc/stats_dir_profiler.swift +++ b/test/Misc/stats_dir_profiler.swift @@ -3,8 +3,11 @@ // RUN: %empty-directory(%t/stats-entities) // RUN: %target-swiftc_driver -o %t/main -module-name main -stats-output-dir %t/stats-events %s -profile-stats-events // RUN: %target-swiftc_driver -o %t/main -module-name main -stats-output-dir %t/stats-entities %s -profile-stats-entities -// RUN: %FileCheck -check-prefix=EVENTS -input-file %t/stats-events/*.dir/Time.User.events %s -// RUN: %FileCheck -check-prefix=ENTITIES -input-file %t/stats-entities/*.dir/Time.User.entities %s + +// Need to use %long-tmp because in Windows the * may expand to a path longer +// than 260 characters. +// RUN: %FileCheck -check-prefix=EVENTS -input-file %long-tmp/stats-events/*.dir/Time.User.events %s +// RUN: %FileCheck -check-prefix=ENTITIES -input-file %long-tmp/stats-entities/*.dir/Time.User.entities %s // EVENTS: {{perform-sema;.*;typecheck-decl.* [0-9]+}} // ENTITIES: {{perform-sema;.*;TypeCheckFunctionBodyRequest bar\(\);typecheck-stmt.* [0-9]+}} diff --git a/test/lit.cfg b/test/lit.cfg index ce463d08a81e2..55ce76e464905 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -2074,8 +2074,12 @@ config.substitutions.append(('%add_num_extra_inhabitants', add_num_extra_inhabit if kIsWindows: config.substitutions.append( ('%diff', 'diff --strip-trailing-cr') ) + # Use instead of %t when the possible temporal path (and suffix) might end + # up being longer than 260 characters in Windows machines. + config.substitutions.append( ('%long-tmp', '\\\\?\\%t') ) else: config.substitutions.append( ('%diff', 'diff') ) + config.substitutions.append( ('%long-tmp', '%t') ) visual_studio_version = os.environ.get('VisualStudioVersion') if kIsWindows and visual_studio_version: From 6f15007656385f11c0e2f282dce600a1ccafd024 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Sun, 11 Oct 2020 17:05:54 -0700 Subject: [PATCH 405/745] XFAIL Incremental/CrossModule tests These are failing in multiple bots; it looks like they need more time to bake. --- test/Incremental/CrossModule/external-cascade.swift | 3 +++ test/Incremental/CrossModule/linear.swift | 3 +++ test/Incremental/CrossModule/transitive.swift | 3 +++ 3 files changed, 9 insertions(+) diff --git a/test/Incremental/CrossModule/external-cascade.swift b/test/Incremental/CrossModule/external-cascade.swift index 83407e1573124..993493a092157 100644 --- a/test/Incremental/CrossModule/external-cascade.swift +++ b/test/Incremental/CrossModule/external-cascade.swift @@ -4,6 +4,9 @@ // rdar://problem/70012853 // XFAIL: OS=windows-msvc +// rdar://70175753 +// REQUIRES: rdar70175753 + // // This test establishes a chain of modules that all depend on a set of // bridging headers. This test ensures that changes to external dependencies - diff --git a/test/Incremental/CrossModule/linear.swift b/test/Incremental/CrossModule/linear.swift index 488c715320159..084392d1c88c8 100644 --- a/test/Incremental/CrossModule/linear.swift +++ b/test/Incremental/CrossModule/linear.swift @@ -4,6 +4,9 @@ // rdar://problem/70012853 // XFAIL: OS=windows-msvc +// rdar://70175753 +// REQUIRES: rdar70175753 + // // This test establishes a "linear" chain of modules that import one another // and ensures that a cross-module incremental build does not needlessly diff --git a/test/Incremental/CrossModule/transitive.swift b/test/Incremental/CrossModule/transitive.swift index 83f354f082ed9..8c5d29b096c01 100644 --- a/test/Incremental/CrossModule/transitive.swift +++ b/test/Incremental/CrossModule/transitive.swift @@ -4,6 +4,9 @@ // rdar://problem/70012853 // XFAIL: OS=windows-msvc +// rdar://70175753 +// REQUIRES: rdar70175753 + // // This test establishes a "transitive" chain of modules that import one another // and ensures that a cross-module incremental build rebuilds all modules From 7cea31ba3c055ca316b344b3ca8eff8f35a5baa1 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Wed, 7 Oct 2020 13:56:38 -0700 Subject: [PATCH 406/745] SILMem2Reg: Don't add dead values as phi arguments A dealloc_stack ends the lifetime of an alloc_stack on a path. We don't have to pass RunningVal beyond the dealloc_stack as phi argument to the post dominating block. --- lib/SILOptimizer/Transforms/SILMem2Reg.cpp | 6 ++++- test/SILOptimizer/mem2reg.sil | 27 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp index 332af38b3f2b3..e72abfb68a8fb 100644 --- a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp +++ b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp @@ -468,8 +468,12 @@ StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) { // Stop on deallocation. if (auto *DSI = dyn_cast(Inst)) { - if (DSI->getOperand() == ASI) + if (DSI->getOperand() == ASI) { + // Reset LastStore. + // So that we don't pass RunningVal as a phi arg beyond dealloc_stack + LastStore = nullptr; break; + } } } if (LastStore) { diff --git a/test/SILOptimizer/mem2reg.sil b/test/SILOptimizer/mem2reg.sil index a77e77e4c2552..8373fbee77f66 100644 --- a/test/SILOptimizer/mem2reg.sil +++ b/test/SILOptimizer/mem2reg.sil @@ -465,3 +465,30 @@ bb0(%0 : $Optional): %4 = tuple() return %4 : $() } + +// CHECK-LABEL: sil @multi_basic_block_use_on_one_path : +// CHECK-NOT: alloc_stack +// CHECK:bb2: +// CHECK: br bb3(undef : $Klass) +// CHECK-LABEL: } // end sil function 'multi_basic_block_use_on_one_path' +sil @multi_basic_block_use_on_one_path : $@convention(thin) (@owned Klass) -> () { +bb0(%0 : $Klass): + %1 = alloc_stack $Klass + cond_br undef, bb1, bb2 + +bb1: + dealloc_stack %1 : $*Klass + strong_release %0 : $Klass + br bb3 + +bb2: + store %0 to %1 : $*Klass + %7 = load %1 : $*Klass + dealloc_stack %1 : $*Klass + strong_release %7 : $Klass + br bb3 + +bb3: + %11 = tuple () + return %11 : $() +} From 826f17e53422d284edeeaf7ed6f13a122cd10f27 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Thu, 8 Oct 2020 10:03:17 +0300 Subject: [PATCH 407/745] =?UTF-8?q?Decouple=20detection=20of=20=C2=ABSelf?= =?UTF-8?q?=20=3D=3D=C2=BB=20constraints=20from=20SelfReferenceKind?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/swift/AST/Decl.h | 24 ++++++++--------------- lib/AST/Decl.cpp | 35 ---------------------------------- lib/Sema/TypeCheckProtocol.cpp | 26 ++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 52 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 0c06182cc3dc9..d6dca239d7170 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3747,56 +3747,48 @@ class ClassDecl final : public NominalTypeDecl { struct SelfReferenceKind { bool result; bool parameter; - bool requirement; bool other; /// The type does not refer to 'Self' at all. static SelfReferenceKind None() { - return SelfReferenceKind(false, false, false, false); + return SelfReferenceKind(false, false, false); } /// The type refers to 'Self', but only as the type of a property or /// the result type of a method/subscript. static SelfReferenceKind Result() { - return SelfReferenceKind(true, false, false, false); + return SelfReferenceKind(true, false, false); } /// The type refers to 'Self', but only as the parameter type /// of a method/subscript. static SelfReferenceKind Parameter() { - return SelfReferenceKind(false, true, false, false); - } - - /// The type refers to 'Self' within a same-type requiement. - static SelfReferenceKind Requirement() { - return SelfReferenceKind(false, false, true, false); + return SelfReferenceKind(false, true, false); } /// The type refers to 'Self' in a position that is invariant. static SelfReferenceKind Other() { - return SelfReferenceKind(false, false, false, true); + return SelfReferenceKind(false, false, true); } SelfReferenceKind flip() const { - return SelfReferenceKind(parameter, result, requirement, other); + return SelfReferenceKind(parameter, result, other); } SelfReferenceKind operator|=(SelfReferenceKind kind) { result |= kind.result; - requirement |= kind.requirement; parameter |= kind.parameter; other |= kind.other; return *this; } operator bool() const { - return result || parameter || requirement || other; + return result || parameter || other; } private: - SelfReferenceKind(bool result, bool parameter, bool requirement, bool other) - : result(result), parameter(parameter), requirement(requirement), - other(other) { } + SelfReferenceKind(bool result, bool parameter, bool other) + : result(result), parameter(parameter), other(other) { } }; /// The set of known protocols for which derived conformances are supported. diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 46b1c2c493c37..e33dd9186b7bc 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4778,25 +4778,6 @@ findProtocolSelfReferences(const ProtocolDecl *proto, Type type, return SelfReferenceKind::None(); } -/// Find Self references in a generic signature's same-type requirements. -static SelfReferenceKind -findProtocolSelfReferences(const ProtocolDecl *protocol, - GenericSignature genericSig){ - if (!genericSig) return SelfReferenceKind::None(); - - auto selfTy = protocol->getSelfInterfaceType(); - for (const auto &req : genericSig->getRequirements()) { - if (req.getKind() != RequirementKind::SameType) - continue; - - if (req.getFirstType()->isEqual(selfTy) || - req.getSecondType()->isEqual(selfTy)) - return SelfReferenceKind::Requirement(); - } - - return SelfReferenceKind::None(); -} - /// Find Self references within the given requirement. SelfReferenceKind ProtocolDecl::findProtocolSelfReferences(const ValueDecl *value, @@ -4836,27 +4817,11 @@ ProtocolDecl::findProtocolSelfReferences(const ValueDecl *value, return SelfReferenceKind::Other(); } - // Check the requirements of a generic function. - if (func->isGeneric()) { - if (auto result = - ::findProtocolSelfReferences(this, func->getGenericSignature())) - return result; - } - return ::findProtocolSelfReferences(this, type, skipAssocTypes); } else { assert(isa(value)); - if (auto *const subscript = dyn_cast(value)) { - // Check the requirements of a generic subscript. - if (subscript->isGeneric()) { - if (auto result = ::findProtocolSelfReferences( - this, subscript->getGenericSignature())) - return result; - } - } - return ::findProtocolSelfReferences(this, type, skipAssocTypes); } diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 750bfbe4d25ba..fcb261c6dc01c 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -3359,6 +3359,30 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) { } } +/// Whether the given protocol requirement has a "Self ==" constraint. +static bool hasSelfSameTypeConstraint(const ValueDecl *req) { + const auto *proto = cast(req->getDeclContext()); + const auto *genCtx = req->getAsGenericContext(); + if (!genCtx) + return false; + + const auto genericSig = genCtx->getGenericSignature(); + if (!genericSig) + return false; + + const auto selfTy = proto->getSelfInterfaceType(); + for (const auto &constr : genericSig->getRequirements()) { + if (constr.getKind() != RequirementKind::SameType) + continue; + + if (constr.getFirstType()->isEqual(selfTy) || + constr.getSecondType()->isEqual(selfTy)) + return true; + } + + return false; +} + /// Determine the given witness has a same-type constraint constraining the /// given 'Self' type, and return the requirement that does. /// @@ -3509,7 +3533,7 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement, }); } } - } else if (selfKind.requirement) { + } else if (hasSelfSameTypeConstraint(requirement)) { if (auto targetPair = getAdopteeSelfSameTypeConstraint(classDecl, witness)) { // A "Self ==" constraint works incorrectly with subclasses. Complain. From 4ee517f65cded3bcfc707ed277113165db486167 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Fri, 9 Oct 2020 14:01:56 +0300 Subject: [PATCH 408/745] AST: Refactor SelfReferenceKind in preparation for #33767 This will enable us to allow members containing references to Self or associated types only in covariant position to be used on an existential --- include/swift/AST/Decl.h | 104 ++++++++++++--------- lib/AST/Decl.cpp | 164 +++++++++++++++++++-------------- lib/Sema/TypeCheckProtocol.cpp | 23 ++--- 3 files changed, 165 insertions(+), 126 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index d6dca239d7170..bbd51f19efbf2 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3741,54 +3741,79 @@ class ClassDecl final : public NominalTypeDecl { } }; +/// A convenience wrapper around the \c SelfReferencePosition::Kind enum. +struct SelfReferencePosition final { + enum Kind : uint8_t { None, Covariant, Contravariant, Invariant }; -/// Describes whether a requirement refers to 'Self', for use in the -/// is-inheritable and is-available-existential checks. -struct SelfReferenceKind { - bool result; - bool parameter; - bool other; +private: + Kind kind; - /// The type does not refer to 'Self' at all. - static SelfReferenceKind None() { - return SelfReferenceKind(false, false, false); +public: + SelfReferencePosition(Kind kind) : kind(kind) {} + + SelfReferencePosition flipped() const { + switch (kind) { + case None: + case Invariant: + return *this; + case Covariant: + return Contravariant; + case Contravariant: + return Covariant; + } } - /// The type refers to 'Self', but only as the type of a property or - /// the result type of a method/subscript. - static SelfReferenceKind Result() { - return SelfReferenceKind(true, false, false); - } + explicit operator bool() const { return kind > None; } - /// The type refers to 'Self', but only as the parameter type - /// of a method/subscript. - static SelfReferenceKind Parameter() { - return SelfReferenceKind(false, true, false); - } + operator Kind() const { return kind; } +}; - /// The type refers to 'Self' in a position that is invariant. - static SelfReferenceKind Other() { - return SelfReferenceKind(false, false, true); +/// Describes the least favorable positions at which a requirement refers +/// to 'Self' in terms of variance, for use in the is-inheritable and +/// is-available-existential checks. +struct SelfReferenceInfo final { + using Position = SelfReferencePosition; + + bool hasCovariantSelfResult; + Position selfRef; + Position assocTypeRef; + + /// A reference to 'Self'. + static SelfReferenceInfo forSelfRef(Position position) { + assert(position); + return SelfReferenceInfo(false, position, Position::None); } - SelfReferenceKind flip() const { - return SelfReferenceKind(parameter, result, other); + /// A reference to 'Self' through an associated type. + static SelfReferenceInfo forAssocTypeRef(Position position) { + assert(position); + return SelfReferenceInfo(false, Position::None, position); } - SelfReferenceKind operator|=(SelfReferenceKind kind) { - result |= kind.result; - parameter |= kind.parameter; - other |= kind.other; + SelfReferenceInfo operator|=(const SelfReferenceInfo &pos) { + hasCovariantSelfResult |= pos.hasCovariantSelfResult; + if (pos.selfRef > selfRef) { + selfRef = pos.selfRef; + } + if (pos.assocTypeRef > assocTypeRef) { + assocTypeRef = pos.assocTypeRef; + } return *this; } - operator bool() const { - return result || parameter || other; + explicit operator bool() const { + return hasCovariantSelfResult || selfRef || assocTypeRef; } + SelfReferenceInfo() + : hasCovariantSelfResult(false), selfRef(Position::None), + assocTypeRef(Position::None) {} + private: - SelfReferenceKind(bool result, bool parameter, bool other) - : result(result), parameter(parameter), other(other) { } + SelfReferenceInfo(bool hasCovariantSelfResult, Position selfRef, + Position assocTypeRef) + : hasCovariantSelfResult(hasCovariantSelfResult), selfRef(selfRef), + assocTypeRef(assocTypeRef) {} }; /// The set of known protocols for which derived conformances are supported. @@ -3970,15 +3995,12 @@ class ProtocolDecl final : public NominalTypeDecl { /// Find direct Self references within the given requirement. /// - /// \param allowCovariantParameters If true, 'Self' is assumed to be - /// covariant anywhere; otherwise, only in the return type of the top-level - /// function type. - /// - /// \param skipAssocTypes If true, associated types of 'Self' are ignored; - /// otherwise, they count as an 'other' usage of 'Self'. - SelfReferenceKind findProtocolSelfReferences(const ValueDecl *decl, - bool allowCovariantParameters, - bool skipAssocTypes) const; + /// \param treatNonResultCovariantSelfAsInvariant If true, 'Self' is only + /// assumed to be covariant in a top-level non-function type, or in the + /// eventual result type of a top-level function type. + SelfReferenceInfo + findProtocolSelfReferences(const ValueDecl *decl, + bool treatNonResultCovariantSelfAsInvariant) const; /// Determine whether we are allowed to refer to an existential type /// conforming to this protocol. This is only permitted if the type of diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index e33dd9186b7bc..17d7db199123b 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4699,148 +4699,172 @@ bool ProtocolDecl::existentialConformsToSelf() const { } /// Classify usages of Self in the given type. -static SelfReferenceKind +/// +/// \param position The position we are currently at, in terms of variance. +static SelfReferenceInfo findProtocolSelfReferences(const ProtocolDecl *proto, Type type, - bool skipAssocTypes) { + SelfReferencePosition position) { // Tuples preserve variance. if (auto tuple = type->getAs()) { - auto kind = SelfReferenceKind::None(); + auto info = SelfReferenceInfo(); for (auto &elt : tuple->getElements()) { - kind |= findProtocolSelfReferences(proto, elt.getType(), skipAssocTypes); + info |= findProtocolSelfReferences(proto, elt.getType(), position); } - return kind; + + // A covariant Self result inside a tuple will not be bona fide. + info.hasCovariantSelfResult = false; + + return info; } // Function preserve variance in the result type, and flip variance in // the parameter type. if (auto funcTy = type->getAs()) { - auto inputKind = SelfReferenceKind::None(); + auto inputInfo = SelfReferenceInfo(); for (auto param : funcTy->getParams()) { // inout parameters are invariant. if (param.isInOut()) { - if (findProtocolSelfReferences(proto, param.getPlainType(), - skipAssocTypes)) { - return SelfReferenceKind::Other(); - } + inputInfo |= findProtocolSelfReferences( + proto, param.getPlainType(), SelfReferencePosition::Invariant); + continue; } - inputKind |= findProtocolSelfReferences(proto, param.getParameterType(), - skipAssocTypes); + inputInfo |= findProtocolSelfReferences(proto, param.getParameterType(), + position.flipped()); } - auto resultKind = findProtocolSelfReferences(proto, funcTy->getResult(), - skipAssocTypes); - auto kind = inputKind.flip(); - kind |= resultKind; - return kind; + // A covariant Self result inside a parameter will not be bona fide. + inputInfo.hasCovariantSelfResult = false; + + auto resultInfo = + findProtocolSelfReferences(proto, funcTy->getResult(), position); + if (resultInfo.selfRef == SelfReferencePosition::Covariant) { + resultInfo.hasCovariantSelfResult = true; + } + return inputInfo |= resultInfo; } // Metatypes preserve variance. if (auto metaTy = type->getAs()) { return findProtocolSelfReferences(proto, metaTy->getInstanceType(), - skipAssocTypes); + position); } // Optionals preserve variance. if (auto optType = type->getOptionalObjectType()) { - return findProtocolSelfReferences(proto, optType, - skipAssocTypes); + return findProtocolSelfReferences(proto, optType, position); } // DynamicSelfType preserves variance. // FIXME: This shouldn't ever appear in protocol requirement // signatures. if (auto selfType = type->getAs()) { - return findProtocolSelfReferences(proto, selfType->getSelfType(), - skipAssocTypes); + return findProtocolSelfReferences(proto, selfType->getSelfType(), position); } // Bound generic types are invariant. if (auto boundGenericType = type->getAs()) { + auto info = SelfReferenceInfo(); for (auto paramType : boundGenericType->getGenericArgs()) { - if (findProtocolSelfReferences(proto, paramType, - skipAssocTypes)) { - return SelfReferenceKind::Other(); - } + info |= findProtocolSelfReferences(proto, paramType, + SelfReferencePosition::Invariant); } + + return info; } - // A direct reference to 'Self' is covariant. + // A direct reference to 'Self'. if (proto->getSelfInterfaceType()->isEqual(type)) - return SelfReferenceKind::Result(); + return SelfReferenceInfo::forSelfRef(position); - // Special handling for associated types. - if (!skipAssocTypes && type->is()) { + // A reference to an associated type rooted on 'Self'. + if (type->is()) { type = type->getRootGenericParam(); if (proto->getSelfInterfaceType()->isEqual(type)) - return SelfReferenceKind::Other(); + return SelfReferenceInfo::forAssocTypeRef(position); } - return SelfReferenceKind::None(); + return SelfReferenceInfo(); } /// Find Self references within the given requirement. -SelfReferenceKind -ProtocolDecl::findProtocolSelfReferences(const ValueDecl *value, - bool allowCovariantParameters, - bool skipAssocTypes) const { +SelfReferenceInfo ProtocolDecl::findProtocolSelfReferences( + const ValueDecl *value, bool treatNonResultCovariantSelfAsInvariant) const { // Types never refer to 'Self'. if (isa(value)) - return SelfReferenceKind::None(); + return SelfReferenceInfo(); auto type = value->getInterfaceType(); // Skip invalid declarations. if (type->hasError()) - return SelfReferenceKind::None(); + return SelfReferenceInfo(); - if (auto func = dyn_cast(value)) { - // Skip the 'self' parameter. - type = type->castTo()->getResult(); + if (isa(value) || isa(value)) { + // For a method, skip the 'self' parameter. + if (isa(value)) + type = type->castTo()->getResult(); - // Methods of non-final classes can only contain a covariant 'Self' - // as a function result type. - if (!allowCovariantParameters) { - auto inputKind = SelfReferenceKind::None(); - for (auto param : type->castTo()->getParams()) { - // inout parameters are invariant. - if (param.isInOut()) { - if (::findProtocolSelfReferences(this, param.getPlainType(), - skipAssocTypes)) { - return SelfReferenceKind::Other(); - } - } - inputKind |= ::findProtocolSelfReferences(this, param.getParameterType(), - skipAssocTypes); + auto inputInfo = SelfReferenceInfo(); + for (auto param : type->castTo()->getParams()) { + // inout parameters are invariant. + if (param.isInOut()) { + inputInfo |= ::findProtocolSelfReferences( + this, param.getPlainType(), SelfReferencePosition::Invariant); + continue; } + inputInfo |= ::findProtocolSelfReferences( + this, param.getParameterType(), SelfReferencePosition::Contravariant); + } + + // A covariant Self result inside a parameter will not be bona fide. + inputInfo.hasCovariantSelfResult = false; - if (inputKind.parameter) - return SelfReferenceKind::Other(); + // FIXME: Rather than having a special flag for the is-inheritable check, + // ensure non-result covariant Self is always diagnosed during type + // resolution. + // + // Methods of non-final classes can only contain a covariant 'Self' + // as their result type. + if (treatNonResultCovariantSelfAsInvariant && + inputInfo.selfRef == SelfReferencePosition::Covariant) { + inputInfo.selfRef = SelfReferencePosition::Invariant; } - return ::findProtocolSelfReferences(this, type, - skipAssocTypes); + auto resultInfo = ::findProtocolSelfReferences( + this, type->castTo()->getResult(), + SelfReferencePosition::Covariant); + if (resultInfo.selfRef == SelfReferencePosition::Covariant) { + resultInfo.hasCovariantSelfResult = true; + } + + return inputInfo |= resultInfo; } else { - assert(isa(value)); + assert(isa(value)); - return ::findProtocolSelfReferences(this, type, - skipAssocTypes); + auto info = ::findProtocolSelfReferences(this, type, + SelfReferencePosition::Covariant); + if (info.selfRef == SelfReferencePosition::Covariant) { + info.hasCovariantSelfResult = true; + } + + return info; } - return SelfReferenceKind::None(); + return SelfReferenceInfo(); } bool ProtocolDecl::isAvailableInExistential(const ValueDecl *decl) const { - // If the member type uses 'Self' in non-covariant position, - // we cannot use the existential type. - auto selfKind = findProtocolSelfReferences(decl, - /*allowCovariantParameters=*/true, - /*skipAssocTypes=*/false); - if (selfKind.parameter || selfKind.other) + // If the member type references 'Self' in non-covariant position, or an + // associated type in any position, we cannot use the existential type. + const auto info = findProtocolSelfReferences( + decl, /*treatNonResultCovariantSelfAsInvariant=*/false); + if (info.selfRef > SelfReferencePosition::Covariant || info.assocTypeRef) { return false; + } // FIXME: Appropriately diagnose assignments instead. if (auto *const storageDecl = dyn_cast(decl)) { - if (selfKind.result && storageDecl->supportsMutation()) + if (info.hasCovariantSelfResult && storageDecl->supportsMutation()) return false; } diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index fcb261c6dc01c..7e8856ab29a00 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -913,10 +913,9 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache, // defining a non-final class conforming to 'Collection' which uses // the default witness for 'Collection.Iterator', which is defined // as 'IndexingIterator'. - auto selfKind = proto->findProtocolSelfReferences(req, - /*allowCovariantParameters=*/false, - /*skipAssocTypes=*/false); - if (!selfKind.other) { + const auto selfRefInfo = proto->findProtocolSelfReferences( + req, /*treatNonResultCovariantSelfAsInvariant=*/true); + if (!selfRefInfo.assocTypeRef) { covariantSelf = classDecl; } } @@ -3482,11 +3481,10 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement, // Check whether this requirement uses Self in a way that might // prevent conformance from succeeding. - auto selfKind = Proto->findProtocolSelfReferences(requirement, - /*allowCovariantParameters=*/false, - /*skipAssocTypes=*/true); + const auto selfRefInfo = Proto->findProtocolSelfReferences( + requirement, /*treatNonResultCovariantSelfAsInvariant=*/true); - if (selfKind.other) { + if (selfRefInfo.selfRef == SelfReferencePosition::Invariant) { // References to Self in a position where subclasses cannot do // the right thing. Complain if the adoptee is a non-final // class. @@ -3501,7 +3499,7 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement, conformance->getType()); emitDeclaredHereIfNeeded(diags, diagLoc, witness); }); - } else if (selfKind.result) { + } else if (selfRefInfo.hasCovariantSelfResult) { // The reference to Self occurs in the result type of a method/subscript // or the type of a property. A non-final class can satisfy this requirement // by holding onto Self accordingly. @@ -3569,12 +3567,7 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement, // associated types. if (isa(witness) || isa(witness)) { if (witness->getDeclContext()->getExtendedProtocolDecl()) { - auto selfKindWithAssocTypes = Proto->findProtocolSelfReferences( - requirement, - /*allowCovariantParameters=*/false, - /*skipAssocTypes=*/false); - if (selfKindWithAssocTypes.other && - selfKindWithAssocTypes.result) { + if (selfRefInfo.hasCovariantSelfResult && selfRefInfo.assocTypeRef) { diagnoseOrDefer(requirement, false, [witness, requirement](NormalProtocolConformance *conformance) { auto proto = conformance->getProtocol(); From 0f6611cfad28c189784c7a88865fb039b5a9b24d Mon Sep 17 00:00:00 2001 From: Eric Miotto <1094986+edymtt@users.noreply.github.com> Date: Mon, 12 Oct 2020 07:12:04 -0700 Subject: [PATCH 409/745] [build] properly install compiler-rt from Xcode toolchain (#31247) Similarly to what was done for #25547, copy the compiler-rt built-ins for embedded platforms from the Xcode toolchain into the new generated one, so to avoid link time errors. Addresses SR-12001, rdar://57837918 --- utils/build-script-impl | 112 ++++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index b4ef4e120833e..8bd7d238d9bea 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -1361,6 +1361,61 @@ function cmake_config_opt() { fi } +function copy_embedded_compiler_rt_builtins_from_darwin_host_toolchain() { + local clang_dest_dir="$1" + + HOST_CXX_DIR=$(dirname "${HOST_CXX}") + HOST_LIB_CLANG_DIR="${HOST_CXX_DIR}/../lib/clang" + DEST_LIB_CLANG_DIR="${clang_dest_dir}/lib/clang" + + [ -d "${HOST_LIB_CLANG_DIR}" -a -d "${DEST_LIB_CLANG_DIR}" ] || return 0 + + DEST_CXX_BUILTINS_VERSION=$(ls -1 "${DEST_LIB_CLANG_DIR}") + DEST_BUILTINS_DIR="${clang_dest_dir}/lib/clang/${DEST_CXX_BUILTINS_VERSION}/lib/darwin" + + if [[ -d "${DEST_BUILTINS_DIR}" ]]; then + for HOST_CXX_BUILTINS_PATH in "${HOST_LIB_CLANG_DIR}"/*; do + HOST_CXX_BUILTINS_DIR="${HOST_CXX_BUILTINS_PATH}/lib/darwin" + echo "copying compiler-rt embedded builtins from ${HOST_CXX_BUILTINS_DIR} into the local clang build directory ${DEST_BUILTINS_DIR}." + + for OS in ios watchos tvos; do + # Copy over the device .a when necessary + LIB_NAME="libclang_rt.$OS.a" + HOST_LIB_PATH="$HOST_CXX_BUILTINS_DIR/$LIB_NAME" + DEST_LIB_PATH="${DEST_BUILTINS_DIR}/${LIB_NAME}" + if [[ ! -f "${DEST_LIB_PATH}" ]]; then + if [[ -f "${HOST_LIB_PATH}" ]]; then + call cp "${HOST_LIB_PATH}" "${DEST_LIB_PATH}" + elif [[ "${VERBOSE_BUILD}" ]]; then + echo "no file exists at ${HOST_LIB_PATH}" + fi + fi + + # Copy over the simulator .a when necessary + SIM_LIB_NAME="libclang_rt.${OS}sim.a" + HOST_SIM_LIB_PATH="$HOST_CXX_BUILTINS_DIR/$SIM_LIB_NAME" + DEST_SIM_LIB_PATH="${DEST_BUILTINS_DIR}/${SIM_LIB_NAME}" + if [[ ! -f "${DEST_SIM_LIB_PATH}" ]]; then + if [[ -f "${HOST_SIM_LIB_PATH}" ]]; then + call cp "${HOST_SIM_LIB_PATH}" "${DEST_SIM_LIB_PATH}" + elif [[ -f "${HOST_LIB_PATH}" ]]; then + # The simulator .a might not exist if the host + # Xcode is old. In that case, copy over the + # device library to the simulator location to allow + # clang to find it. The device library has the simulator + # slices in Xcode that doesn't have the simulator .a, so + # the link is still valid. + echo "copying over faux-sim library ${HOST_LIB_PATH} to ${SIM_LIB_NAME}" + call cp "${HOST_LIB_PATH}" "${DEST_SIM_LIB_PATH}" + elif [[ "${VERBOSE_BUILD}" ]]; then + echo "no file exists at ${HOST_SIM_LIB_PATH}" + fi + fi + done + done + fi +} + # # Configure and build each product # @@ -2421,55 +2476,7 @@ for host in "${ALL_HOSTS[@]}"; do # builtins for iOS/tvOS/watchOS to ensure that Swift's # stdlib can use compiler-rt builtins when targetting iOS/tvOS/watchOS. if [[ "${product}" = "llvm" ]] && [[ "${BUILD_LLVM}" = "1" ]] && [[ "$(uname -s)" = "Darwin" ]]; then - HOST_CXX_DIR=$(dirname "${HOST_CXX}") - HOST_LIB_CLANG_DIR="${HOST_CXX_DIR}/../lib/clang" - DEST_LIB_CLANG_DIR="$(build_directory_bin ${host} llvm)/../lib/clang" - - if [[ -d "${HOST_LIB_CLANG_DIR}" ]] && [[ -d "${DEST_LIB_CLANG_DIR}" ]]; then - DEST_CXX_BUILTINS_VERSION=$(ls "${DEST_LIB_CLANG_DIR}" | awk '{print $0}') - DEST_BUILTINS_DIR="$(build_directory_bin ${host} llvm)/../lib/clang/$DEST_CXX_BUILTINS_VERSION/lib/darwin" - - if [[ -d "${DEST_BUILTINS_DIR}" ]]; then - for HOST_CXX_BUILTINS_PATH in "${HOST_LIB_CLANG_DIR}"/*; do - HOST_CXX_BUILTINS_DIR="${HOST_CXX_BUILTINS_PATH}/lib/darwin" - echo "copying compiler-rt embedded builtins from ${HOST_CXX_BUILTINS_DIR} into the local clang build directory ${DEST_BUILTINS_DIR}." - - for OS in ios watchos tvos; do - # Copy over the device .a when necessary - LIB_NAME="libclang_rt.$OS.a" - HOST_LIB_PATH="$HOST_CXX_BUILTINS_DIR/$LIB_NAME" - DEST_LIB_PATH="${DEST_BUILTINS_DIR}/${LIB_NAME}" - if [[ ! -f "${DEST_LIB_PATH}" ]]; then - if [[ -f "${HOST_LIB_PATH}" ]]; then - call cp "${HOST_LIB_PATH}" "${DEST_LIB_PATH}" - elif [[ "${VERBOSE_BUILD}" ]]; then - echo "no file exists at ${HOST_LIB_PATH}" - fi - fi - # Copy over the simulator .a when necessary - SIM_LIB_NAME="libclang_rt.${OS}sim.a" - HOST_SIM_LIB_PATH="$HOST_CXX_BUILTINS_DIR/$SIM_LIB_NAME" - DEST_SIM_LIB_PATH="${DEST_BUILTINS_DIR}/${SIM_LIB_NAME}" - if [[ ! -f "${DEST_SIM_LIB_PATH}" ]]; then - if [[ -f "${HOST_SIM_LIB_PATH}" ]]; then - call cp "${HOST_SIM_LIB_PATH}" "${DEST_SIM_LIB_PATH}" - elif [[ -f "${HOST_LIB_PATH}" ]]; then - # The simulator .a might not exist if the host - # Xcode is old. In that case, copy over the - # device library to the simulator location to allow - # clang to find it. The device library has the simulator - # slices in Xcode that doesn't have the simulator .a, so - # the link is still valid. - echo "copying over faux-sim library ${HOST_LIB_PATH} to ${SIM_LIB_NAME}" - call cp "${HOST_LIB_PATH}" "${DEST_SIM_LIB_PATH}" - elif [[ "${VERBOSE_BUILD}" ]]; then - echo "no file exists at ${HOST_SIM_LIB_PATH}" - fi - fi - done - done - fi - fi + copy_embedded_compiler_rt_builtins_from_darwin_host_toolchain "$(build_directory_bin ${host} llvm)/.." fi done done @@ -2990,6 +2997,13 @@ for host in "${ALL_HOSTS[@]}"; do build_dir=$(build_directory ${host} ${product}) call env DESTDIR="${host_install_destdir}" "${CMAKE_BUILD[@]}" "${build_dir}" -- ${INSTALL_TARGETS} + + # When we are installing LLVM copy over the compiler-rt + # builtins for iOS/tvOS/watchOS to ensure that we don't + # have linker errors when building apps for such platforms. + if [[ "${product}" = "llvm" ]] && [[ ! -z "${LLVM_INSTALL_COMPONENTS}" ]] && [[ "$(uname -s)" = "Darwin" ]]; then + copy_embedded_compiler_rt_builtins_from_darwin_host_toolchain "${host_install_destdir}${host_install_prefix}" + fi done done From be38df8e3d51c23294b6534db8c6bcc9de1ab1ae Mon Sep 17 00:00:00 2001 From: Eric Miotto <1094986+edymtt@users.noreply.github.com> Date: Mon, 12 Oct 2020 07:35:27 -0700 Subject: [PATCH 410/745] [build] Adjust install name for Differentiation and Concurrency (#34216) This effectively reverts #31183 -- we need to match the install name convention of the other stdlib libraries. From the review feedback: > The right way to load the stdlib & runtime libraries from a custom toolchain is to set `DYLD_LIBRARY_PATH` when executing the generated binary. This is how we run tests against the just-built libraries and this is how downloadable toolchain snapshots are currently configured in Xcode -- see #33178 --- stdlib/public/Concurrency/CMakeLists.txt | 1 - stdlib/public/Differentiation/CMakeLists.txt | 1 - 2 files changed, 2 deletions(-) diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index e63b3e475255b..92db5f25e1d0e 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -29,5 +29,4 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} -parse-stdlib LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - DARWIN_INSTALL_NAME_DIR "@rpath" INSTALL_IN_COMPONENT stdlib) diff --git a/stdlib/public/Differentiation/CMakeLists.txt b/stdlib/public/Differentiation/CMakeLists.txt index 6e0257aa3f73e..ecd1b8637f3fd 100644 --- a/stdlib/public/Differentiation/CMakeLists.txt +++ b/stdlib/public/Differentiation/CMakeLists.txt @@ -38,5 +38,4 @@ add_swift_target_library(swift_Differentiation ${SWIFT_STDLIB_LIBRARY_BUILD_TYPE ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} -parse-stdlib LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - DARWIN_INSTALL_NAME_DIR "@rpath" INSTALL_IN_COMPONENT stdlib) From 77f16471951fbcdf0bba9929b8e6d5d9228e32f6 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Mon, 12 Oct 2020 17:28:30 +0200 Subject: [PATCH 411/745] RCIdentityAnalysis: some refactoring to improve clarity. NFC. --- .../Analysis/RCIdentityAnalysis.cpp | 65 +++++++++++-------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/lib/SILOptimizer/Analysis/RCIdentityAnalysis.cpp b/lib/SILOptimizer/Analysis/RCIdentityAnalysis.cpp index fe5863f5daa11..a41a67e027ebd 100644 --- a/lib/SILOptimizer/Analysis/RCIdentityAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/RCIdentityAnalysis.cpp @@ -34,6 +34,21 @@ static bool isNoPayloadEnum(SILValue V) { return !EI->hasOperand(); } +/// Returns true if V is a valid operand of a cast which preserves RC identity. +/// +/// V is a valid operand if it has a non-trivial type. +/// RCIdentityAnalysis must not look through casts which cast from a trivial +/// type, like a metatype, to something which is retainable, like an AnyObject. +/// On some platforms such casts dynamically allocate a ref-counted box for the +/// metatype. Naturally that is the place where a new rc-identity begins. +/// Therefore such a cast is introducing a new rc identical object. +/// +/// If we would look through such a cast, ARC optimizations would get confused +/// and might eliminate a retain of such an object completely. +static bool isValidRCIdentityCastOperand(SILValue V, SILFunction *F) { + return !V->getType().isTrivial(*F); +} + /// RC identity is more than a guarantee that references refer to the same /// object. It also means that reference counting operations on those references /// have the same semantics. If the types on either side of a cast do not have @@ -42,8 +57,8 @@ static bool isNoPayloadEnum(SILValue V) { /// necessarily preserve RC identity because it may cast from a /// reference-counted type to a non-reference counted type, or from a larger to /// a smaller struct with fewer references. -static bool isRCIdentityPreservingCast(ValueKind Kind) { - switch (Kind) { +static SILValue getRCIdentityPreservingCastOperand(SILValue V) { + switch (V->getKind()) { case ValueKind::UpcastInst: case ValueKind::UncheckedRefCastInst: case ValueKind::UnconditionalCheckedCastInst: @@ -51,26 +66,17 @@ static bool isRCIdentityPreservingCast(ValueKind Kind) { case ValueKind::OpenExistentialRefInst: case ValueKind::RefToBridgeObjectInst: case ValueKind::BridgeObjectToRefInst: - case ValueKind::ConvertFunctionInst: - return true; + case ValueKind::ConvertFunctionInst: { + auto *inst = cast(V); + SILValue castOp = inst->getOperand(0); + if (isValidRCIdentityCastOperand(castOp, inst->getFunction())) + return castOp; + break; + } default: - return false; + break; } -} - -/// Returns a null SILValue if V has a trivial type, otherwise returns V. -/// -/// RCIdentityAnalysis must not look through casts which cast from a trivial -/// type, like a metatype, to something which is retainable, like an AnyObject. -/// On some platforms such casts dynamically allocate a ref-counted box for the -/// metatype. -/// Now, if the RCRoot of such an AnyObject would be a trivial type, ARC -/// optimizations get confused and might eliminate a retain of such an object -/// completely. -static SILValue noTrivialType(SILValue V, SILFunction *F) { - if (V->getType().isTrivial(*F)) - return SILValue(); - return V; + return SILValue(); } //===----------------------------------------------------------------------===// @@ -79,10 +85,8 @@ static SILValue noTrivialType(SILValue V, SILFunction *F) { static SILValue stripRCIdentityPreservingInsts(SILValue V) { // First strip off RC identity preserving casts. - if (isRCIdentityPreservingCast(V->getKind())) { - auto *inst = cast(V); - return noTrivialType(inst->getOperand(0), inst->getFunction()); - } + if (SILValue castOp = getRCIdentityPreservingCastOperand(V)) + return castOp; // Then if we have a struct_extract that is extracting a non-trivial member // from a struct with no other non-trivial members, a ref count operation on @@ -133,9 +137,14 @@ static SILValue stripRCIdentityPreservingInsts(SILValue V) { // since we will only visit it twice if we go around a back edge due to a // different SILArgument that is actually being used for its phi node like // purposes. - if (auto *A = dyn_cast(V)) - if (SILValue Result = A->getSingleTerminatorOperand()) - return noTrivialType(Result, A->getFunction()); + if (auto *A = dyn_cast(V)) { + if (SILValue Result = A->getSingleTerminatorOperand()) { + // In case the terminator is a conditional cast, Result is the source of + // the cast. + if (isValidRCIdentityCastOperand(Result, A->getFunction())) + return Result; + } + } return SILValue(); } @@ -339,7 +348,7 @@ SILValue RCIdentityFunctionInfo::stripRCIdentityPreservingArgs(SILValue V, unsigned IVListSize = IncomingValues.size(); if (IVListSize == 1 && - !noTrivialType(IncomingValues[0].second, A->getFunction())) + !isValidRCIdentityCastOperand(IncomingValues[0].second, A->getFunction())) return SILValue(); assert(IVListSize != 1 && "Should have been handled in " From 8c3081c750f2b22075e9bb2efc8ab3647996b7b8 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Mon, 12 Oct 2020 16:28:58 +0100 Subject: [PATCH 412/745] test: Remove duplicate test for -static-executable - test/Driver/static-executable-linux.swift is duplicated by test/Driver/static-executable.swift so remove it. - Additionally, test/Driver/static-executable-linux.swift uses 'file' instead of 'lvm-readelf' to detect if the executable is statically linked and this breaks on Centos7. --- test/Driver/static-executable-linux.swift | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 test/Driver/static-executable-linux.swift diff --git a/test/Driver/static-executable-linux.swift b/test/Driver/static-executable-linux.swift deleted file mode 100644 index 8f0589aeccf36..0000000000000 --- a/test/Driver/static-executable-linux.swift +++ /dev/null @@ -1,10 +0,0 @@ -// Build a static executable "hello world" program -// REQUIRES: OS=linux-gnu -// REQUIRES: static_stdlib -print("hello world!") -// RUN: %empty-directory(%t) -// RUN: %target-swiftc_driver -static-executable -o %t/static-executable %s -// RUN: %t/static-executable | %FileCheck %s -// RUN: file %t/static-executable | %FileCheck %s --check-prefix=FILE -// CHECK: hello world! -// FILE: , statically linked, From b994bf319177ed07ab84402ec6e89590cbfdd5cd Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Fri, 18 Sep 2020 13:56:21 -0700 Subject: [PATCH 413/745] Add support for `_specialize(exported: true, ...)` This attribute allows to define a pre-specialized entry point of a generic function in a library. The following definition provides a pre-specialized entry point for `genericFunc(_:)` for the parameter type `Int` that clients of the library can call. ``` @_specialize(exported: true, where T == Int) public func genericFunc(_ t: T) { ... } ``` Pre-specializations of internal `@inlinable` functions are allowed. ``` @usableFromInline internal struct GenericThing { @_specialize(exported: true, where T == Int) @inlinable internal func genericMethod(_ t: T) { } } ``` There is syntax to pre-specialize a method from a different module. ``` import ModuleDefiningGenericFunc @_specialize(exported: true, target: genericFunc(_:), where T == Double) func prespecialize_genericFunc(_ t: T) { fatalError("dont call") } ``` Specially marked extensions allow for pre-specialization of internal methods accross module boundries (respecting `@inlinable` and `@usableFromInline`). ``` import ModuleDefiningGenericThing public struct Something {} @_specializeExtension extension GenericThing { @_specialize(exported: true, target: genericMethod(_:), where T == Something) func prespecialize_genericMethod(_ t: T) { fatalError("dont call") } } ``` rdar://64993425 --- docs/ABI/Mangling.rst | 1 + include/swift/AST/Attr.def | 5 + include/swift/AST/Attr.h | 28 ++- include/swift/AST/Decl.h | 3 +- include/swift/AST/DeclContext.h | 4 + include/swift/AST/DiagnosticsParse.def | 4 + include/swift/AST/DiagnosticsSema.def | 6 + include/swift/AST/LazyResolver.h | 4 + include/swift/AST/LookupKinds.h | 3 + include/swift/AST/ModuleNameLookup.h | 5 +- include/swift/AST/NameLookup.h | 3 + include/swift/AST/NameLookupRequests.h | 13 +- include/swift/AST/TypeCheckRequests.h | 19 ++ include/swift/AST/TypeCheckerTypeIDZone.def | 3 + include/swift/Demangling/DemangleNodes.def | 1 + include/swift/Parse/Parser.h | 11 +- .../swift/SIL/GenericSpecializationMangler.h | 105 +++++++++++ include/swift/SIL/SILDeclRef.h | 7 + include/swift/SIL/SILFunction.h | 23 ++- .../swift/SILOptimizer/PassManager/Passes.def | 2 + .../swift/SILOptimizer/Utils/GenericCloner.h | 23 ++- include/swift/SILOptimizer/Utils/Generics.h | 16 +- .../Utils/SpecializationMangler.h | 61 +------ lib/AST/Attr.cpp | 43 ++++- lib/AST/Decl.cpp | 21 ++- lib/AST/DeclContext.cpp | 15 ++ lib/AST/ModuleNameLookup.cpp | 24 ++- lib/AST/NameLookup.cpp | 10 +- lib/AST/UnqualifiedLookup.cpp | 5 +- lib/ClangImporter/ImporterImpl.h | 5 + lib/Demangling/Demangler.cpp | 4 + lib/Demangling/NodePrinter.cpp | 4 + lib/Demangling/OldRemangler.cpp | 3 + lib/Demangling/Remangler.cpp | 8 + lib/Parse/ParseDecl.cpp | 40 +++-- lib/SIL/IR/SILDeclRef.cpp | 24 +++ lib/SIL/IR/SILFunction.cpp | 44 ++++- lib/SIL/IR/SILFunctionBuilder.cpp | 20 ++- lib/SIL/IR/SILModule.cpp | 3 + lib/SIL/IR/SILPrinter.cpp | 3 + lib/SIL/Parser/ParseSIL.cpp | 32 +++- lib/SIL/Utils/CMakeLists.txt | 1 + .../Utils/GenericSpecializationMangler.cpp | 137 ++++++++++++++ lib/SILGen/SILGen.cpp | 41 +++++ lib/SILGen/SILGen.h | 2 +- .../IPO/DeadFunctionElimination.cpp | 10 ++ lib/SILOptimizer/PassManager/PassPipeline.cpp | 3 + .../Transforms/EagerSpecializer.cpp | 83 ++++++--- .../Transforms/GenericSpecializer.cpp | 2 +- .../Transforms/PerformanceInliner.cpp | 8 + lib/SILOptimizer/Utils/GenericCloner.cpp | 7 +- lib/SILOptimizer/Utils/Generics.cpp | 82 ++++++++- .../Utils/SpecializationMangler.cpp | 82 +-------- lib/Sema/ImportResolution.cpp | 3 +- lib/Sema/TypeCheckAccess.cpp | 8 +- lib/Sema/TypeCheckAttr.cpp | 114 +++++++++--- lib/Sema/TypeCheckDecl.cpp | 9 +- lib/Sema/TypeCheckDeclOverride.cpp | 1 + lib/Sema/TypeCheckGeneric.cpp | 5 +- lib/Sema/TypeCheckNameLookup.cpp | 4 + lib/Sema/TypeCheckType.cpp | 17 +- lib/Sema/TypeCheckType.h | 3 + lib/Sema/TypeChecker.h | 2 + lib/Serialization/Deserialization.cpp | 32 +++- lib/Serialization/DeserializeSIL.cpp | 22 ++- lib/Serialization/ModuleFile.h | 2 + lib/Serialization/ModuleFormat.h | 7 +- lib/Serialization/SILFormat.h | 3 +- lib/Serialization/Serialization.cpp | 27 ++- lib/Serialization/SerializeSIL.cpp | 8 +- lib/TBDGen/TBDGen.cpp | 13 ++ test/IRGen/Inputs/pre_specialize_module.swift | 112 ++++++++++++ .../Inputs/pre_specialize_module_B.swift | 68 +++++++ test/IRGen/pre_specialize.swift | 170 ++++++++++++++++++ .../Inputs/specialize_attr_module.swift | 102 +++++++++++ .../Inputs/specialize_attr_module2.swift | 37 ++++ test/SILGen/specialize_attr.swift | 163 ++++++++++++++++- .../Inputs/pre_specialized_module.swift | 96 ++++++++++ test/SILOptimizer/pre_specialize.swift | 90 ++++++++++ test/TBD/specialize_verify.swift | 2 +- test/attr/attr_specialize.swift | 46 +++-- .../lib/SwiftLang/SwiftSourceDocInfo.cpp | 3 +- tools/swift-demangle/swift-demangle.cpp | 1 + utils/gyb_syntax_support/AttributeNodes.py | 23 +++ .../NodeSerializationCodes.py | 1 + 85 files changed, 1978 insertions(+), 332 deletions(-) create mode 100644 include/swift/SIL/GenericSpecializationMangler.h create mode 100644 lib/SIL/Utils/GenericSpecializationMangler.cpp create mode 100644 test/IRGen/Inputs/pre_specialize_module.swift create mode 100644 test/IRGen/Inputs/pre_specialize_module_B.swift create mode 100644 test/IRGen/pre_specialize.swift create mode 100644 test/SILGen/Inputs/specialize_attr_module.swift create mode 100644 test/SILGen/Inputs/specialize_attr_module2.swift create mode 100644 test/SILOptimizer/Inputs/pre_specialized_module.swift create mode 100644 test/SILOptimizer/pre_specialize.swift diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index bc0b78d5c1edf..2f682c97dd5c9 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -995,6 +995,7 @@ Function Specializations :: specialization ::= type '_' type* 'Tg' SPEC-INFO // Generic re-abstracted specialization + specialization ::= type '_' type* 'Ts' SPEC-INFO // Generic re-abstracted prespecialization specialization ::= type '_' type* 'TG' SPEC-INFO // Generic not re-abstracted specialization specialization ::= type '_' type* 'Ti' SPEC-INFO // Inlined function with generic substitutions. diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 5f2c0a39d92a3..e174d143c82cc 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -584,6 +584,11 @@ SIMPLE_DECL_ATTR(globalActor, GlobalActor, APIStableToAdd | APIBreakingToRemove, 104) +SIMPLE_DECL_ATTR(_specializeExtension, SpecializeExtension, + OnExtension | UserInaccessible | + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + 105) + #undef TYPE_ATTR #undef DECL_ATTR_ALIAS #undef CONTEXTUAL_DECL_ATTR_ALIAS diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 1bc8d90dbe501..f5fee5fa36c12 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -1409,6 +1409,8 @@ class SynthesizedProtocolAttr : public DeclAttribute { /// The @_specialize attribute, which forces specialization on the specified /// type list. class SpecializeAttr : public DeclAttribute { + friend class SpecializeAttrTargetDeclRequest; + public: // NOTE: When adding new kinds, you must update the inline bitfield macro. enum class SpecializationKind { @@ -1420,18 +1422,35 @@ class SpecializeAttr : public DeclAttribute { TrailingWhereClause *trailingWhereClause; GenericSignature specializedSignature; + DeclNameRef targetFunctionName; + LazyMemberLoader *resolver = nullptr; + uint64_t resolverContextData; + SpecializeAttr(SourceLoc atLoc, SourceRange Range, TrailingWhereClause *clause, bool exported, SpecializationKind kind, - GenericSignature specializedSignature); + GenericSignature specializedSignature, + DeclNameRef targetFunctionName); public: static SpecializeAttr *create(ASTContext &Ctx, SourceLoc atLoc, SourceRange Range, TrailingWhereClause *clause, bool exported, SpecializationKind kind, + DeclNameRef targetFunctionName, GenericSignature specializedSignature = nullptr); + static SpecializeAttr *create(ASTContext &ctx, bool exported, + SpecializationKind kind, + GenericSignature specializedSignature, + DeclNameRef replacedFunction); + + static SpecializeAttr *create(ASTContext &ctx, bool exported, + SpecializationKind kind, + GenericSignature specializedSignature, + DeclNameRef replacedFunction, + LazyMemberLoader *resolver, uint64_t data); + TrailingWhereClause *getTrailingWhereClause() const; GenericSignature getSpecializedSignature() const { @@ -1458,6 +1477,13 @@ class SpecializeAttr : public DeclAttribute { return getSpecializationKind() == SpecializationKind::Partial; } + DeclNameRef getTargetFunctionName() const { + return targetFunctionName; + } + + /// \p forDecl is the value decl that the attribute belongs to. + ValueDecl *getTargetFunctionDecl(const ValueDecl *forDecl) const; + static bool classof(const DeclAttribute *DA) { return DA->getKind() == DAK_Specialize; } diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 0c06182cc3dc9..343521e51d805 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -2187,7 +2187,8 @@ class ValueDecl : public Decl { /// implementations for the requirements of a public protocol, even when /// the default implementations are not visible to name lookup. bool isAccessibleFrom(const DeclContext *DC, - bool forConformance = false) const; + bool forConformance = false, + bool includeInlineable = false) const; /// Returns whether this declaration should be treated as \c open from /// \p useDC. This is very similar to #getFormalAccess, but takes diff --git a/include/swift/AST/DeclContext.h b/include/swift/AST/DeclContext.h index c6ef79874d2b9..904a292653f92 100644 --- a/include/swift/AST/DeclContext.h +++ b/include/swift/AST/DeclContext.h @@ -490,6 +490,10 @@ class alignas(1 << DeclContextAlignInBits) DeclContext { /// Determine whether the innermost context is generic. bool isInnermostContextGeneric() const; + /// Determine whether this or any parent context is a `@_specialize` extension + /// context. + bool isInSpecializeExtensionContext() const; + /// Get the most optimal resilience expansion for code in this context. /// If the body is able to be inlined into functions in other resilience /// domains, this ensures that only sufficiently-conservative access patterns diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index e1e1de631d027..392241cf039a2 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -626,6 +626,8 @@ ERROR(expected_sil_function_type, none, "sil function expected to have SIL function type", ()) ERROR(sil_dynamically_replaced_func_not_found,none, "dynamically replaced function not found %0", (Identifier)) +ERROR(sil_specialize_target_func_not_found,none, + "_specialize target function not found %0", (Identifier)) ERROR(sil_availability_expected_version,none, "expected version number in 'available' attribute", ()) @@ -1579,6 +1581,8 @@ ERROR(attr_specialize_parameter_already_defined,none, ERROR(attr_specialize_expected_partial_or_full,none, "expected 'partial' or 'full' as values of the 'kind' parameter in '_specialize' attribute", ()) +ERROR(attr_specialize_expected_function,none, + "expected a function name as the value of the 'target' parameter in '_specialize' attribute", ()) // _implements ERROR(attr_implements_expected_member_name,PointsToFirstBadToken, diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 13272e13a4858..fe1bba17b162a 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5113,6 +5113,12 @@ ERROR(specialize_attr_missing_constraint,none, "Missing constraint for %0 in '_specialize' attribute", (DeclName)) ERROR(specialize_attr_unsupported_kind_of_req,none, "Only same-type and layout requirements are supported by '_specialize' attribute", ()) +ERROR(specialize_target_function_not_found, none, + "target function %0 could not be found", (DeclNameRef)) +ERROR(specialize_target_function_of_type_not_found, none, + "target function %0 of type %1 could not be found", (DeclNameRef, Type)) +NOTE(specialize_found_function_of_type, none, + "found function %0 of type %1", (DeclName, Type)) //------------------------------------------------------------------------------ // MARK: Variable usage diagnostics diff --git a/include/swift/AST/LazyResolver.h b/include/swift/AST/LazyResolver.h index ee6e64ff25ac8..97632e4cbe45c 100644 --- a/include/swift/AST/LazyResolver.h +++ b/include/swift/AST/LazyResolver.h @@ -114,6 +114,10 @@ class alignas(void*) LazyMemberLoader { /// Returns the type for a given @_typeEraser() attribute. virtual Type loadTypeEraserType(const TypeEraserAttr *TRA, uint64_t contextData) = 0; + + // Returns the target parameter of the `@_specialize` attribute or null. + virtual ValueDecl *loadTargetFunctionDecl(const SpecializeAttr *attr, + uint64_t contextData) = 0; }; /// A class that can lazily load conformances from a serialized format. diff --git a/include/swift/AST/LookupKinds.h b/include/swift/AST/LookupKinds.h index 67d45bf74c3f5..ec0b1bbb96ccd 100644 --- a/include/swift/AST/LookupKinds.h +++ b/include/swift/AST/LookupKinds.h @@ -50,6 +50,9 @@ enum NLOptions : unsigned { /// Include synonyms declared with @_implements() NL_IncludeAttributeImplements = 1 << 5, + // Include @usableFromInline and @inlinable + NL_IncludeUsableFromInlineAndInlineable = 1 << 6, + /// The default set of options used for qualified name lookup. /// /// FIXME: Eventually, add NL_ProtocolMembers to this, once all of the diff --git a/include/swift/AST/ModuleNameLookup.h b/include/swift/AST/ModuleNameLookup.h index 54a791980bbce..6ec53e6c17887 100644 --- a/include/swift/AST/ModuleNameLookup.h +++ b/include/swift/AST/ModuleNameLookup.h @@ -56,10 +56,13 @@ void simple_display(llvm::raw_ostream &out, ResolutionKind kind); /// \param moduleScopeContext The top-level context from which the lookup is /// being performed, for checking access. This must be either a /// FileUnit or a Module. +/// \param options name lookup options. Currently only used to communicate the +/// NL_IncludeUsableFromInlineAndInlineable option. void lookupInModule(const DeclContext *moduleOrFile, DeclName name, SmallVectorImpl &decls, NLKind lookupKind, ResolutionKind resolutionKind, - const DeclContext *moduleScopeContext); + const DeclContext *moduleScopeContext, + NLOptions options); /// Performs a qualified lookup into the given module and, if necessary, its /// reexports, observing proper shadowing rules. diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index f9a6df52039a5..c4ad80ca196b5 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -225,6 +225,9 @@ enum class UnqualifiedLookupFlags { /// This lookup should include results from outside the innermost scope with /// results. IncludeOuterResults = 1 << 4, + // This lookup should include results that are @inlinable or + // @usableFromInline. + IncludeInlineableAndUsableFromInline = 1 << 5, }; using UnqualifiedLookupOptions = OptionSet; diff --git a/include/swift/AST/NameLookupRequests.h b/include/swift/AST/NameLookupRequests.h index 7ed09f1e10b87..778b7c3e3c49f 100644 --- a/include/swift/AST/NameLookupRequests.h +++ b/include/swift/AST/NameLookupRequests.h @@ -437,11 +437,12 @@ using QualifiedLookupResult = SmallVector; /// Performs a lookup into a given module and its imports. class LookupInModuleRequest - : public SimpleRequest { + : public SimpleRequest< + LookupInModuleRequest, + QualifiedLookupResult(const DeclContext *, DeclName, NLKind, + namelookup::ResolutionKind, const DeclContext *, + NLOptions), + RequestFlags::Uncached | RequestFlags::DependencySink> { public: using SimpleRequest::SimpleRequest; @@ -452,7 +453,7 @@ class LookupInModuleRequest QualifiedLookupResult evaluate(Evaluator &evaluator, const DeclContext *moduleOrFile, DeclName name, NLKind lookupKind, namelookup::ResolutionKind resolutionKind, - const DeclContext *moduleScopeContext) const; + const DeclContext *moduleScopeContext, NLOptions options) const; public: // Incremental dependencies diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index faaa1fc11ccd2..30d3c85f8fbb2 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -2177,6 +2177,25 @@ class DynamicallyReplacedDeclRequest bool isCached() const { return true; } }; +class SpecializeAttrTargetDeclRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + ValueDecl *evaluate(Evaluator &evaluator, const ValueDecl *vd, + SpecializeAttr *attr) const; + +public: + // Caching. + bool isCached() const { return true; } +}; + class TypeCheckSourceFileRequest : public SimpleRequest< TypeCheckSourceFileRequest, evaluator::SideEffect(SourceFile *), diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index fb3b1ca2f3ed0..b5a33543cfb06 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -67,6 +67,9 @@ SWIFT_REQUEST(TypeChecker, DynamicallyReplacedDeclRequest, Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, SemanticMembersRequest, ArrayRef(IterableDeclContext *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, SpecializeAttrTargetDeclRequest, + ValueDecl *(const ValueDecl *, SpecializeAttr *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, EnumRawValuesRequest, evaluator::SideEffect (EnumDecl *, TypeResolutionStage), SeparatelyCached, NoLocationInfo) diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 1c3c612f9e98a..99d5141437f60 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -104,6 +104,7 @@ NODE(ResilientProtocolWitnessTable) NODE(GenericSpecialization) NODE(GenericSpecializationNotReAbstracted) NODE(GenericSpecializationParam) +NODE(GenericSpecializationPrespecialized) NODE(InlinedGenericFunction) NODE(GenericTypeMetadataPattern) CONTEXT_NODE(Getter) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 224e5db97a524..5118e6c00bd22 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1014,14 +1014,19 @@ class Parser { /// Parse the @_specialize attribute. /// \p closingBrace is the expected closing brace, which can be either ) or ] /// \p Attr is where to store the parsed attribute - bool parseSpecializeAttribute(swift::tok ClosingBrace, SourceLoc AtLoc, - SourceLoc Loc, SpecializeAttr *&Attr); + bool parseSpecializeAttribute( + swift::tok ClosingBrace, SourceLoc AtLoc, SourceLoc Loc, + SpecializeAttr *&Attr, + llvm::function_ref parseSILTargetName = [](Parser &) { + return false; + }); /// Parse the arguments inside the @_specialize attribute bool parseSpecializeAttributeArguments( swift::tok ClosingBrace, bool &DiscardAttribute, Optional &Exported, Optional &Kind, - TrailingWhereClause *&TrailingWhereClause); + TrailingWhereClause *&TrailingWhereClause, DeclNameRef &targetFunction, + llvm::function_ref parseSILTargetName); /// Parse the @_implements attribute. /// \p Attr is where to store the parsed attribute diff --git a/include/swift/SIL/GenericSpecializationMangler.h b/include/swift/SIL/GenericSpecializationMangler.h new file mode 100644 index 0000000000000..9af0dc34b46d5 --- /dev/null +++ b/include/swift/SIL/GenericSpecializationMangler.h @@ -0,0 +1,105 @@ +//===- GenericSpecializationMangler.h - generic specializations -*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SIL_UTILS_GENERICSPECIALIZATIONMANGLER_H +#define SWIFT_SIL_UTILS_GENERICSPECIALIZATIONMANGLER_H + +#include "swift/AST/ASTMangler.h" +#include "swift/Basic/NullablePtr.h" +#include "swift/Demangling/Demangler.h" +#include "swift/SIL/SILFunction.h" + +namespace swift { +namespace Mangle { + +enum class SpecializationKind : uint8_t { + Generic, + NotReAbstractedGeneric, + FunctionSignature, +}; + +/// Inject SpecializationPass into the Mangle namespace. +using SpecializationPass = Demangle::SpecializationPass; + +/// The base class for specialization mangles. +class SpecializationMangler : public Mangle::ASTMangler { +protected: + /// The specialization pass. + SpecializationPass Pass; + + IsSerialized_t Serialized; + + /// The original function which is specialized. + SILFunction *Function; + std::string FunctionName; + + llvm::SmallVector ArgOpStorage; + llvm::raw_svector_ostream ArgOpBuffer; + +protected: + SpecializationMangler(SpecializationPass P, IsSerialized_t Serialized, + SILFunction *F) + : Pass(P), Serialized(Serialized), Function(F), + ArgOpBuffer(ArgOpStorage) {} + + SpecializationMangler(SpecializationPass P, IsSerialized_t Serialized, + std::string functionName) + : Pass(P), Serialized(Serialized), Function(nullptr), + FunctionName(functionName), ArgOpBuffer(ArgOpStorage) {} + + void beginMangling(); + + /// Finish the mangling of the symbol and return the mangled name. + std::string finalize(); + + void appendSpecializationOperator(StringRef Op) { + appendOperator(Op, StringRef(ArgOpStorage.data(), ArgOpStorage.size())); + } +}; + +// The mangler for specialized generic functions. +class GenericSpecializationMangler : public SpecializationMangler { + + SubstitutionMap SubMap; + bool isReAbstracted; + bool isInlined; + bool isPrespecializaton; + +public: + GenericSpecializationMangler(SILFunction *F, SubstitutionMap SubMap, + IsSerialized_t Serialized, bool isReAbstracted, + bool isInlined = false, + bool isPrespecializaton = false) + : SpecializationMangler(SpecializationPass::GenericSpecializer, + Serialized, F), + SubMap(SubMap), isReAbstracted(isReAbstracted), isInlined(isInlined), + isPrespecializaton(isPrespecializaton) {} + + GenericSpecializationMangler(std::string origFuncName, SubstitutionMap SubMap) + : SpecializationMangler(SpecializationPass::GenericSpecializer, + IsNotSerialized, origFuncName), + SubMap(SubMap), isReAbstracted(true), isInlined(false), + isPrespecializaton(true) {} + + std::string mangle(GenericSignature Sig = GenericSignature()); + + // TODO: This utility should move from the libswiftSILOptimizer to + // libswiftSIL. + static std::string manglePrespecialization(std::string unspecializedName, + GenericSignature genericSig, + GenericSignature specializedSig); +}; + +} // end namespace Mangle +} // end namespace swift + +#endif diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index 434a17a1831d5..e463d857e0229 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -20,6 +20,7 @@ #define SWIFT_SIL_SILDeclRef_H #include "swift/AST/ClangNode.h" +#include "swift/AST/GenericSignature.h" #include "swift/AST/TypeAlignments.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/DenseMap.h" @@ -48,6 +49,7 @@ namespace swift { class SILLocation; enum class SILLinkage : uint8_t; class AnyFunctionRef; + class GenericSignature; /// How a method is dispatched. enum class MethodDispatch { @@ -151,6 +153,8 @@ struct SILDeclRef { /// The derivative function identifier. AutoDiffDerivativeFunctionIdentifier *derivativeFunctionIdentifier = nullptr; + GenericSignature specializedSignature; + /// Produces a null SILDeclRef. SILDeclRef() : loc(), kind(Kind::Func), isForeign(0), defaultArgIndex(0), @@ -174,6 +178,9 @@ struct SILDeclRef { /// SILDeclRef. explicit SILDeclRef(Loc loc, bool isForeign = false); + /// See above put produces a prespecialization according to the signature. + explicit SILDeclRef(Loc loc, GenericSignature prespecializationSig); + /// Produce a SIL constant for a default argument generator. static SILDeclRef getDefaultArgGenerator(Loc loc, unsigned defaultArgIndex); diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index 2831bbf36d02f..782e85c96be91 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -71,7 +71,8 @@ class SILSpecializeAttr final { static SILSpecializeAttr *create(SILModule &M, GenericSignature specializedSignature, - bool exported, SpecializationKind kind); + bool exported, SpecializationKind kind, + SILFunction *target); bool isExported() const { return exported; @@ -97,6 +98,10 @@ class SILSpecializeAttr final { return F; } + SILFunction *getTargetFunction() const { + return targetFunction; + } + void print(llvm::raw_ostream &OS) const; private: @@ -104,9 +109,10 @@ class SILSpecializeAttr final { bool exported; GenericSignature specializedSignature; SILFunction *F = nullptr; + SILFunction *targetFunction = nullptr; SILSpecializeAttr(bool exported, SpecializationKind kind, - GenericSignature specializedSignature); + GenericSignature specializedSignature, SILFunction *target); }; /// SILFunction - A function body that has been lowered to SIL. This consists of @@ -593,6 +599,9 @@ class SILFunction /// Returns true if this is a definition of a function defined in this module. bool isDefinition() const { return !isExternalDeclaration(); } + /// Returns true if there exist pre-specializations. + bool hasPrespecialization() const; + /// Get this function's linkage attribute. SILLinkage getLinkage() const { return SILLinkage(Linkage); } @@ -722,10 +731,18 @@ class SILFunction } /// Removes all specialize attributes from this function. - void clearSpecializeAttrs() { SpecializeAttrSet.clear(); } + void clearSpecializeAttrs() { + forEachSpecializeAttrTargetFunction( + [](SILFunction *targetFun) { targetFun->decrementRefCount(); }); + SpecializeAttrSet.clear(); + } void addSpecializeAttr(SILSpecializeAttr *Attr); + void removeSpecializeAttr(SILSpecializeAttr *attr); + + void forEachSpecializeAttrTargetFunction( + llvm::function_ref action); /// Get this function's optimization mode or OptimizationMode::NotSet if it is /// not set for this specific function. diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 4d41b442d5b9a..4c640d63981c9 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -161,6 +161,8 @@ PASS(DifferentiabilityWitnessDevirtualizer, "Inlines Differentiability Witnesses") PASS(EagerSpecializer, "eager-specializer", "Eager Specialization via @_specialize") +PASS(OnonePrespecializations, "onone-prespecializer", + "Pre specialization via @_specialize") PASS(EarlyCodeMotion, "early-codemotion", "Early Code Motion without Release Hoisting") PASS(EarlyInliner, "early-inline", diff --git a/include/swift/SILOptimizer/Utils/GenericCloner.h b/include/swift/SILOptimizer/Utils/GenericCloner.h index 7b5a2c424e74c..6a768210d4890 100644 --- a/include/swift/SILOptimizer/Utils/GenericCloner.h +++ b/include/swift/SILOptimizer/Utils/GenericCloner.h @@ -46,14 +46,12 @@ class GenericCloner public: friend class SILCloner; - GenericCloner(SILOptFunctionBuilder &FuncBuilder, - SILFunction *F, - const ReabstractionInfo &ReInfo, - SubstitutionMap ParamSubs, - StringRef NewName, - CloneCollector::CallbackType Callback) - : SuperTy(*initCloned(FuncBuilder, F, ReInfo, NewName), *F, - ParamSubs), FuncBuilder(FuncBuilder), ReInfo(ReInfo), Callback(Callback) { + GenericCloner(SILOptFunctionBuilder &FuncBuilder, SILFunction *F, + const ReabstractionInfo &ReInfo, SubstitutionMap ParamSubs, + StringRef NewName, CloneCollector::CallbackType Callback) + : SuperTy(*createDeclaration(FuncBuilder, F, ReInfo, NewName), *F, + ParamSubs), + FuncBuilder(FuncBuilder), ReInfo(ReInfo), Callback(Callback) { assert(F->getDebugScope()->Parent != getCloned()->getDebugScope()->Parent); } /// Clone and remap the types in \p F according to the substitution @@ -75,6 +73,11 @@ class GenericCloner void fixUp(SILFunction *calleeFunction); + static SILFunction *createDeclaration(SILOptFunctionBuilder &FuncBuilder, + SILFunction *Orig, + const ReabstractionInfo &ReInfo, + StringRef NewName); + protected: void visitTerminator(SILBasicBlock *BB); @@ -93,10 +96,6 @@ class GenericCloner } private: - static SILFunction *initCloned(SILOptFunctionBuilder &FuncBuilder, - SILFunction *Orig, - const ReabstractionInfo &ReInfo, - StringRef NewName); /// Clone the body of the function into the empty function that was created /// by initCloned. void populateCloned(); diff --git a/include/swift/SILOptimizer/Utils/Generics.h b/include/swift/SILOptimizer/Utils/Generics.h index 22af588a16532..17cb6656ccf27 100644 --- a/include/swift/SILOptimizer/Utils/Generics.h +++ b/include/swift/SILOptimizer/Utils/Generics.h @@ -123,6 +123,8 @@ class ReabstractionInfo { // It uses interface types. SubstitutionMap CallerInterfaceSubs; + bool isPrespecialization = false; + // Is the generated specialization going to be serialized? IsSerialized_t Serialized; @@ -147,8 +149,9 @@ class ReabstractionInfo { void finishPartialSpecializationPreparation( FunctionSignaturePartialSpecializer &FSPS); - ReabstractionInfo() {} public: + ReabstractionInfo() {} + /// Constructs the ReabstractionInfo for generic function \p Callee with /// substitutions \p ParamSubs. /// If specialization is not possible getSpecializedType() will return an @@ -164,7 +167,10 @@ class ReabstractionInfo { /// Constructs the ReabstractionInfo for generic function \p Callee with /// a specialization signature. ReabstractionInfo(ModuleDecl *targetModule, bool isModuleWholeModule, - SILFunction *Callee, GenericSignature SpecializedSig); + SILFunction *Callee, GenericSignature SpecializedSig, + bool isPrespecialization = false); + + bool isPrespecialized() const { return isPrespecialization; } IsSerialized_t isSerialized() const { return Serialized; @@ -306,17 +312,17 @@ class GenericFuncSpecializer { SILFunction *lookupSpecialization(); /// Return a newly created specialized function. - SILFunction *tryCreateSpecialization(); + SILFunction *tryCreateSpecialization(bool forcePrespecialization = false); /// Try to specialize GenericFunc given a list of ParamSubs. /// Returns either a new or existing specialized function, or nullptr. - SILFunction *trySpecialization() { + SILFunction *trySpecialization(bool forcePrespecialization = false) { if (!ReInfo.getSpecializedType()) return nullptr; SILFunction *SpecializedF = lookupSpecialization(); if (!SpecializedF) - SpecializedF = tryCreateSpecialization(); + SpecializedF = tryCreateSpecialization(forcePrespecialization); return SpecializedF; } diff --git a/include/swift/SILOptimizer/Utils/SpecializationMangler.h b/include/swift/SILOptimizer/Utils/SpecializationMangler.h index 6006521020b3a..24db9306381aa 100644 --- a/include/swift/SILOptimizer/Utils/SpecializationMangler.h +++ b/include/swift/SILOptimizer/Utils/SpecializationMangler.h @@ -13,6 +13,8 @@ #ifndef SWIFT_SILOPTIMIZER_UTILS_SPECIALIZATIONMANGLER_H #define SWIFT_SILOPTIMIZER_UTILS_SPECIALIZATIONMANGLER_H +#include "swift/SIL/GenericSpecializationMangler.h" + #include "swift/Demangling/Demangler.h" #include "swift/Demangling/NamespaceMacros.h" #include "swift/Basic/NullablePtr.h" @@ -24,65 +26,6 @@ namespace swift { namespace Mangle { SWIFT_BEGIN_INLINE_NAMESPACE -enum class SpecializationKind : uint8_t { - Generic, - NotReAbstractedGeneric, - FunctionSignature, -}; - -/// Inject SpecializationPass into the Mangle namespace. -using SpecializationPass = Demangle::SpecializationPass; - -/// The base class for specialization mangles. -class SpecializationMangler : public Mangle::ASTMangler { -protected: - /// The specialization pass. - SpecializationPass Pass; - - IsSerialized_t Serialized; - - /// The original function which is specialized. - SILFunction *Function; - - llvm::SmallVector ArgOpStorage; - llvm::raw_svector_ostream ArgOpBuffer; - -protected: - SpecializationMangler(SpecializationPass P, IsSerialized_t Serialized, - SILFunction *F) - : Pass(P), Serialized(Serialized), Function(F), - ArgOpBuffer(ArgOpStorage) {} - - SILFunction *getFunction() const { return Function; } - - void beginMangling(); - - /// Finish the mangling of the symbol and return the mangled name. - std::string finalize(); - - void appendSpecializationOperator(StringRef Op) { - appendOperator(Op, StringRef(ArgOpStorage.data(), ArgOpStorage.size())); - } -}; - -// The mangler for specialized generic functions. -class GenericSpecializationMangler : public SpecializationMangler { - - SubstitutionMap SubMap; - bool isReAbstracted; - bool isInlined; - -public: - GenericSpecializationMangler(SILFunction *F, SubstitutionMap SubMap, - IsSerialized_t Serialized, bool isReAbstracted, - bool isInlined = false) - : SpecializationMangler(SpecializationPass::GenericSpecializer, - Serialized, F), - SubMap(SubMap), isReAbstracted(isReAbstracted), isInlined(isInlined) {} - - std::string mangle(GenericSignature Sig = GenericSignature()); -}; - class PartialSpecializationMangler : public SpecializationMangler { CanSILFunctionType SpecializedFnTy; diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 70a1ec3429409..47223ea350caa 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -902,9 +902,11 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, auto *attr = cast(this); auto exported = attr->isExported() ? "true" : "false"; auto kind = attr->isPartialSpecialization() ? "partial" : "full"; - + auto target = attr->getTargetFunctionName(); Printer << "exported: "<< exported << ", "; Printer << "kind: " << kind << ", "; + if (target) + Printer << "target: " << target << ", "; SmallVector requirementsScratch; ArrayRef requirements; if (auto sig = attr->getSpecializedSignature()) @@ -1559,11 +1561,13 @@ SpecializeAttr::SpecializeAttr(SourceLoc atLoc, SourceRange range, TrailingWhereClause *clause, bool exported, SpecializationKind kind, - GenericSignature specializedSignature) + GenericSignature specializedSignature, + DeclNameRef targetFunctionName) : DeclAttribute(DAK_Specialize, atLoc, range, /*Implicit=*/clause == nullptr), trailingWhereClause(clause), - specializedSignature(specializedSignature) { + specializedSignature(specializedSignature), + targetFunctionName(targetFunctionName) { Bits.SpecializeAttr.exported = exported; Bits.SpecializeAttr.kind = unsigned(kind); } @@ -1577,9 +1581,40 @@ SpecializeAttr *SpecializeAttr::create(ASTContext &Ctx, SourceLoc atLoc, TrailingWhereClause *clause, bool exported, SpecializationKind kind, + DeclNameRef targetFunctionName, GenericSignature specializedSignature) { return new (Ctx) SpecializeAttr(atLoc, range, clause, exported, kind, - specializedSignature); + specializedSignature, targetFunctionName); +} + +SpecializeAttr *SpecializeAttr::create(ASTContext &ctx, bool exported, + SpecializationKind kind, + GenericSignature specializedSignature, + DeclNameRef targetFunctionName) { + return new (ctx) + SpecializeAttr(SourceLoc(), SourceRange(), nullptr, exported, kind, + specializedSignature, targetFunctionName); +} + +SpecializeAttr *SpecializeAttr::create(ASTContext &ctx, bool exported, + SpecializationKind kind, + GenericSignature specializedSignature, + DeclNameRef targetFunctionName, + LazyMemberLoader *resolver, + uint64_t data) { + auto *attr = new (ctx) + SpecializeAttr(SourceLoc(), SourceRange(), nullptr, exported, kind, + specializedSignature, targetFunctionName); + attr->resolver = resolver; + attr->resolverContextData = data; + return attr; +} + +ValueDecl * SpecializeAttr::getTargetFunctionDecl(const ValueDecl *onDecl) const { + return evaluateOrDefault(onDecl->getASTContext().evaluator, + SpecializeAttrTargetDeclRequest{ + onDecl, const_cast(this)}, + nullptr); } SPIAccessControlAttr::SPIAccessControlAttr(SourceLoc atLoc, SourceRange range, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 46b1c2c493c37..03251a6306b8d 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3295,13 +3295,14 @@ ValueDecl::getFormalAccessScope(const DeclContext *useDC, /// See ValueDecl::isAccessibleFrom for a description of \p forConformance. static bool checkAccessUsingAccessScopes(const DeclContext *useDC, const ValueDecl *VD, - AccessLevel access) { + AccessLevel access, + bool includeInlineable) { if (VD->getASTContext().isAccessControlDisabled()) return true; - AccessScope accessScope = - getAccessScopeForFormalAccess(VD, access, useDC, - /*treatUsableFromInlineAsPublic*/false); + AccessScope accessScope = getAccessScopeForFormalAccess( + VD, access, useDC, + /*treatUsableFromInlineAsPublic*/ includeInlineable); if (accessScope.getDeclContext() == useDC) return true; if (!AccessScope(useDC).isChildOf(accessScope)) return false; @@ -3324,6 +3325,7 @@ static bool checkAccessUsingAccessScopes(const DeclContext *useDC, /// See ValueDecl::isAccessibleFrom for a description of \p forConformance. static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD, bool forConformance, + bool includeInlineable, llvm::function_ref getAccessLevel) { if (VD->getASTContext().isAccessControlDisabled()) return true; @@ -3336,7 +3338,7 @@ static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD, // check declarations inside inaccessible members via slower // access scope based check, which is helpful for diagnostics. if (!(sourceDC->getSelfProtocolDecl() || VD->isOperator())) - return checkAccessUsingAccessScopes(useDC, VD, access); + return checkAccessUsingAccessScopes(useDC, VD, access, includeInlineable); if (!forConformance) { if (auto *proto = sourceDC->getSelfProtocolDecl()) { @@ -3351,7 +3353,7 @@ static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD, } // Skip the fast path below and just compare access scopes. - return checkAccessUsingAccessScopes(useDC, VD, access); + return checkAccessUsingAccessScopes(useDC, VD, access, includeInlineable); } } @@ -3391,8 +3393,9 @@ static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD, } bool ValueDecl::isAccessibleFrom(const DeclContext *useDC, - bool forConformance) const { - return checkAccess(useDC, this, forConformance, + bool forConformance, + bool includeInlineable) const { + return checkAccess(useDC, this, forConformance, includeInlineable, [&]() { return getFormalAccess(); }); } @@ -3409,7 +3412,7 @@ bool AbstractStorageDecl::isSetterAccessibleFrom(const DeclContext *DC, if (isa(this)) return true; - return checkAccess(DC, this, forConformance, + return checkAccess(DC, this, forConformance, /*includeInlineable*/ false, [&]() { return getSetterFormalAccess(); }); } diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index 8edd07131b887..e0da769881d85 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -1269,3 +1269,18 @@ void swift::simple_display(llvm::raw_ostream &out, SourceLoc swift::extractNearestSourceLoc(const IterableDeclContext *idc) { return extractNearestSourceLoc(idc->getDecl()); } + +static bool isSpecializeExtensionContext(const DeclContext *dc) { + if (dc->isModuleScopeContext()) + return false; + if (auto *extCtx = dyn_cast(dc)) { + // and has specialized attr ... + return extCtx->getAttrs().hasAttribute(); + } + auto *parentDecl = dc->getParent(); + return isSpecializeExtensionContext(parentDecl); +} + +bool DeclContext::isInSpecializeExtensionContext() const { + return isSpecializeExtensionContext(this); +} diff --git a/lib/AST/ModuleNameLookup.cpp b/lib/AST/ModuleNameLookup.cpp index 16c9d96a87c8e..44bc1f54c761b 100644 --- a/lib/AST/ModuleNameLookup.cpp +++ b/lib/AST/ModuleNameLookup.cpp @@ -53,7 +53,8 @@ class ModuleNameLookup { void lookupInModule(SmallVectorImpl &decls, const DeclContext *moduleOrFile, ImportPath::Access accessPath, - const DeclContext *moduleScopeContext); + const DeclContext *moduleScopeContext, + NLOptions options); }; @@ -124,7 +125,8 @@ void ModuleNameLookup::lookupInModule( SmallVectorImpl &decls, const DeclContext *moduleOrFile, ImportPath::Access accessPath, - const DeclContext *moduleScopeContext) { + const DeclContext *moduleScopeContext, + NLOptions options) { assert(moduleOrFile->isModuleScopeContext()); // Does the module scope have any separately-imported overlays shadowing @@ -135,7 +137,7 @@ void ModuleNameLookup::lookupInModule( if (!overlays.empty()) { // If so, look in each of those overlays. for (auto overlay : overlays) - lookupInModule(decls, overlay, accessPath, moduleScopeContext); + lookupInModule(decls, overlay, accessPath, moduleScopeContext, options); // FIXME: This may not work gracefully if more than one of these lookups // finds something. return; @@ -143,6 +145,7 @@ void ModuleNameLookup::lookupInModule( const size_t initialCount = decls.size(); size_t currentCount = decls.size(); + bool includeInlineable = options & NL_IncludeUsableFromInlineAndInlineable; auto updateNewDecls = [&](const DeclContext *moduleScopeContext) { if (decls.size() == currentCount) @@ -153,7 +156,8 @@ void ModuleNameLookup::lookupInModule( [&](ValueDecl *VD) { if (resolutionKind == ResolutionKind::TypesOnly && !isa(VD)) return true; - if (respectAccessControl && !VD->isAccessibleFrom(moduleScopeContext)) + if (respectAccessControl && + !VD->isAccessibleFrom(moduleScopeContext, false, includeInlineable)) return true; return false; }); @@ -247,13 +251,13 @@ QualifiedLookupResult LookupInModuleRequest::evaluate( Evaluator &evaluator, const DeclContext *moduleOrFile, DeclName name, NLKind lookupKind, ResolutionKind resolutionKind, - const DeclContext *moduleScopeContext) const { + const DeclContext *moduleScopeContext, NLOptions options) const { assert(moduleScopeContext->isModuleScopeContext()); QualifiedLookupResult decls; LookupByName lookup(moduleOrFile->getASTContext(), resolutionKind, name, lookupKind); - lookup.lookupInModule(decls, moduleOrFile, {}, moduleScopeContext); + lookup.lookupInModule(decls, moduleOrFile, {}, moduleScopeContext, options); return decls; } @@ -262,10 +266,11 @@ void namelookup::lookupInModule(const DeclContext *moduleOrFile, SmallVectorImpl &decls, NLKind lookupKind, ResolutionKind resolutionKind, - const DeclContext *moduleScopeContext) { + const DeclContext *moduleScopeContext, + NLOptions options) { auto &ctx = moduleOrFile->getASTContext(); LookupInModuleRequest req(moduleOrFile, name, lookupKind, resolutionKind, - moduleScopeContext); + moduleScopeContext, options); auto results = evaluateOrDefault(ctx.evaluator, req, {}); decls.append(results.begin(), results.end()); } @@ -280,7 +285,8 @@ void namelookup::lookupVisibleDeclsInModule( assert(moduleScopeContext->isModuleScopeContext()); auto &ctx = moduleOrFile->getASTContext(); LookupVisibleDecls lookup(ctx, resolutionKind, lookupKind); - lookup.lookupInModule(decls, moduleOrFile, accessPath, moduleScopeContext); + lookup.lookupInModule(decls, moduleOrFile, accessPath, moduleScopeContext, + NL_QualifiedDefault); } void namelookup::simple_display(llvm::raw_ostream &out, ResolutionKind kind) { diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index b8ee1ff3f1c8c..daf6a4c30c206 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -1502,7 +1502,8 @@ static bool isAcceptableLookupResult(const DeclContext *dc, // Check access. if (!(options & NL_IgnoreAccessControl) && !dc->getASTContext().isAccessControlDisabled()) { - return decl->isAccessibleFrom(dc); + bool allowInlinable = options & NL_IncludeUsableFromInlineAndInlineable; + return decl->isAccessibleFrom(dc, /*forConformance*/ false, allowInlinable); } return true; @@ -1778,8 +1779,8 @@ ModuleQualifiedLookupRequest::evaluate(Evaluator &eval, const DeclContext *DC, : ResolutionKind::Overloadable); auto topLevelScope = DC->getModuleScopeContext(); if (module == topLevelScope->getParentModule()) { - lookupInModule(module, member.getFullName(), decls, - NLKind::QualifiedLookup, kind, topLevelScope); + lookupInModule(module, member.getFullName(), decls, NLKind::QualifiedLookup, + kind, topLevelScope, options); } else { // Note: This is a lookup into another module. Unless we're compiling // multiple modules at once, or if the other module re-exports this one, @@ -1795,7 +1796,8 @@ ModuleQualifiedLookupRequest::evaluate(Evaluator &eval, const DeclContext *DC, return accessPath.matches(member.getFullName()); })) { lookupInModule(module, member.getFullName(), decls, - NLKind::QualifiedLookup, kind, topLevelScope); + NLKind::QualifiedLookup, kind, topLevelScope, + options); } } diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index d1267254ca94c..4738926b5793f 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -383,8 +383,11 @@ void UnqualifiedLookupFactory::addImportedResults(const DeclContext *const dc) { SmallVector CurModuleResults; auto resolutionKind = isOriginallyTypeLookup ? ResolutionKind::TypesOnly : ResolutionKind::Overloadable; + auto nlOptions = NL_UnqualifiedDefault; + if (options.contains(Flags::IncludeInlineableAndUsableFromInline)) + nlOptions |= NL_IncludeUsableFromInlineAndInlineable; lookupInModule(dc, Name.getFullName(), CurModuleResults, - NLKind::UnqualifiedLookup, resolutionKind, dc); + NLKind::UnqualifiedLookup, resolutionKind, dc, nlOptions); // Always perform name shadowing for type lookup. if (options.contains(Flags::TypeLookup)) { diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index b82f88a83852f..5b0876cb98e45 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -1300,6 +1300,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation llvm_unreachable("unimplemented for ClangImporter"); } + ValueDecl *loadTargetFunctionDecl(const SpecializeAttr *attr, + uint64_t contextData) override { + llvm_unreachable("unimplemented for ClangImporter"); + } + void loadRequirementSignature(const ProtocolDecl *decl, uint64_t contextData, SmallVectorImpl &reqs) override { llvm_unreachable("unimplemented for ClangImporter"); diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index d6a06b003f6da..a1334ca5a951e 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -103,6 +103,7 @@ bool swift::Demangle::isFunctionAttr(Node::Kind kind) { switch (kind) { case Node::Kind::FunctionSignatureSpecialization: case Node::Kind::GenericSpecialization: + case Node::Kind::GenericSpecializationPrespecialized: case Node::Kind::InlinedGenericFunction: case Node::Kind::GenericSpecializationNotReAbstracted: case Node::Kind::GenericPartialSpecialization: @@ -2268,6 +2269,9 @@ NodePointer Demangler::demangleThunkOrSpecialization() { case 'G': return demangleGenericSpecialization(Node::Kind:: GenericSpecializationNotReAbstracted); + case 's': + return demangleGenericSpecialization( + Node::Kind::GenericSpecializationPrespecialized); case 'i': return demangleGenericSpecialization(Node::Kind::InlinedGenericFunction); case'p': { diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 5287239caffc5..187c33f61f569 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -388,6 +388,7 @@ class NodePrinter { case Node::Kind::GenericSpecialization: case Node::Kind::GenericSpecializationNotReAbstracted: case Node::Kind::GenericSpecializationParam: + case Node::Kind::GenericSpecializationPrespecialized: case Node::Kind::InlinedGenericFunction: case Node::Kind::GenericTypeMetadataPattern: case Node::Kind::Getter: @@ -1389,6 +1390,9 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::GenericSpecialization: printSpecializationPrefix(Node, "generic specialization"); return nullptr; + case Node::Kind::GenericSpecializationPrespecialized: + printSpecializationPrefix(Node, "generic pre-specialization"); + return nullptr; case Node::Kind::GenericSpecializationNotReAbstracted: printSpecializationPrefix(Node, "generic not re-abstracted specialization"); return nullptr; diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index d402cc7bcbb0f..10c11716636d6 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -363,6 +363,9 @@ void Remangler::mangleGenericSpecialization(Node *node) { unreachable("unsupported"); } +void Remangler::mangleGenericSpecializationPrespecialized(Node *node) { + unreachable("unsupported"); +} void Remangler::mangleGenericSpecializationNotReAbstracted(Node *node) { unreachable("unsupported"); } diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 0b0fdec171717..a553ab024cb60 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -1289,6 +1289,9 @@ void Remangler::mangleGenericSpecialization(Node *node) { case Node::Kind::GenericSpecialization: Buffer << "Tg"; break; + case Node::Kind::GenericSpecializationPrespecialized: + Buffer << "Ts"; + break; case Node::Kind::GenericSpecializationNotReAbstracted: Buffer << "TG"; break; @@ -1305,6 +1308,10 @@ void Remangler::mangleGenericSpecialization(Node *node) { } } +void Remangler::mangleGenericSpecializationPrespecialized(Node *node) { + mangleGenericSpecialization(node); +} + void Remangler::mangleGenericSpecializationNotReAbstracted(Node *node) { mangleGenericSpecialization(node); } @@ -1340,6 +1347,7 @@ void Remangler::mangleGlobal(Node *node) { switch (Child->getKind()) { case Node::Kind::FunctionSignatureSpecialization: case Node::Kind::GenericSpecialization: + case Node::Kind::GenericSpecializationPrespecialized: case Node::Kind::GenericSpecializationNotReAbstracted: case Node::Kind::InlinedGenericFunction: case Node::Kind::GenericPartialSpecialization: diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 46b04cd8462ee..18826b1c606a4 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -581,16 +581,21 @@ ParserResult Parser::parseExtendedAvailabilitySpecList( bool Parser::parseSpecializeAttributeArguments( swift::tok ClosingBrace, bool &DiscardAttribute, Optional &Exported, Optional &Kind, - swift::TrailingWhereClause *&TrailingWhereClause) { + swift::TrailingWhereClause *&TrailingWhereClause, + DeclNameRef &targetFunction, + llvm::function_ref parseSILTargetName) { SyntaxParsingContext ContentContext(SyntaxContext, SyntaxKind::SpecializeAttributeSpecList); // Parse optional "exported" and "kind" labeled parameters. while (!Tok.is(tok::kw_where)) { - SyntaxParsingContext ArgumentContext(SyntaxContext, - SyntaxKind::LabeledSpecializeEntry); if (Tok.is(tok::identifier)) { auto ParamLabel = Tok.getText(); - if (ParamLabel != "exported" && ParamLabel != "kind") { + SyntaxParsingContext ArgumentContext( + SyntaxContext, ParamLabel == "target" + ? SyntaxKind::TargetFunctionEntry + : SyntaxKind::LabeledSpecializeEntry); + if (ParamLabel != "exported" && ParamLabel != "kind" && + ParamLabel != "target") { diagnose(Tok.getLoc(), diag::attr_specialize_unknown_parameter_name, ParamLabel); } @@ -618,9 +623,6 @@ bool Parser::parseSpecializeAttributeArguments( if (ParamLabel == "exported") { auto trueLoc = Tok.getLoc(); bool isTrue = consumeIf(tok::kw_true); - if (isTrue) { - diagnose(trueLoc, diag::attr_specialize_export_true_no_op); - } bool isFalse = consumeIf(tok::kw_false); if (!isTrue && !isFalse) { diagnose(Tok.getLoc(), diag::attr_specialize_expected_bool_value); @@ -659,6 +661,18 @@ bool Parser::parseSpecializeAttributeArguments( diag::attr_specialize_expected_partial_or_full); } } + if (ParamLabel == "target") { + if (!parseSILTargetName(*this)) { + SyntaxParsingContext ContentContext(SyntaxContext, + SyntaxKind::DeclName); + DeclNameLoc loc; + targetFunction = parseDeclNameRef( + loc, diag::attr_specialize_expected_function, + DeclNameFlag::AllowZeroArgCompoundNames | + DeclNameFlag::AllowKeywordsUsingSpecialNames | + DeclNameFlag::AllowOperators); + } + } if (!consumeIf(tok::comma)) { diagnose(Tok.getLoc(), diag::attr_specialize_missing_comma); skipUntil(tok::comma, tok::kw_where); @@ -695,8 +709,10 @@ bool Parser::parseSpecializeAttributeArguments( return true; } -bool Parser::parseSpecializeAttribute(swift::tok ClosingBrace, SourceLoc AtLoc, - SourceLoc Loc, SpecializeAttr *&Attr) { +bool Parser::parseSpecializeAttribute( + swift::tok ClosingBrace, SourceLoc AtLoc, SourceLoc Loc, + SpecializeAttr *&Attr, + llvm::function_ref parseSILTargetName) { assert(ClosingBrace == tok::r_paren || ClosingBrace == tok::r_square); SourceLoc lParenLoc = consumeToken(); @@ -708,8 +724,10 @@ bool Parser::parseSpecializeAttribute(swift::tok ClosingBrace, SourceLoc AtLoc, TrailingWhereClause *trailingWhereClause = nullptr; + DeclNameRef targetFunction; if (!parseSpecializeAttributeArguments(ClosingBrace, DiscardAttribute, - exported, kind, trailingWhereClause)) { + exported, kind, trailingWhereClause, + targetFunction, parseSILTargetName)) { return false; } @@ -738,7 +756,7 @@ bool Parser::parseSpecializeAttribute(swift::tok ClosingBrace, SourceLoc AtLoc, // Store the attribute. Attr = SpecializeAttr::create(Context, AtLoc, SourceRange(Loc, rParenLoc), trailingWhereClause, exported.getValue(), - kind.getValue()); + kind.getValue(), targetFunction); return true; } diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 89a70eb4d8cfa..d303956b23d23 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -20,6 +20,7 @@ #include "swift/ClangImporter/ClangModule.h" #include "swift/SIL/SILLinkage.h" #include "swift/SIL/SILLocation.h" +#include "swift/SILOptimizer/Utils/SpecializationMangler.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" @@ -160,6 +161,12 @@ SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign) isForeign = asForeign; } +SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, + GenericSignature prespecializedSig) + : SILDeclRef(baseLoc, false) { + specializedSignature = prespecializedSig; +} + Optional SILDeclRef::getAnyFunctionRef() const { if (auto vd = loc.dyn_cast()) { if (auto afd = dyn_cast(vd)) { @@ -223,6 +230,12 @@ bool SILDeclRef::isImplicit() const { } SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const { + + // Prespecializations are public. + if (specializedSignature) { + return SILLinkage::Public; + } + if (getAbstractClosureExpr()) { return isSerialized() ? SILLinkage::Shared : SILLinkage::Private; } @@ -702,6 +715,17 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { } } + // Mangle prespecializations. + if (specializedSignature) { + SILDeclRef nonSpecializedDeclRef = *this; + nonSpecializedDeclRef.specializedSignature = GenericSignature(); + auto mangledNonSpecializedString = nonSpecializedDeclRef.mangle(); + auto *funcDecl = cast(getDecl()); + auto genericSig = funcDecl->getGenericSignature(); + return GenericSpecializationMangler::manglePrespecialization( + mangledNonSpecializedString, genericSig, specializedSignature); + } + ASTMangler::SymbolKind SKind = ASTMangler::SymbolKind::Default; switch (MKind) { case SILDeclRef::ManglingKind::Default: diff --git a/lib/SIL/IR/SILFunction.cpp b/lib/SIL/IR/SILFunction.cpp index 94a57b596f95c..4aa3d857b380a 100644 --- a/lib/SIL/IR/SILFunction.cpp +++ b/lib/SIL/IR/SILFunction.cpp @@ -32,15 +32,21 @@ using namespace swift; using namespace Lowering; SILSpecializeAttr::SILSpecializeAttr(bool exported, SpecializationKind kind, - GenericSignature specializedSig) - : kind(kind), exported(exported), specializedSignature(specializedSig) { } + GenericSignature specializedSig, + SILFunction *target) + : kind(kind), exported(exported), specializedSignature(specializedSig), + targetFunction(target) { + if (targetFunction) + targetFunction->incrementRefCount(); + } SILSpecializeAttr *SILSpecializeAttr::create(SILModule &M, GenericSignature specializedSig, bool exported, - SpecializationKind kind) { + SpecializationKind kind, + SILFunction *target) { void *buf = M.allocate(sizeof(SILSpecializeAttr), alignof(SILSpecializeAttr)); - return ::new (buf) SILSpecializeAttr(exported, kind, specializedSig); + return ::new (buf) SILSpecializeAttr(exported, kind, specializedSig, target); } void SILFunction::addSpecializeAttr(SILSpecializeAttr *Attr) { @@ -50,6 +56,19 @@ void SILFunction::addSpecializeAttr(SILSpecializeAttr *Attr) { } } +void SILFunction::removeSpecializeAttr(SILSpecializeAttr *attr) { + // Drop the reference to the _specialize(target:) function. + if (auto *targetFun = attr->getTargetFunction()) { + targetFun->decrementRefCount(); + } + SpecializeAttrSet.erase(std::remove_if(SpecializeAttrSet.begin(), + SpecializeAttrSet.end(), + [attr](SILSpecializeAttr *member) { + return member == attr; + }), + SpecializeAttrSet.end()); +} + SILFunction * SILFunction::create(SILModule &M, SILLinkage linkage, StringRef name, CanSILFunctionType loweredType, @@ -689,3 +708,20 @@ const UnifiedStatsReporter::TraceFormatter* FrontendStatsTracer::getTraceFormatter() { return &TF; } + +bool SILFunction::hasPrespecialization() const { + for (auto *attr : getSpecializeAttrs()) { + if (attr->isExported()) + return true; + } + return false; +} + +void SILFunction::forEachSpecializeAttrTargetFunction( + llvm::function_ref action) { + for (auto *attr : getSpecializeAttrs()) { + if (auto *f = attr->getTargetFunction()) { + action(f); + } + } +} diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index 0ec7e74dbb4d2..b53b9d4f20d8e 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -16,8 +16,7 @@ using namespace swift; SILFunction *SILFunctionBuilder::getOrCreateFunction( - SILLocation loc, StringRef name, SILLinkage linkage, - CanSILFunctionType type, IsBare_t isBareSILFunction, + SILLocation loc, StringRef name, SILLinkage linkage, CanSILFunctionType type, IsBare_t isBareSILFunction, IsTransparent_t isTransparent, IsSerialized_t isSerialized, IsDynamicallyReplaceable_t isDynamic, ProfileCounter entryCount, IsThunk_t isThunk, SubclassScope subclassScope) { @@ -53,9 +52,20 @@ void SILFunctionBuilder::addFunctionAttributes( SA->getSpecializationKind() == SpecializeAttr::SpecializationKind::Full ? SILSpecializeAttr::SpecializationKind::Full : SILSpecializeAttr::SpecializationKind::Partial; - F->addSpecializeAttr( - SILSpecializeAttr::create(M, SA->getSpecializedSignature(), - SA->isExported(), kind)); + assert(!constant.isNull()); + SILFunction *targetFunction = nullptr; + auto *attributedFuncDecl = constant.getDecl(); + auto *targetFunctionDecl = SA->getTargetFunctionDecl(attributedFuncDecl); + if (targetFunctionDecl) { + SILDeclRef declRef(targetFunctionDecl, constant.kind, false); + targetFunction = getOrCreateDeclaration(targetFunctionDecl, declRef); + F->addSpecializeAttr( + SILSpecializeAttr::create(M, SA->getSpecializedSignature(), + SA->isExported(), kind, targetFunction)); + } else { + F->addSpecializeAttr(SILSpecializeAttr::create( + M, SA->getSpecializedSignature(), SA->isExported(), kind, nullptr)); + } } if (auto *OA = Attrs.getAttribute()) { diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 2966cb89e7ba7..3c15b8f2ec1d3 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -133,6 +133,7 @@ SILModule::~SILModule() { for (SILFunction &F : *this) { F.dropAllReferences(); F.dropDynamicallyReplacedFunction(); + F.clearSpecializeAttrs(); } } @@ -516,6 +517,8 @@ void SILModule::eraseFunction(SILFunction *F) { F->dropAllReferences(); F->dropDynamicallyReplacedFunction(); F->getBlocks().clear(); + // Drop references for any _specialize(target:) functions. + F->clearSpecializeAttrs(); } void SILModule::invalidateFunctionInSILCache(SILFunction *F) { diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index f08d0cfc025f3..faaa7bdc6d253 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -3482,6 +3482,9 @@ void SILSpecializeAttr::print(llvm::raw_ostream &OS) const { requirements = specializedSig->getRequirements(); } } + if (targetFunction) { + OS << "target: \"" << targetFunction->getName() << "\", "; + } if (!requirements.empty()) { OS << "where "; SILFunction *F = getFunction(); diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 1d093aa9e7132..0059f912e8683 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -136,6 +136,7 @@ namespace { ArrayRef requirements; bool exported; SILSpecializeAttr::SpecializationKind kind; + SILFunction *target = nullptr; }; class SILParser { @@ -1050,10 +1051,32 @@ static bool parseDeclSILOptional(bool *isTransparent, SpecAttr.exported = false; SpecAttr.kind = SILSpecializeAttr::SpecializationKind::Full; SpecializeAttr *Attr; + StringRef targetFunctionName; + + if (!SP.P.parseSpecializeAttribute( + tok::r_square, AtLoc, Loc, Attr, + [&targetFunctionName](Parser &P) -> bool { + if (P.Tok.getKind() != tok::string_literal) { + P.diagnose(P.Tok, diag::expected_in_attribute_list); + return true; + } + // Drop the double quotes. + targetFunctionName = P.Tok.getText().drop_front().drop_back(); - if (!SP.P.parseSpecializeAttribute(tok::r_square, AtLoc, Loc, Attr)) - return true; - + P.consumeToken(tok::string_literal); + return true; + })) + return true; + SILFunction *targetFunction = nullptr; + if (!targetFunctionName.empty()) { + targetFunction = M.lookUpFunction(targetFunctionName.str()); + if (!targetFunction) { + Identifier Id = SP.P.Context.getIdentifier(targetFunctionName); + SP.P.diagnose(SP.P.Tok, diag::sil_specialize_target_func_not_found, + Id); + return true; + } + } // Convert SpecializeAttr into ParsedSpecAttr. SpecAttr.requirements = Attr->getTrailingWhereClause()->getRequirements(); SpecAttr.kind = Attr->getSpecializationKind() == @@ -1061,6 +1084,7 @@ static bool parseDeclSILOptional(bool *isTransparent, ? SILSpecializeAttr::SpecializationKind::Full : SILSpecializeAttr::SpecializationKind::Partial; SpecAttr.exported = Attr->isExported(); + SpecAttr.target = targetFunction; SpecAttrs->emplace_back(SpecAttr); continue; } @@ -5848,7 +5872,7 @@ bool SILParserState::parseDeclSIL(Parser &P) { GenericSignature()); FunctionState.F->addSpecializeAttr(SILSpecializeAttr::create( FunctionState.F->getModule(), genericSig, Attr.exported, - Attr.kind)); + Attr.kind, Attr.target)); } } diff --git a/lib/SIL/Utils/CMakeLists.txt b/lib/SIL/Utils/CMakeLists.txt index 503af715ecab2..0fc6b38212f57 100644 --- a/lib/SIL/Utils/CMakeLists.txt +++ b/lib/SIL/Utils/CMakeLists.txt @@ -3,6 +3,7 @@ target_sources(swiftSIL PRIVATE DebugUtils.cpp Dominance.cpp DynamicCasts.cpp + GenericSpecializationMangler.cpp InstructionUtils.cpp LoopInfo.cpp MemAccessUtils.cpp diff --git a/lib/SIL/Utils/GenericSpecializationMangler.cpp b/lib/SIL/Utils/GenericSpecializationMangler.cpp new file mode 100644 index 0000000000000..529e36b09ff6a --- /dev/null +++ b/lib/SIL/Utils/GenericSpecializationMangler.cpp @@ -0,0 +1,137 @@ +//===--- GenericSpecializationMangler.cpp - mangling of specializations ---===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/SIL/GenericSpecializationMangler.h" +#include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericSignature.h" +#include "swift/AST/SubstitutionMap.h" +#include "swift/Demangling/ManglingMacros.h" + +using namespace swift; +using namespace Mangle; + +void SpecializationMangler::beginMangling() { + ASTMangler::beginManglingWithoutPrefix(); + if (Serialized) + ArgOpBuffer << 'q'; + ArgOpBuffer << char(uint8_t(Pass) + '0'); +} + +namespace { + +/// Utility class for demangling specialization attributes. +class AttributeDemangler : public Demangle::Demangler { +public: + void demangleAndAddAsChildren(StringRef MangledSpecialization, + NodePointer Parent) { + DemangleInitRAII state(*this, MangledSpecialization, nullptr); + if (!parseAndPushNodes()) { + llvm::errs() << "Can't demangle: " << MangledSpecialization << '\n'; + abort(); + } + for (Node *Nd : NodeStack) { + addChild(Parent, Nd); + } + } +}; + +} // namespace + +std::string SpecializationMangler::finalize() { + StringRef MangledSpecialization(Storage.data(), Storage.size()); + AttributeDemangler D; + NodePointer TopLevel = D.createNode(Node::Kind::Global); + D.demangleAndAddAsChildren(MangledSpecialization, TopLevel); + + StringRef FuncName = Function ? Function->getName() : StringRef(FunctionName); + NodePointer FuncTopLevel = nullptr; + if (FuncName.startswith(MANGLING_PREFIX_STR)) { + FuncTopLevel = D.demangleSymbol(FuncName); + assert(FuncTopLevel); + } + if (!FuncTopLevel) { + FuncTopLevel = D.createNode(Node::Kind::Global); + FuncTopLevel->addChild(D.createNode(Node::Kind::Identifier, FuncName), D); + } + for (NodePointer FuncChild : *FuncTopLevel) { + TopLevel->addChild(FuncChild, D); + } + std::string mangledName = Demangle::mangleNode(TopLevel); + verify(mangledName); + return mangledName; +} + +//===----------------------------------------------------------------------===// +// Generic Specialization +//===----------------------------------------------------------------------===// + +std::string GenericSpecializationMangler::mangle(GenericSignature Sig) { + beginMangling(); + + if (!Sig) { + assert(Function && + "Need a SIL function if no generic signature is provided"); + SILFunctionType *FTy = Function->getLoweredFunctionType(); + Sig = FTy->getInvocationGenericSignature(); + } + + bool First = true; + Sig->forEachParam([&](GenericTypeParamType *ParamType, bool Canonical) { + if (Canonical) { + appendType(Type(ParamType).subst(SubMap)->getCanonicalType()); + appendListSeparator(First); + } + }); + assert(!First && "no generic substitutions"); + + if (isInlined) + appendSpecializationOperator("Ti"); + else + appendSpecializationOperator( + isPrespecializaton ? "Ts" : (isReAbstracted ? "Tg" : "TG")); + return finalize(); +} + +static SubstitutionMap +getSubstitutionMapForPrespecialization(GenericSignature genericSig, + GenericSignature specSig) { + auto CalleeGenericSig = genericSig; + auto SpecializedGenericSig = specSig; + auto SpecializedGenericEnv = specSig->getGenericEnvironment(); + + auto CalleeInterfaceToSpecializedInterfaceMap = SubstitutionMap::get( + CalleeGenericSig, + [&](SubstitutableType *type) -> Type { + return type; + }, + LookUpConformanceInSignature(CalleeGenericSig.getPointer())); + + auto subs = SubstitutionMap::get( + CalleeGenericSig, + [&](SubstitutableType *type) -> Type { + auto SpecializedInterfaceTy = + Type(type).subst(CalleeInterfaceToSpecializedInterfaceMap); + return SpecializedGenericEnv->mapTypeIntoContext( + SpecializedInterfaceTy); + }, + LookUpConformanceInSignature(SpecializedGenericSig.getPointer())); + return subs; +} + +std::string GenericSpecializationMangler::manglePrespecialization( + std::string unspecializedName, GenericSignature genericSig, + GenericSignature specializedSig) { + auto subs = + getSubstitutionMapForPrespecialization(genericSig, specializedSig); + GenericSpecializationMangler mangler(unspecializedName, subs); + return mangler.mangle(genericSig); +} diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 4233f9dd8e7f8..07aa464d9468e 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1988,3 +1988,44 @@ swift::performASTLowering(FileUnit &sf, Lowering::TypeConverter &tc, auto desc = ASTLoweringDescriptor::forFile(sf, tc, options); return llvm::cantFail(sf.getASTContext().evaluator(ASTLoweringRequest{desc})); } + +static void transferSpecializeAttributeTargets(SILGenModule &SGM, SILModule &M, + Decl *d) { + if (auto *asd = dyn_cast(d)) { + for (auto ad : asd->getAllAccessors()) { + transferSpecializeAttributeTargets(SGM, M, ad); + } + } else if (auto *vd = dyn_cast(d)) { + for (auto *A : vd->getAttrs().getAttributes()) { + auto *SA = cast(A); + if (auto *targetFunctionDecl = SA->getTargetFunctionDecl(vd)) { + auto target = SILDeclRef(targetFunctionDecl); + auto targetSILFunction = SGM.getFunction(target, NotForDefinition); + auto kind = SA->getSpecializationKind() == + SpecializeAttr::SpecializationKind::Full + ? SILSpecializeAttr::SpecializationKind::Full + : SILSpecializeAttr::SpecializationKind::Partial; + + targetSILFunction->addSpecializeAttr(SILSpecializeAttr::create( + M, SA->getSpecializedSignature(), SA->isExported(), kind, nullptr)); + } + } + } +} + +void SILGenModule::visitImportDecl(ImportDecl *import) { + auto *module = import->getModule(); + if (module->isNonSwiftModule()) + return; + + SmallVector topLevelDecls; + module->getTopLevelDecls(topLevelDecls); + for (auto *t : topLevelDecls) { + if (auto *vd = dyn_cast(t)) { + transferSpecializeAttributeTargets(*this, M, vd); + } else if (auto *extension = dyn_cast(t)) { + for (auto *d : extension->getMembers()) + transferSpecializeAttributeTargets(*this, M, d); + } + } +} diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 76708975f8919..37a0e7fdb11b8 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -237,7 +237,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { // These are either not allowed at global scope or don't require // code emission. - void visitImportDecl(ImportDecl *d) {} + void visitImportDecl(ImportDecl *d); void visitEnumCaseDecl(EnumCaseDecl *d) {} void visitEnumElementDecl(EnumElementDecl *d) {} void visitOperatorDecl(OperatorDecl *d) {} diff --git a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp index c72e35aee7817..4908107793fe4 100644 --- a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp +++ b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp @@ -105,6 +105,11 @@ class FunctionLivenessComputation { if (F->isDynamicallyReplaceable()) return true; + // Don't remove pre-specialized functions. We need to preserver the + // pre-specialization specifications from other modules. + if (F->hasPrespecialization()) + return true; + // ObjC functions are called through the runtime and are therefore alive // even if not referenced inside SIL. if (F->getRepresentation() == SILFunctionTypeRepresentation::ObjCMethod) @@ -391,6 +396,11 @@ class FunctionLivenessComputation { ensureAlive(&F); } + // Make sure that functions referenced by _specialize(target: targetFun()) + // are kept alive. + F.forEachSpecializeAttrTargetFunction( + [this](SILFunction *targetFun) { ensureAlive(targetFun); }); + if (!F.shouldOptimize()) { LLVM_DEBUG(llvm::dbgs() << " anchor a no optimization function: " << F.getName() << "\n"); diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index afcbd3c7ddbcd..57038b5b5ac67 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -807,6 +807,9 @@ SILPassPipelinePlan::getOnonePassPipeline(const SILOptions &Options) { // Has only an effect if the -assume-single-thread option is specified. P.addAssumeSingleThreaded(); + // Create pre-specializations. + P.addOnonePrespecializations(); + // Has only an effect if the -gsil option is specified. P.addSILDebugInfoGenerator(); diff --git a/lib/SILOptimizer/Transforms/EagerSpecializer.cpp b/lib/SILOptimizer/Transforms/EagerSpecializer.cpp index 234b856d7c0ff..9e6be717987b8 100644 --- a/lib/SILOptimizer/Transforms/EagerSpecializer.cpp +++ b/lib/SILOptimizer/Transforms/EagerSpecializer.cpp @@ -739,8 +739,10 @@ namespace { // FIXME: This should be a function transform that pushes cloned functions on // the pass manager worklist. class EagerSpecializerTransform : public SILFunctionTransform { + bool onlyCreatePrespecializations; public: - EagerSpecializerTransform() {} + EagerSpecializerTransform(bool onlyPrespecialize) + : onlyCreatePrespecializations(onlyPrespecialize) {} void run() override; @@ -772,13 +774,19 @@ static SILFunction *eagerSpecialize(SILOptFunctionBuilder &FuncBuilder, SILFunction *NewFunc = FuncSpecializer.lookupSpecialization(); if (!NewFunc) { - NewFunc = FuncSpecializer.tryCreateSpecialization(); + NewFunc = FuncSpecializer.tryCreateSpecialization( + true /*forcePrespecialization*/); if (NewFunc) newFunctions.push_back(NewFunc); } - if (!NewFunc) + if (!NewFunc) { LLVM_DEBUG(llvm::dbgs() << " Failed. Cannot specialize function.\n"); + } else if (SA.isExported()) { + NewFunc->setLinkage(NewFunc->isDefinition() ? SILLinkage::Public + : SILLinkage::PublicExternal); + } + return NewFunc; } @@ -791,7 +799,7 @@ void EagerSpecializerTransform::run() { auto &F = *getFunction(); // Process functions in any order. - if (!F.shouldOptimize()) { + if (!F.shouldOptimize() && !onlyCreatePrespecializations) { LLVM_DEBUG(llvm::dbgs() << " Cannot specialize function " << F.getName() << " because it is marked to be " "excluded from optimizations.\n"); @@ -816,28 +824,53 @@ void EagerSpecializerTransform::run() { // TODO: Use a decision-tree to reduce the amount of dynamic checks being // performed. + SmallVector attrsToRemove; + for (auto *SA : F.getSpecializeAttrs()) { + if (onlyCreatePrespecializations && !SA->isExported()) { + attrsToRemove.push_back(SA); + continue; + } + auto *targetFunc = &F; + // If the _specialize attribute specifies another target function use it + // instead to specialize. + if (SA->getTargetFunction()) { + targetFunc = SA->getTargetFunction(); + if (!targetFunc->isDefinition()) { + auto &module = FuncBuilder.getModule(); + bool success = module.loadFunction(targetFunc); + assert(success); + module.linkFunction(targetFunc); + } + onlyCreatePrespecializations = true; + } ReInfoVec.emplace_back(FuncBuilder.getModule().getSwiftModule(), - FuncBuilder.getModule().isWholeModule(), &F, - SA->getSpecializedSignature()); - auto *NewFunc = - eagerSpecialize(FuncBuilder, &F, *SA, ReInfoVec.back(), newFunctions); - - SpecializedFuncs.push_back(NewFunc); + FuncBuilder.getModule().isWholeModule(), targetFunc, + SA->getSpecializedSignature(), SA->isExported()); + auto *NewFunc = eagerSpecialize(FuncBuilder, targetFunc, *SA, + ReInfoVec.back(), newFunctions); + SpecializedFuncs.push_back( + (onlyCreatePrespecializations || + SA->isExported() /*currently we don't handle coroutines in emitDispatchTo*/) + ? nullptr + : NewFunc); + if (!SA->isExported()) + attrsToRemove.push_back(SA); } // TODO: Optimize the dispatch code to minimize the amount // of checks. Use decision trees for this purpose. bool Changed = false; - for_each3(F.getSpecializeAttrs(), SpecializedFuncs, ReInfoVec, - [&](const SILSpecializeAttr *SA, SILFunction *NewFunc, - const ReabstractionInfo &ReInfo) { - if (NewFunc) { - NewFunc->verify(); - Changed = true; - EagerDispatch(&F, ReInfo).emitDispatchTo(NewFunc); - } - }); + if (!onlyCreatePrespecializations) + for_each3(F.getSpecializeAttrs(), SpecializedFuncs, ReInfoVec, + [&](const SILSpecializeAttr *SA, SILFunction *NewFunc, + const ReabstractionInfo &ReInfo) { + if (NewFunc) { + NewFunc->verify(); + Changed = true; + EagerDispatch(&F, ReInfo).emitDispatchTo(NewFunc); + } + }); // Invalidate everything since we delete calls as well as add new // calls and branches. @@ -845,8 +878,10 @@ void EagerSpecializerTransform::run() { invalidateAnalysis(SILAnalysis::InvalidationKind::Everything); } - // As specializations are created, the attributes should be removed. - F.clearSpecializeAttrs(); + // As specializations are created, the non-exported attributes should be + // removed. + for (auto *SA : attrsToRemove) + F.removeSpecializeAttr(SA); F.verify(); for (SILFunction *newF : newFunctions) { @@ -855,5 +890,9 @@ void EagerSpecializerTransform::run() { } SILTransform *swift::createEagerSpecializer() { - return new EagerSpecializerTransform(); + return new EagerSpecializerTransform(false/*onlyCreatePrespecializations*/); +} + +SILTransform *swift::createOnonePrespecializations() { + return new EagerSpecializerTransform(true /*onlyCreatePrespecializations*/); } diff --git a/lib/SILOptimizer/Transforms/GenericSpecializer.cpp b/lib/SILOptimizer/Transforms/GenericSpecializer.cpp index e52357cd22cad..326cb1b6311c3 100644 --- a/lib/SILOptimizer/Transforms/GenericSpecializer.cpp +++ b/lib/SILOptimizer/Transforms/GenericSpecializer.cpp @@ -81,7 +81,7 @@ bool GenericSpecializer::specializeAppliesInFunction(SILFunction &F) { auto *Callee = Apply.getReferencedFunctionOrNull(); if (!Callee) continue; - if (!Callee->isDefinition()) { + if (!Callee->isDefinition() && !Callee->hasPrespecialization()) { ORE.emit([&]() { using namespace OptRemark; return RemarkMissed("NoDef", I) diff --git a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp index ce3a00e413754..077b851c167ac 100644 --- a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp +++ b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp @@ -318,6 +318,14 @@ bool SILPerformanceInliner::isProfitableToInline( return false; } + // Bail out if this is a generic call of a `@_specialize(exported:)` function + // and we are in the early inliner. We want to give the generic specializer + // the opportunity to see specialized call sites. + if (IsGeneric && WhatToInline == InlineSelection::NoSemanticsAndGlobalInit && + Callee->hasPrespecialization()) { + return false; + } + SILLoopInfo *LI = LA->get(Callee); ShortestPathAnalysis *SPA = getSPA(Callee, LI); assert(SPA->isValid()); diff --git a/lib/SILOptimizer/Utils/GenericCloner.cpp b/lib/SILOptimizer/Utils/GenericCloner.cpp index e027cdfb3927f..57dc9cc1f1743 100644 --- a/lib/SILOptimizer/Utils/GenericCloner.cpp +++ b/lib/SILOptimizer/Utils/GenericCloner.cpp @@ -26,10 +26,9 @@ using namespace swift; /// Create a new empty function with the correct arguments and a unique name. -SILFunction *GenericCloner::initCloned(SILOptFunctionBuilder &FunctionBuilder, - SILFunction *Orig, - const ReabstractionInfo &ReInfo, - StringRef NewName) { +SILFunction *GenericCloner::createDeclaration( + SILOptFunctionBuilder &FunctionBuilder, SILFunction *Orig, + const ReabstractionInfo &ReInfo, StringRef NewName) { assert((!ReInfo.isSerialized() || Orig->isSerialized()) && "Specialization cannot make body more resilient"); assert((Orig->isTransparent() || Orig->isBare() || Orig->getLocation()) diff --git a/lib/SILOptimizer/Utils/Generics.cpp b/lib/SILOptimizer/Utils/Generics.cpp index 2bde614efd66c..15bca08352a17 100644 --- a/lib/SILOptimizer/Utils/Generics.cpp +++ b/lib/SILOptimizer/Utils/Generics.cpp @@ -1769,9 +1769,12 @@ void ReabstractionInfo::finishPartialSpecializationPreparation( /// This constructor is used when processing @_specialize. ReabstractionInfo::ReabstractionInfo(ModuleDecl *targetModule, bool isWholeModule, SILFunction *Callee, - GenericSignature SpecializedSig) - : TargetModule(targetModule), isWholeModule(isWholeModule) { - Serialized = Callee->isSerialized(); + GenericSignature SpecializedSig, + bool isPrespecialization) + : TargetModule(targetModule), isWholeModule(isWholeModule), + isPrespecialization(isPrespecialization) { + Serialized = + this->isPrespecialization ? IsNotSerialized : Callee->isSerialized(); if (shouldNotSpecialize(Callee, nullptr)) return; @@ -1805,7 +1808,8 @@ GenericFuncSpecializer::GenericFuncSpecializer( ParamSubs(ParamSubs), ReInfo(ReInfo) { - assert(GenericFunc->isDefinition() && "Expected definition to specialize!"); + assert((GenericFunc->isDefinition() || ReInfo.isPrespecialized()) && + "Expected definition or pre-specialized entry-point to specialize!"); auto FnTy = ReInfo.getSpecializedType(); if (ReInfo.isPartialSpecialization()) { @@ -1814,7 +1818,8 @@ GenericFuncSpecializer::GenericFuncSpecializer( ClonedName = Mangler.mangle(); } else { Mangle::GenericSpecializationMangler Mangler( - GenericFunc, ParamSubs, ReInfo.isSerialized(), /*isReAbstracted*/ true); + GenericFunc, ParamSubs, ReInfo.isSerialized(), /*isReAbstracted*/ true, + /*isInlined*/ false, ReInfo.isPrespecialized()); ClonedName = Mangler.mangle(); } LLVM_DEBUG(llvm::dbgs() << " Specialized function " << ClonedName << '\n'); @@ -1849,9 +1854,10 @@ void ReabstractionInfo::verify() const { } /// Create a new specialized function if possible, and cache it. -SILFunction *GenericFuncSpecializer::tryCreateSpecialization() { +SILFunction * +GenericFuncSpecializer::tryCreateSpecialization(bool forcePrespecialization) { // Do not create any new specializations at Onone. - if (!GenericFunc->shouldOptimize()) + if (!GenericFunc->shouldOptimize() && !forcePrespecialization) return nullptr; LLVM_DEBUG(llvm::dbgs() << "Creating a specialization: " @@ -2416,6 +2422,51 @@ static bool createPrespecializations(ApplySite Apply, SILFunction *ProxyFunc, return prespecializeFound; } +static SILFunction * +lookupOrCreatePrespecialization(SILOptFunctionBuilder &funcBuilder, + SILFunction *origF, std::string clonedName, + ReabstractionInfo &reInfo) { + + if (auto *specializedF = funcBuilder.getModule().lookUpFunction(clonedName)) { + assert(reInfo.getSpecializedType() == + specializedF->getLoweredFunctionType() && + "Previously specialized function does not match expected type."); + return specializedF; + } + + auto *declaration = + GenericCloner::createDeclaration(funcBuilder, origF, reInfo, clonedName); + declaration->setLinkage(SILLinkage::PublicExternal); + + return declaration; +} + +static SILFunction * +usePrespecialized(SILOptFunctionBuilder &funcBuilder, ApplySite apply, + SILFunction *refF, const ReabstractionInfo &specializedReInfo, + ReabstractionInfo &prespecializedReInfo) { + for (auto *SA : refF->getSpecializeAttrs()) { + if (!SA->isExported()) + continue; + ReabstractionInfo reInfo(funcBuilder.getModule().getSwiftModule(), + funcBuilder.getModule().isWholeModule(), refF, + SA->getSpecializedSignature(), + /*isPrespecialization*/ true); + if (specializedReInfo.getSpecializedType() != reInfo.getSpecializedType()) + continue; + + Mangle::GenericSpecializationMangler mangler( + refF, reInfo.getCalleeParamSubstitutionMap(), reInfo.isSerialized(), + /*isReAbstracted*/ true, /*isInlined*/ false, + reInfo.isPrespecialized()); + + prespecializedReInfo = reInfo; + return lookupOrCreatePrespecialization(funcBuilder, refF, mangler.mangle(), + reInfo); + } + return nullptr; +} + void swift::trySpecializeApplyOfGeneric( SILOptFunctionBuilder &FuncBuilder, ApplySite Apply, DeadInstructionSet &DeadApplies, @@ -2466,6 +2517,18 @@ void swift::trySpecializeApplyOfGeneric( if (!ReInfo.canBeSpecialized()) return; + // Check if there is a pre-specialization available in a library. + SILFunction *prespecializedF = nullptr; + ReabstractionInfo prespecializedReInfo; + if ((prespecializedF = usePrespecialized(FuncBuilder, Apply, RefF, ReInfo, + prespecializedReInfo))) { + ReInfo = prespecializedReInfo; + } + + // If there is not pre-specialization and we don't have a body give up. + if (!prespecializedF && !RefF->isDefinition()) + return; + SILModule &M = F->getModule(); bool needAdaptUsers = false; @@ -2521,7 +2584,9 @@ void swift::trySpecializeApplyOfGeneric( GenericFuncSpecializer FuncSpecializer(FuncBuilder, RefF, Apply.getSubstitutionMap(), ReInfo); - SILFunction *SpecializedF = FuncSpecializer.lookupSpecialization(); + SILFunction *SpecializedF = prespecializedF + ? prespecializedF + : FuncSpecializer.lookupSpecialization(); if (!SpecializedF) { SpecializedF = FuncSpecializer.tryCreateSpecialization(); if (!SpecializedF) @@ -2710,4 +2775,3 @@ SILFunction *swift::lookupPrespecializedSymbol(SILModule &M, return Specialization; } - diff --git a/lib/SILOptimizer/Utils/SpecializationMangler.cpp b/lib/SILOptimizer/Utils/SpecializationMangler.cpp index 3a37e3f3430ba..a5076f0309d25 100644 --- a/lib/SILOptimizer/Utils/SpecializationMangler.cpp +++ b/lib/SILOptimizer/Utils/SpecializationMangler.cpp @@ -11,93 +11,15 @@ //===----------------------------------------------------------------------===// #include "swift/SILOptimizer/Utils/SpecializationMangler.h" -#include "swift/SIL/SILGlobalVariable.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/SubstitutionMap.h" #include "swift/Demangling/ManglingMacros.h" +#include "swift/SIL/SILGlobalVariable.h" using namespace swift; using namespace Mangle; -void SpecializationMangler::beginMangling() { - ASTMangler::beginManglingWithoutPrefix(); - if (Serialized) - ArgOpBuffer << 'q'; - ArgOpBuffer << char(uint8_t(Pass) + '0'); -} - -namespace { - -/// Utility class for demangling specialization attributes. -class AttributeDemangler : public Demangle::Demangler { -public: - void demangleAndAddAsChildren(StringRef MangledSpecialization, - NodePointer Parent) { - DemangleInitRAII state(*this, MangledSpecialization, nullptr); - if (!parseAndPushNodes()) { - llvm::errs() << "Can't demangle: " << MangledSpecialization << '\n'; - abort(); - } - for (Node *Nd : NodeStack) { - addChild(Parent, Nd); - } - } -}; - -} // namespace - -std::string SpecializationMangler::finalize() { - StringRef MangledSpecialization(Storage.data(), Storage.size()); - AttributeDemangler D; - NodePointer TopLevel = D.createNode(Node::Kind::Global); - D.demangleAndAddAsChildren(MangledSpecialization, TopLevel); - - StringRef FuncName = Function->getName(); - NodePointer FuncTopLevel = nullptr; - if (FuncName.startswith(MANGLING_PREFIX_STR)) { - FuncTopLevel = D.demangleSymbol(FuncName); - assert(FuncTopLevel); - } - if (!FuncTopLevel) { - FuncTopLevel = D.createNode(Node::Kind::Global); - FuncTopLevel->addChild(D.createNode(Node::Kind::Identifier, FuncName), D); - } - for (NodePointer FuncChild : *FuncTopLevel) { - TopLevel->addChild(FuncChild, D); - } - std::string mangledName = Demangle::mangleNode(TopLevel); - verify(mangledName); - return mangledName; -} - -//===----------------------------------------------------------------------===// -// Generic Specialization -//===----------------------------------------------------------------------===// - -std::string GenericSpecializationMangler::mangle(GenericSignature Sig) { - beginMangling(); - - if (!Sig) { - SILFunctionType *FTy = Function->getLoweredFunctionType(); - Sig = FTy->getInvocationGenericSignature(); - } - - bool First = true; - Sig->forEachParam([&](GenericTypeParamType *ParamType, bool Canonical) { - if (Canonical) { - appendType(Type(ParamType).subst(SubMap)->getCanonicalType()); - appendListSeparator(First); - } - }); - assert(!First && "no generic substitutions"); - - if (isInlined) - appendSpecializationOperator("Ti"); - else - appendSpecializationOperator(isReAbstracted ? "Tg" : "TG"); - return finalize(); -} - //===----------------------------------------------------------------------===// // Partial Generic Specialization //===----------------------------------------------------------------------===// diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index 017fe84c4e5f6..fbe90853e79d7 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -868,7 +868,8 @@ ScopedImportLookupRequest::evaluate(Evaluator &evaluator, SmallVector decls; lookupInModule(topLevelModule, accessPath.front().Item, decls, NLKind::QualifiedLookup, ResolutionKind::Overloadable, - import->getDeclContext()->getModuleScopeContext()); + import->getDeclContext()->getModuleScopeContext(), + NL_QualifiedDefault); auto importLoc = import->getLoc(); if (decls.empty()) { diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 67f15ea3f539b..0214c9f9b91f5 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -418,6 +418,10 @@ namespace { class AccessControlChecker : public AccessControlCheckerBase, public DeclVisitor { public: + + AccessControlChecker(bool allowUsableFromInline) + : AccessControlCheckerBase(allowUsableFromInline) {} + AccessControlChecker() : AccessControlCheckerBase(/*checkUsableFromInline=*/false) {} @@ -2073,7 +2077,9 @@ DisallowedOriginKind swift::getDisallowedOriginKind(const Decl *decl, void swift::checkAccessControl(Decl *D) { if (isa(D) || isa(D)) { - AccessControlChecker().visit(D); + bool allowInlineable = + D->getDeclContext()->isInSpecializeExtensionContext(); + AccessControlChecker(allowInlineable).visit(D); UsableFromInlineChecker().visit(D); } else if (auto *ED = dyn_cast(D)) { checkExtensionGenericParamAccess(ED); diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index d5e36117c2e83..29597227c705b 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -119,6 +119,7 @@ class AttributeChecker : public AttributeVisitor { IGNORED_ATTR(ReferenceOwnership) IGNORED_ATTR(OriginallyDefinedIn) IGNORED_ATTR(NoDerivative) + IGNORED_ATTR(SpecializeExtension) #undef IGNORED_ATTR void visitAlignmentAttr(AlignmentAttr *attr) { @@ -1792,7 +1793,7 @@ void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, namelookup::lookupInModule(KitModule, Id_ApplicationDelegate, decls, NLKind::QualifiedLookup, namelookup::ResolutionKind::TypesOnly, - SF); + SF, NL_QualifiedDefault); if (decls.size() == 1) ApplicationDelegateProto = dyn_cast(decls[0]); } @@ -2310,6 +2311,9 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) { attr->getLocation(), /*allowConcreteGenericParams=*/true); attr->setSpecializedSignature(specializedSig); + + // Check the target function if there is one. + attr->getTargetFunctionDecl(FD); } void AttributeChecker::visitFixedLayoutAttr(FixedLayoutAttr *attr) { @@ -2423,7 +2427,7 @@ void AttributeChecker::visitDiscardableResultAttr(DiscardableResultAttr *attr) { /// Lookup the replaced decl in the replacments scope. static void lookupReplacedDecl(DeclNameRef replacedDeclName, - const DynamicReplacementAttr *attr, + const DeclAttribute *attr, const ValueDecl *replacement, SmallVectorImpl &results) { auto *declCtxt = replacement->getDeclContext(); @@ -2452,9 +2456,13 @@ static void lookupReplacedDecl(DeclNameRef replacedDeclName, if (!typeCtx) typeCtx = cast(declCtxt->getAsDecl())->getExtendedNominal(); + auto options = NL_QualifiedDefault; + if (declCtxt->isInSpecializeExtensionContext()) + options |= NL_IncludeUsableFromInlineAndInlineable; + if (typeCtx) - moduleScopeCtxt->lookupQualified({typeCtx}, replacedDeclName, - NL_QualifiedDefault, results); + moduleScopeCtxt->lookupQualified({typeCtx}, replacedDeclName, options, + results); } /// Remove any argument labels from the interface type of the given value that @@ -2476,10 +2484,10 @@ static Type getDynamicComparisonType(ValueDecl *value) { return interfaceType->removeArgumentLabels(numArgumentLabels); } -static FuncDecl *findReplacedAccessor(DeclNameRef replacedVarName, - AccessorDecl *replacement, - DynamicReplacementAttr *attr, - ASTContext &ctx) { +static FuncDecl *findSimilarAccessor(DeclNameRef replacedVarName, + const AccessorDecl *replacement, + DeclAttribute *attr, ASTContext &ctx, + bool forDynamicReplacement) { // Retrieve the replaced abstract storage decl. SmallVector results; @@ -2537,7 +2545,7 @@ static FuncDecl *findReplacedAccessor(DeclNameRef replacedVarName, assert(!isa(results[0])); auto *origStorage = cast(results[0]); - if (!origStorage->isDynamic()) { + if (forDynamicReplacement && !origStorage->isDynamic()) { Diags.diagnose(attr->getLocation(), diag::dynamic_replacement_accessor_not_dynamic, origStorage->getName()); @@ -2565,31 +2573,47 @@ static FuncDecl *findReplacedAccessor(DeclNameRef replacedVarName, return origAccessor; } +static FuncDecl *findReplacedAccessor(DeclNameRef replacedVarName, + const AccessorDecl *replacement, + DeclAttribute *attr, + ASTContext &ctx) { + return findSimilarAccessor(replacedVarName, replacement, attr, ctx, + /*forDynamicReplacement*/ true); +} + +static FuncDecl *findTargetAccessor(DeclNameRef replacedVarName, + const AccessorDecl *replacement, + DeclAttribute *attr, + ASTContext &ctx) { + return findSimilarAccessor(replacedVarName, replacement, attr, ctx, + /*forDynamicReplacement*/ false); +} + static AbstractFunctionDecl * -findReplacedFunction(DeclNameRef replacedFunctionName, - const AbstractFunctionDecl *replacement, - DynamicReplacementAttr *attr, DiagnosticEngine *Diags) { +findSimilarFunction(DeclNameRef replacedFunctionName, + const AbstractFunctionDecl *base, DeclAttribute *attr, + DiagnosticEngine *Diags, bool forDynamicReplacement) { // Note: we might pass a constant attribute when typechecker is nullptr. // Any modification to attr must be guarded by a null check on TC. // SmallVector results; - lookupReplacedDecl(replacedFunctionName, attr, replacement, results); + lookupReplacedDecl(replacedFunctionName, attr, base, results); for (auto *result : results) { // Protocol requirements are not replaceable. if (isa(result->getDeclContext())) continue; // Check for static/instance mismatch. - if (result->isStatic() != replacement->isStatic()) + if (result->isStatic() != base->isStatic()) continue; auto resultTy = result->getInterfaceType(); - auto replaceTy = replacement->getInterfaceType(); + auto replaceTy = base->getInterfaceType(); TypeMatchOptions matchMode = TypeMatchFlags::AllowABICompatible; matchMode |= TypeMatchFlags::AllowCompatibleOpaqueTypeArchetypes; if (resultTy->matches(replaceTy, matchMode)) { - if (!result->isDynamic()) { + if (forDynamicReplacement && !result->isDynamic()) { if (Diags) { Diags->diagnose(attr->getLocation(), diag::dynamic_replacement_function_not_dynamic, @@ -2607,17 +2631,23 @@ findReplacedFunction(DeclNameRef replacedFunctionName, if (results.empty()) { Diags->diagnose(attr->getLocation(), - diag::dynamic_replacement_function_not_found, + forDynamicReplacement + ? diag::dynamic_replacement_function_not_found + : diag::specialize_target_function_not_found, replacedFunctionName); } else { Diags->diagnose(attr->getLocation(), - diag::dynamic_replacement_function_of_type_not_found, + forDynamicReplacement + ? diag::dynamic_replacement_function_of_type_not_found + : diag::specialize_target_function_of_type_not_found, replacedFunctionName, - replacement->getInterfaceType()->getCanonicalType()); + base->getInterfaceType()->getCanonicalType()); for (auto *result : results) { Diags->diagnose(SourceLoc(), - diag::dynamic_replacement_found_function_of_type, + forDynamicReplacement + ? diag::dynamic_replacement_found_function_of_type + : diag::specialize_found_function_of_type, result->getName(), result->getInterfaceType()->getCanonicalType()); } @@ -2626,6 +2656,22 @@ findReplacedFunction(DeclNameRef replacedFunctionName, return nullptr; } +static AbstractFunctionDecl * +findReplacedFunction(DeclNameRef replacedFunctionName, + const AbstractFunctionDecl *replacement, + DynamicReplacementAttr *attr, DiagnosticEngine *Diags) { + return findSimilarFunction(replacedFunctionName, replacement, attr, Diags, + true /*forDynamicReplacement*/); +} + +static AbstractFunctionDecl * +findTargetFunction(DeclNameRef targetFunctionName, + const AbstractFunctionDecl *base, + SpecializeAttr * attr, DiagnosticEngine *diags) { + return findSimilarFunction(targetFunctionName, base, attr, diags, + false /*forDynamicReplacement*/); +} + static AbstractStorageDecl * findReplacedStorageDecl(DeclNameRef replacedFunctionName, const AbstractStorageDecl *replacement, @@ -3517,6 +3563,34 @@ DynamicallyReplacedDeclRequest::evaluate(Evaluator &evaluator, return nullptr; } +ValueDecl * +SpecializeAttrTargetDeclRequest::evaluate(Evaluator &evaluator, + const ValueDecl *vd, + SpecializeAttr *attr) const { + if (auto *lazyResolver = attr->resolver) { + auto *decl = + lazyResolver->loadTargetFunctionDecl(attr, attr->resolverContextData); + attr->resolver = nullptr; + return decl; + } + + auto &ctx = vd->getASTContext(); + + auto targetFunctionName = attr->getTargetFunctionName(); + if (!targetFunctionName) + return nullptr; + + if (auto *ad = dyn_cast(vd)) { + return findTargetAccessor(targetFunctionName, ad, attr, ctx); + } + + if (auto *afd = dyn_cast(vd)) { + return findTargetFunction(targetFunctionName, afd, attr, &ctx.Diags); + } + + return nullptr; + +} /// Returns true if the given type conforms to `Differentiable` in the given /// context. If `tangentVectorEqualsSelf` is true, also check whether the given /// type satisfies `TangentVector == Self`. diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 66240cc334e70..bf400f54c9e32 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2189,6 +2189,9 @@ static Type validateParameterType(ParamDecl *decl) { TypeResolverContext::FunctionInput); options |= TypeResolutionFlags::Direct; + if (dc->isInSpecializeExtensionContext()) + options |= TypeResolutionFlags::AllowInlinable; + const auto resolution = TypeResolution::forInterface(dc, options, unboundTyOpener); auto Ty = resolution.resolveType(decl->getTypeRepr()); @@ -2722,9 +2725,11 @@ ExtendedTypeRequest::evaluate(Evaluator &eval, ExtensionDecl *ext) const { return error(); // Compute the extended type. - const TypeResolutionOptions options(TypeResolverContext::ExtensionBinding); + TypeResolutionOptions options(TypeResolverContext::ExtensionBinding); + if (ext->isInSpecializeExtensionContext()) + options |= TypeResolutionFlags::AllowInlinable; const auto resolution = TypeResolution::forStructural( - ext->getDeclContext(), TypeResolverContext::ExtensionBinding, + ext->getDeclContext(), options, [](auto unboundTy) { // FIXME: Don't let unbound generic types escape type resolution. // For now, just return the unbound generic type. diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 99de01af99b28..11c7985877a54 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1472,6 +1472,7 @@ namespace { UNINTERESTING_ATTR(SwiftNativeObjCRuntimeBase) UNINTERESTING_ATTR(ShowInInterface) UNINTERESTING_ATTR(Specialize) + UNINTERESTING_ATTR(SpecializeExtension) UNINTERESTING_ATTR(DynamicReplacement) UNINTERESTING_ATTR(PrivateImport) UNINTERESTING_ATTR(MainType) diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index 9c69020d726b0..da60f36c5b31a 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -903,8 +903,9 @@ RequirementRequest::evaluate(Evaluator &evaluator, unsigned index, TypeResolutionStage stage) const { // Figure out the type resolution. - const auto options = - TypeResolutionOptions(TypeResolverContext::GenericRequirement); + auto options = TypeResolutionOptions(TypeResolverContext::GenericRequirement); + if (owner.dc->isInSpecializeExtensionContext()) + options |= TypeResolutionFlags::AllowInlinable; Optional resolution; switch (stage) { case TypeResolutionStage::Structural: diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 62347f886bbec..b148adab7180c 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -211,6 +211,8 @@ convertToUnqualifiedLookupOptions(NameLookupOptions options) { newOptions |= UnqualifiedLookupFlags::IgnoreAccessControl; if (options.contains(NameLookupFlags::IncludeOuterResults)) newOptions |= UnqualifiedLookupFlags::IncludeOuterResults; + if (options.contains(NameLookupFlags::IncludeInlineableAndUsableFromInline)) + newOptions |= UnqualifiedLookupFlags::IncludeInlineableAndUsableFromInline; return newOptions; } @@ -386,6 +388,8 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc, if (options.contains(NameLookupFlags::IgnoreAccessControl)) subOptions |= NL_IgnoreAccessControl; + if (options.contains(NameLookupFlags::IncludeInlineableAndUsableFromInline)) + subOptions |= NL_IncludeUsableFromInlineAndInlineable; // Make sure we've resolved implicit members, if we need them. if (auto *current = type->getAnyNominal()) { diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 2a2040fc26505..476e02939136d 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1321,8 +1321,11 @@ static Type resolveTopLevelIdentTypeComponent(TypeResolution resolution, } } + NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; + if (options.contains(TypeResolutionFlags::AllowInlinable)) + lookupOptions |= NameLookupFlags::IncludeInlineableAndUsableFromInline; auto globals = TypeChecker::lookupUnqualifiedType(DC, id, comp->getLoc(), - defaultUnqualifiedLookupOptions); + lookupOptions); // Process the names we found. Type current; @@ -1400,7 +1403,7 @@ static Type resolveTopLevelIdentTypeComponent(TypeResolution resolution, return ErrorType::get(ctx); return diagnoseUnknownType(resolution, nullptr, SourceRange(), comp, - defaultUnqualifiedLookupOptions); + lookupOptions); } comp->setValue(currentDecl, currentDC); @@ -1522,11 +1525,13 @@ static Type resolveNestedIdentTypeComponent(TypeResolution resolution, // Phase 1: Find and bind the component decl. // Look for member types with the given name. + NameLookupOptions lookupOptions = defaultMemberLookupOptions; + if (options.contains(TypeResolutionFlags::AllowInlinable)) + lookupOptions |= NameLookupFlags::IncludeInlineableAndUsableFromInline; LookupTypeResult memberTypes; if (parentTy->mayHaveMembers()) - memberTypes = TypeChecker::lookupMemberType(DC, parentTy, - comp->getNameRef(), - defaultMemberLookupOptions); + memberTypes = TypeChecker::lookupMemberType( + DC, parentTy, comp->getNameRef(), lookupOptions); // Name lookup was ambiguous. Complain. // FIXME: Could try to apply generic arguments first, and see whether @@ -1549,7 +1554,7 @@ static Type resolveNestedIdentTypeComponent(TypeResolution resolution, return ErrorType::get(ctx); memberType = diagnoseUnknownType(resolution, parentTy, parentRange, comp, - defaultMemberLookupOptions); + lookupOptions); member = comp->getBoundDecl(); if (!member) return ErrorType::get(ctx); diff --git a/lib/Sema/TypeCheckType.h b/lib/Sema/TypeCheckType.h index 4810dd0e344ba..098e9fd1e31bd 100644 --- a/lib/Sema/TypeCheckType.h +++ b/lib/Sema/TypeCheckType.h @@ -64,6 +64,9 @@ enum class TypeResolutionFlags : uint16_t { /// Whether to allow module declaration types. AllowModule = 1 << 9, + + /// Make internal @usableFromInline and @inlinable decls visible. + AllowInlinable = 1 << 10, }; /// Type resolution contexts that require special handling. diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 629cd1f136a20..3caaeaed2d3c3 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -152,6 +152,8 @@ enum class NameLookupFlags { /// Whether to include results from outside the innermost scope that has a /// result. IncludeOuterResults = 1 << 1, + // Whether to include results that are marked @inlinable or @usableFromInline. + IncludeInlineableAndUsableFromInline = 1 << 2, }; /// A set of options that control name lookup. diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 78dd359d94238..93ea69092739f 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -4255,18 +4255,33 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { unsigned specializationKindVal; GenericSignatureID specializedSigID; + ArrayRef rawPieceIDs; + uint64_t numArgs; + DeclID targetFunID; + serialization::decls_block::SpecializeDeclAttrLayout::readRecord( - scratch, exported, specializationKindVal, specializedSigID); + scratch, exported, specializationKindVal, specializedSigID, + targetFunID, numArgs, rawPieceIDs); specializationKind = specializationKindVal ? SpecializeAttr::SpecializationKind::Partial : SpecializeAttr::SpecializationKind::Full; + // The 'target' parameter. + DeclNameRef replacedFunctionName; + if (numArgs) { + auto baseName = MF.getDeclBaseName(rawPieceIDs[0]); + SmallVector pieces; + for (auto pieceID : rawPieceIDs.slice(1)) + pieces.push_back(MF.getIdentifier(pieceID)); + replacedFunctionName = (numArgs == 1) + ? DeclNameRef({baseName}) // simple name + : DeclNameRef({ctx, baseName, pieces}); + } auto specializedSig = MF.getGenericSignature(specializedSigID); - Attr = SpecializeAttr::create(ctx, SourceLoc(), SourceRange(), - nullptr, exported != 0, - specializationKind, - specializedSig); + Attr = SpecializeAttr::create(ctx, exported != 0, specializationKind, + specializedSig, replacedFunctionName, &MF, + targetFunID); break; } @@ -6028,6 +6043,13 @@ ModuleFile::loadReferencedFunctionDecl(const DerivativeAttr *DA, return cast(getDecl(contextData)); } +ValueDecl *ModuleFile::loadTargetFunctionDecl(const SpecializeAttr *attr, + uint64_t contextData) { + if (contextData == 0) + return nullptr; + return cast(getDecl(contextData)); +} + Type ModuleFile::loadTypeEraserType(const TypeEraserAttr *TRA, uint64_t contextData) { return getType(contextData); diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index b463b3d84cffd..7df513082ae47 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -683,6 +683,7 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, } // Read and instantiate the specialize attributes. + bool shouldAddAtttributes = fn->getSpecializeAttrs().empty(); while (numSpecAttrs--) { llvm::Expected maybeNext = SILCursor.advance(AF_DontPopBlockAtEnd); @@ -701,18 +702,27 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, unsigned exported; unsigned specializationKindVal; GenericSignatureID specializedSigID; + IdentifierID targetFunctionID; SILSpecializeAttrLayout::readRecord(scratch, exported, - specializationKindVal, - specializedSigID); + specializationKindVal, specializedSigID, + targetFunctionID); + + SILFunction *target = nullptr; + if (targetFunctionID) { + target = getFuncForReference(MF->getIdentifier(targetFunctionID).str()); + } + SILSpecializeAttr::SpecializationKind specializationKind = specializationKindVal ? SILSpecializeAttr::SpecializationKind::Partial : SILSpecializeAttr::SpecializationKind::Full; auto specializedSig = MF->getGenericSignature(specializedSigID); - - // Read the substitution list and construct a SILSpecializeAttr. - fn->addSpecializeAttr(SILSpecializeAttr::create( - SILMod, specializedSig, exported != 0, specializationKind)); + // Only add the specialize attributes once. + if (shouldAddAtttributes) { + // Read the substitution list and construct a SILSpecializeAttr. + fn->addSpecializeAttr(SILSpecializeAttr::create( + SILMod, specializedSig, exported != 0, specializationKind, target)); + } } GenericEnvironment *genericEnv = nullptr; diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index dfa5594eaf46f..3a5b3d5a0a5bd 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -658,6 +658,8 @@ class ModuleFile loadDynamicallyReplacedFunctionDecl(const DynamicReplacementAttr *DRA, uint64_t contextData) override; + virtual ValueDecl *loadTargetFunctionDecl(const SpecializeAttr *attr, + uint64_t contextData) override; virtual AbstractFunctionDecl * loadReferencedFunctionDecl(const DerivativeAttr *DA, uint64_t contextData) override; diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 9bed35d9f2a46..75089d867e7a6 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 580; // async_continuation SIL insns +const uint16_t SWIFTMODULE_VERSION_MINOR = 581; // @_specialize target parameter /// A standard hash seed used for all string hashes in a serialized module. /// @@ -1848,7 +1848,10 @@ namespace decls_block { Specialize_DECL_ATTR, BCFixed<1>, // exported flag BCFixed<1>, // specialization kind - GenericSignatureIDField // specialized signature + GenericSignatureIDField, // specialized signature + DeclIDField, // target function + BCVBR<4>, // # of arguments (+1) or zero if no name + BCArray >; using DifferentiableDeclAttrLayout = BCRecordLayout< diff --git a/lib/Serialization/SILFormat.h b/lib/Serialization/SILFormat.h index 696f06b0d82fa..2f7bcc83b9d08 100644 --- a/lib/Serialization/SILFormat.h +++ b/lib/Serialization/SILFormat.h @@ -302,7 +302,8 @@ namespace sil_block { BCRecordLayout, // exported BCFixed<1>, // specialization kind - GenericSignatureIDField // specialized signature + GenericSignatureIDField, // specialized signature + DeclIDField // Target SILFunction name or 0. >; // Has an optional argument list where each argument is a typed valueref. diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index f5f2830ae446a..a00e0df83cb31 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2362,13 +2362,30 @@ class Serializer::DeclSerializer : public DeclVisitor { case DAK_Specialize: { auto abbrCode = S.DeclTypeAbbrCodes[SpecializeDeclAttrLayout::Code]; - auto SA = cast(DA); + auto attr = cast(DA); + auto targetFun = attr->getTargetFunctionName(); + auto *targetFunDecl = attr->getTargetFunctionDecl(cast(D)); + + SmallVector pieces; + // encodes whether this a a simple or compound name by adding one. + size_t numArgs = 0; + if (targetFun) { + pieces.push_back(S.addDeclBaseNameRef(targetFun.getBaseName())); + for (auto argName : targetFun.getArgumentNames()) + pieces.push_back(S.addDeclBaseNameRef(argName)); + if (targetFun.isSimpleName()) { + assert(pieces.size() == 1); + numArgs = 1; + } else + numArgs = pieces.size() + 1; + } SpecializeDeclAttrLayout::emitRecord( - S.Out, S.ScratchRecord, abbrCode, - (unsigned)SA->isExported(), - (unsigned)SA->getSpecializationKind(), - S.addGenericSignatureRef(SA->getSpecializedSignature())); + S.Out, S.ScratchRecord, abbrCode, (unsigned)attr->isExported(), + (unsigned)attr->getSpecializationKind(), + S.addGenericSignatureRef(attr->getSpecializedSignature()), + S.addDeclRef(targetFunDecl), numArgs, + pieces); return; } diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index b651fcae6698d..95c72eecc5a3a 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -452,11 +452,17 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) { for (auto *SA : F.getSpecializeAttrs()) { unsigned specAttrAbbrCode = SILAbbrCodes[SILSpecializeAttrLayout::Code]; + IdentifierID targetFunctionNameID = 0; + if (auto *target = SA->getTargetFunction()) { + addReferencedSILFunction(target, true); + targetFunctionNameID = S.addUniquedStringRef(target->getName()); + } SILSpecializeAttrLayout::emitRecord( Out, ScratchRecord, specAttrAbbrCode, (unsigned)SA->isExported(), (unsigned)SA->getSpecializationKind(), - S.addGenericSignatureRef(SA->getSpecializedSignature())); + S.addGenericSignatureRef(SA->getSpecializedSignature()), + targetFunctionNameID); } // Assign a unique ID to each basic block of the SILFunction. diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index e3523a683456a..76f84c9d51cae 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -664,6 +664,19 @@ void TBDGenVisitor::visitAbstractFunctionDecl(AbstractFunctionDecl *AFD) { return; } + // Add exported prespecialized symbols. + for (auto *attr : AFD->getAttrs().getAttributes()) { + if (!attr->isExported()) + continue; + if (auto *targetFun = attr->getTargetFunctionDecl(AFD)) { + auto declRef = SILDeclRef(targetFun, attr->getSpecializedSignature()); + addSymbol(declRef.mangle(), SymbolSource::forSILDeclRef(declRef)); + } else { + auto declRef = SILDeclRef(AFD, attr->getSpecializedSignature()); + addSymbol(declRef.mangle(), SymbolSource::forSILDeclRef(declRef)); + } + } + addSymbol(SILDeclRef(AFD)); // Add the global function pointer for a dynamically replaceable function. diff --git a/test/IRGen/Inputs/pre_specialize_module.swift b/test/IRGen/Inputs/pre_specialize_module.swift new file mode 100644 index 0000000000000..8993ec0b92245 --- /dev/null +++ b/test/IRGen/Inputs/pre_specialize_module.swift @@ -0,0 +1,112 @@ +@usableFromInline +struct ResilientInternalBoxedThing { + @usableFromInline + var t: T + + @usableFromInline + init(_ t: T) { + self.t = t + } +} + +@usableFromInline +@frozen +struct InternalThing { + @usableFromInline + var t: T + + @usableFromInline + init(_ t: T) { + self.t = t + } + + @_specialize(exported: true, where T == Int) + @_specialize(exported: true, where T == Bool) + @_specialize(exported: true, where T == ResilientInternalBoxedThing) + @inlinable + func compute() -> T { + return t + } + + @inlinable + var computedX : T { + @_specialize(exported: true, where T == Int) + get { + return t + } + @_specialize(exported: true, where T == Int) + set { + t = newValue + } + } + + @inlinable + subscript(_ i: Int) -> T { + @_specialize(exported: true, where T == Int) + get { + return t + } + @_specialize(exported: true, where T == Int) + set { + t = newValue + } + } +} + +@usableFromInline +class InternalRef { + @usableFromInline + var t: T + + @usableFromInline + init(_ t: T) { + self.t = t + } + + @_specialize(exported: true, where T == Int) + @_specialize(exported: true, where T == Bool) + @_specialize(exported: true, where T == ResilientInternalBoxedThing) + @inlinable + final func compute() -> T { + return t + } + + @inlinable + final var computedX : T { + @_specialize(exported: true, where T == Int) + get { + return t + } + @_specialize(exported: true, where T == Int) + set { + t = newValue + } + } + + @inlinable + final subscript(_ i: Int) -> T { + @_specialize(exported: true, where T == Int) + get { + return t + } + @_specialize(exported: true, where T == Int) + set { + t = newValue + } + } +} + +@inline(never) +@inlinable +public func testSpecialization(_ t: T) { + print(InternalThing(ResilientInternalBoxedThing(t)).compute()) + + print(InternalThing(t).compute()) + + var i = InternalThing(t) + i.computedX = t + print(i.computedX) + + i[1] = t + print(i[2]) +} diff --git a/test/IRGen/Inputs/pre_specialize_module_B.swift b/test/IRGen/Inputs/pre_specialize_module_B.swift new file mode 100644 index 0000000000000..fc30480ef27a2 --- /dev/null +++ b/test/IRGen/Inputs/pre_specialize_module_B.swift @@ -0,0 +1,68 @@ +import A + +public class AnotherThing { + public init() {} +} + +@_specializeExtension +extension InternalThing { + + @_specialize(exported: true, target: compute() , where T == AnotherThing) + @_specialize(exported: true, target: compute(), where T == ResilientInternalBoxedThing) + public func specializeCompute() -> T { + fatalError("don't call") + } + + public var specializeComputedX : T { + @_specialize(exported: true, target: computedX, where T == AnotherThing) + get { + fatalError("don't call") + } + @_specialize(exported: true, target: computedX, where T == AnotherThing) + set { + fatalError("don't call") + } + } + + public subscript(specialized i: Int) -> T { + @_specialize(exported: true, target: subscript(_:), where T == AnotherThing) + get { + fatalError("don't call") + } + @_specialize(exported: true, target: subscript(_:), where T == AnotherThing) + set { + fatalError("don't call") + } + } +} + +@_specializeExtension +extension InternalRef { + + @_specialize(exported: true, target: compute() , where T == AnotherThing) + public func specializeCompute() -> T { + fatalError("don't call") + } + + public var specializeComputedX : T { + @_specialize(exported: true, target: computedX, where T == AnotherThing) + get { + fatalError("don't call") + } + @_specialize(exported: true, target: computedX, where T == AnotherThing) + set { + fatalError("don't call") + } + } + + public subscript(specialized i: Int) -> T { + @_specialize(exported: true, target: subscript(_:), where T == AnotherThing) + get { + fatalError("don't call") + } + @_specialize(exported: true, target: subscript(_:), where T == AnotherThing) + set { + fatalError("don't call") + } + } +} diff --git a/test/IRGen/pre_specialize.swift b/test/IRGen/pre_specialize.swift new file mode 100644 index 0000000000000..0f3979855bc64 --- /dev/null +++ b/test/IRGen/pre_specialize.swift @@ -0,0 +1,170 @@ +// RUN: %empty-directory(%t) + +// Module A code generation. +// RUN: %target-swift-frontend -emit-ir -primary-file %S/Inputs/pre_specialize_module.swift -module-name A | %FileCheck %s -check-prefix=CHECK-A -check-prefix=CHECK-A-FRAG +// RUN: %target-swift-frontend -O -emit-ir -primary-file %S/Inputs/pre_specialize_module.swift -module-name A | %FileCheck %s -check-prefix=CHECK-A -check-prefix=CHECK-A-FRAG +// RUN: %target-swift-frontend -enable-library-evolution -emit-ir -primary-file %S/Inputs/pre_specialize_module.swift -module-name A | %FileCheck %s -check-prefix=CHECK-A -check-prefix=CHECK-A-RES +// RUN: %target-swift-frontend -O -enable-library-evolution -emit-ir -primary-file %S/Inputs/pre_specialize_module.swift -module-name A | %FileCheck %s -check-prefix=CHECK-A -check-prefix=CHECK-A-RES + +// Module B code generation with A.swiftmodule. +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -Xfrontend -validate-tbd-against-ir=missing -emit-module -emit-module-path=%t/A.swiftmodule -module-name A %S/Inputs/pre_specialize_module.swift -emit-library -o %t/%target-library-name(A) +// RUN: %target-swift-frontend -I %t -emit-ir -primary-file %S/Inputs/pre_specialize_module_B.swift -module-name B | %FileCheck %s -check-prefix=CHECK-B +// RUN: %target-swift-frontend -I %t -O -emit-ir -primary-file %S/Inputs/pre_specialize_module_B.swift -module-name B | %FileCheck %s -check-prefix=CHECK-B +// RUN: %target-build-swift -I %t -Xfrontend -validate-tbd-against-ir=missing -emit-module -emit-module-path=%t/B.swiftmodule -module-name B %S/Inputs/pre_specialize_module_B.swift -emit-library -o %t/%target-library-name(B) -L %t -lA +// RUN: %target-build-swift -swift-version 5 -I %t -Xfrontend -validate-tbd-against-ir=all -enable-library-evolution -emit-module-interface-path %t/B.swiftinterface -module-name B %S/Inputs/pre_specialize_module_B.swift -emit-library -o %t/%target-library-name(B) -L %t -lA + +// Module B code generation with A.swiftmodule with library evolution. +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -enable-library-evolution -Xfrontend -validate-tbd-against-ir=all -emit-module -emit-module-path=%t/A.swiftmodule -module-name A %S/Inputs/pre_specialize_module.swift -emit-library -o %t/%target-library-name(A) +// RUN: %target-swift-frontend -I %t -emit-ir -primary-file %S/Inputs/pre_specialize_module_B.swift -module-name B | %FileCheck %s -check-prefix=CHECK-B +// RUN: %target-swift-frontend -I %t -O -emit-ir -primary-file %S/Inputs/pre_specialize_module_B.swift -module-name B | %FileCheck %s -check-prefix=CHECK-B +// RUN: %target-build-swift -I %t -Xfrontend -validate-tbd-against-ir=missing -emit-module -emit-module-path=%t/B.swiftmodule -module-name B %S/Inputs/pre_specialize_module_B.swift -emit-library -o %t/%target-library-name(B) -L %t -lA +// RUN: %target-build-swift -swift-version 5 -I %t -Xfrontend -validate-tbd-against-ir=all -enable-library-evolution -emit-module-interface-path %t/B.swiftinterface -module-name B %S/Inputs/pre_specialize_module_B.swift -emit-library -o %t/%target-library-name(B) -L %t -lA + +// Module B code generation with A.swiftinterface with library evolution. +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -enable-library-evolution -Xfrontend -validate-tbd-against-ir=all -emit-module-interface-path %t/A.swiftinterface -module-name A %S/Inputs/pre_specialize_module.swift -emit-library -o %t/%target-library-name(A) -swift-version 5 +// RUN: %target-swift-frontend -I %t -emit-ir -primary-file %S/Inputs/pre_specialize_module_B.swift -module-name B | %FileCheck %s -check-prefix=CHECK-B +// RUN: %target-swift-frontend -I %t -O -emit-ir -primary-file %S/Inputs/pre_specialize_module_B.swift -module-name B | %FileCheck %s -check-prefix=CHECK-B +// RUN: %target-build-swift -I %t -Xfrontend -validate-tbd-against-ir=missing -emit-module -emit-module-path=%t/B.swiftmodule -module-name B %S/Inputs/pre_specialize_module_B.swift -emit-library -o %t/%target-library-name(B) -L %t -lA +// RUN: %target-build-swift -swift-version 5 -I %t -Xfrontend -validate-tbd-against-ir=all -enable-library-evolution -emit-module-interface-path %t/B.swiftinterface -module-name B %S/Inputs/pre_specialize_module_B.swift -emit-library -o %t/%target-library-name(B) -L %t -lA + +// Module A tests +// -------------- + +// specialized InternalThing.compute() +// CHECK-A-FRAG: define{{( dllexport)?}}{{( protected)?}} swiftcc [[INT:(i64|i32)]] @"$s1A13InternalThingV7computexyFAA09Resilienta5BoxedB0VySiG_Ts5"({{(i64|i32)}}{{( returned)?}} %0) +// CHECK-A-RES: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A13InternalThingV7computexyFAA09Resilienta5BoxedB0VySiG_Ts5"(%T1A27ResilientInternalBoxedThingVySiG* noalias nocapture sret %0, [[INT:(i64|i32)]] %1) +// CHECK-A: define{{( dllexport)?}}{{( protected)?}} swiftcc i1 @"$s1A13InternalThingV7computexyFSb_Ts5"(i1{{( returned)?}} %0) +// CHECK-A: define{{( dllexport)?}}{{( protected)?}} swiftcc [[INT]] @"$s1A13InternalThingV7computexyFSi_Ts5"([[INT]]{{( returned)?}} %0) + +// specialized InternalThing.computedX.getter +// CHECK-A: define{{( dllexport)?}}{{( protected)?}} swiftcc [[INT]] @"$s1A13InternalThingV9computedXxvgSi_Ts5"([[INT]]{{( returned)?}} %0) +// specialized InternalThing.computedX.setter +// CHECK-A: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A13InternalThingV9computedXxvsSi_Ts5"([[INT]] %0, %T1A13InternalThingVySiG* nocapture swiftself dereferenceable({{(4|8)}}) %1) + +// specialized InternalThing.subscript.getter +// CHECK-A: define{{( dllexport)?}}{{( protected)?}} swiftcc [[INT]] @"$s1A13InternalThingVyxSicigSi_Ts5"([[INT]] %0, [[INT]]{{( returned)?}} %1) +// specialized InternalThing.subscript.setter +// CHECK-A: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A13InternalThingVyxSicisSi_Ts5"([[INT]] %0, [[INT]] %1, %T1A13InternalThingVySiG* nocapture swiftself dereferenceable({{(4|8)}}) %2) + +// specialized InternalRef.compute() +// CHECK-A-FRAG: define{{( dllexport)?}}{{( protected)?}} swiftcc [[INT:(i64|i32)]] @"$s1A11InternalRefC7computexyFAA09ResilientA10BoxedThingVySiG_Ts5" +// CHECK-A-RES: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A11InternalRefC7computexyFAA09ResilientA10BoxedThingVySiG_Ts5" +// CHECK-A: define{{( dllexport)?}}{{( protected)?}} swiftcc i1 @"$s1A11InternalRefC7computexyFSb_Ts5" +// CHECK-A: define{{( dllexport)?}}{{( protected)?}} swiftcc [[INT]] @"$s1A11InternalRefC7computexyFSi_Ts5" + +// specialized InternalRef.computedX.getter +// CHECK-A-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc [[INT]] @"$s1A11InternalRefC9computedXxvgSi_Ts5" +// specialized InternalRef.computedX.setter +// CHECK-A-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A11InternalRefC9computedXxvsSi_Ts5" + +// specialized InternalRef.subscript.getter +// CHECK-A-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc [[INT]] @"$s1A11InternalRefCyxSicigSi_Ts5" +// specialized InternalRef.subscript.setter +// CHECK-A-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A11InternalRefCyxSicisSi_Ts5" + +// Module B tests +// -------------- + +// specialized InternalThing.compute() +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc {{(%T1B12AnotherThingC\*|void)}} @"$s1A13InternalThingV7computexyFAA09Resilienta5BoxedB0Vy1B07AnotherB0CG_Ts5" +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc %T1B12AnotherThingC* @"$s1A13InternalThingV7computexyF1B07AnotherB0C_Ts5"(%T1B12AnotherThingC*{{( returned)?}} %0) + +// specialized InternalThing.computedX.getter +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc %T1B12AnotherThingC* @"$s1A13InternalThingV9computedXxvg1B07AnotherB0C_Ts5"(%T1B12AnotherThingC*{{( returned)?}} %0) + +// specialized InternalThing.computedX.setter +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A13InternalThingV9computedXxvs1B07AnotherB0C_Ts5"(%T1B12AnotherThingC* %0, %T1A13InternalThingVy1B07AnotherB0CG* nocapture swiftself dereferenceable({{(4|8)}}) %1) + +// specialized InternalThing.subscript.getter +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc %T1B12AnotherThingC* @"$s1A13InternalThingVyxSicig1B07AnotherB0C_Ts5"([[INT:(i64|i32)]] %0, %T1B12AnotherThingC*{{( returned)?}} %1) + +// specialized InternalThing.subscript.setter +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A13InternalThingVyxSicis1B07AnotherB0C_Ts5"(%T1B12AnotherThingC* %0, [[INT]] %1, %T1A13InternalThingVy1B07AnotherB0CG* nocapture swiftself dereferenceable({{(4|8)}}) %2) + +// specialized InternalRef.compute() +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc %T1B12AnotherThingC* @"$s1A11InternalRefC7computexyF1B12AnotherThingC_Ts5 + +// specialized InternalRef.computedX.getter +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc %T1B12AnotherThingC* @"$s1A11InternalRefC9computedXxvg1B12AnotherThingC_Ts5" + +// specialized InternalRef.computedX.setter +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A11InternalRefC9computedXxvs1B12AnotherThingC_Ts5" + +// specialized InternalRef.subscript.getter +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc %T1B12AnotherThingC* @"$s1A11InternalRefCyxSicig1B12AnotherThingC_Ts5" + +// specialized InternalRef.subscript.setter +// CHECK-B-DAG: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s1A11InternalRefCyxSicis1B12AnotherThingC_Ts5" + + +// Test pre-specialized use. + +// Fragile .swiftmodule +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -emit-module -emit-module-path=%t/A.swiftmodule -module-name A %S/Inputs/pre_specialize_module.swift +// RUN: %target-build-swift -I %t -emit-module -emit-module-path=%t/B.swiftmodule -module-name B %S/Inputs/pre_specialize_module_B.swift +// RUN: %target-swift-frontend -O -I %t -emit-ir -primary-file %s -module-name C | %FileCheck %s -check-prefix=CHECK-C + +// Fragile optimized .swiftmodule +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -O -emit-module -emit-module-path=%t/A.swiftmodule -module-name A %S/Inputs/pre_specialize_module.swift +// RUN: %target-build-swift -O -I %t -emit-module -emit-module-path=%t/B.swiftmodule -module-name B %S/Inputs/pre_specialize_module_B.swift +// RUN: %target-swift-frontend -O -I %t -emit-ir -primary-file %s -module-name C | %FileCheck %s -check-prefix=CHECK-C + +// Resilient .swiftmodule +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -enable-library-evolution -emit-module -emit-module-path=%t/A.swiftmodule -module-name A %S/Inputs/pre_specialize_module.swift +// RUN: %target-build-swift -enable-library-evolution -I %t -emit-module -emit-module-path=%t/B.swiftmodule -module-name B %S/Inputs/pre_specialize_module_B.swift +// RUN: %target-swift-frontend -O -I %t -emit-ir -primary-file %s -module-name C | %FileCheck %s -check-prefix=CHECK-C + +// Resilient optimized .swiftmodule +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -O -enable-library-evolution -emit-module -emit-module-path=%t/A.swiftmodule -module-name A %S/Inputs/pre_specialize_module.swift +// RUN: %target-build-swift -O -enable-library-evolution -I %t -emit-module -emit-module-path=%t/B.swiftmodule -module-name B %S/Inputs/pre_specialize_module_B.swift +// RUN: %target-swift-frontend -O -I %t -emit-ir -primary-file %s -module-name C | %FileCheck %s -check-prefix=CHECK-C + +// .swiftinterface +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -c -enable-library-evolution -emit-module-interface-path %t/A.swiftinterface -module-name A %S/Inputs/pre_specialize_module.swift -o %t/A.o -swift-version 5 +// RUN: %target-build-swift -c -enable-library-evolution -I %t -emit-module-interface-path %t/B.swiftinterface -module-name B %S/Inputs/pre_specialize_module_B.swift -o %t/B.o -swift-version 5 +// RUN: %target-swift-frontend -O -I %t -emit-ir -primary-file %s -module-name C | %FileCheck %s -check-prefix=CHECK-C + +// Optimized .swiftinterface +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -O -c -enable-library-evolution -emit-module-interface-path %t/A.swiftinterface -module-name A %S/Inputs/pre_specialize_module.swift -o %t/A.o -swift-version 5 +// RUN: %target-build-swift -O -c -enable-library-evolution -I %t -emit-module-interface-path %t/B.swiftinterface -module-name B %S/Inputs/pre_specialize_module_B.swift -o %t/B.o -swift-version 5 +// RUN: %target-swift-frontend -O -I %t -emit-ir -primary-file %s -module-name C | %FileCheck %s -check-prefix=CHECK-C + +import A +import B + + +public func testPrespecializedUse() { + + // Test pre-specialization in library A. + + // CHECK-C-LABEL: define{{.*}}s1A18testSpecializationyyxlFSi_Tg5 + // CHECK-C: call{{.*}}s1A13InternalThingV7computexyFAA09Resilienta5BoxedB0VySiG_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingV7computexyFSi_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingV9computedXxvsSi_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingV9computedXxvgSi_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingVyxSicisSi_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingVyxSicigSi_Ts5 + + testSpecialization(5) + + // Test pre-specialization in library B. + + // CHECK-C-LABEL: define{{.*}}s1A18testSpecializationyyxlF1B12AnotherThingC_Tg5 + // CHECK-C: call{{.*}}s1A13InternalThingV7computexyFAA09Resilienta5BoxedB0Vy1B07AnotherB0CG_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingV7computexyF1B07AnotherB0C_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingV9computedXxvs1B07AnotherB0C_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingV9computedXxvg1B07AnotherB0C_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingVyxSicis1B07AnotherB0C_Ts5 + // CHECK-C: call{{.*}}s1A13InternalThingVyxSicig1B07AnotherB0C_Ts5 + + testSpecialization(AnotherThing()) +} diff --git a/test/SILGen/Inputs/specialize_attr_module.swift b/test/SILGen/Inputs/specialize_attr_module.swift new file mode 100644 index 0000000000000..44dcbf3a7465e --- /dev/null +++ b/test/SILGen/Inputs/specialize_attr_module.swift @@ -0,0 +1,102 @@ +@frozen +public struct PublicThing { + @_specialize(exported: true, where T == Double) + @inlinable + public func doStuffWith(_ t: T) { + print(t) + } + public init(_ t: T) {} +} + +@frozen +public struct PublicPlainThing { + public init() {} +} + +@usableFromInline +@frozen +internal struct BoxedThing {} + +@usableFromInline +@frozen +internal struct BoxedThing2 { + var t: T + + init(_ t: T) { + self.t = t + } +} + + +@usableFromInline +@frozen +internal struct InternalThing { + @inlinable + func doStuffWith(_ t: T) { + print(t) + } + + @_specialize(exported: true, where T == Double) + @inlinable + func doStuffWith(boxed: BoxedThing) { + print(boxed) + } + + @inlinable + func doStuffWith(boxed2: BoxedThing2) { + print(boxed2) + } + + @usableFromInline + init(_ t: T) {} +} + +public struct ResilientThing { + public init() {} +} + +@usableFromInline +@frozen +internal struct InternalThing2 { + @usableFromInline + var x : T + + init(_ t: T) { + x = t + } + + @inlinable + var computedX : T { + return x + } + + @inlinable + var computedY : T { + get { + return x + } + set { + x = newValue + } + } + + @inlinable + var computedZ : T { + _modify { + yield &x + } + _read { + yield x + } + } + + @inlinable + subscript(_ i: Int) -> T { + get { + return x + } + set { + x = newValue + } + } +} diff --git a/test/SILGen/Inputs/specialize_attr_module2.swift b/test/SILGen/Inputs/specialize_attr_module2.swift new file mode 100644 index 0000000000000..15449d17b50a9 --- /dev/null +++ b/test/SILGen/Inputs/specialize_attr_module2.swift @@ -0,0 +1,37 @@ +import A + +extension PublicThing { + @_specialize(exported: true, kind: full, target: doStuffWith(_:), where T == Int) + public func specializedoStuff2(_ t: T) {} +} + +@_specializeExtension +extension InternalThing { + @_specialize(exported: true, target:doStuffWith(boxed:), where T == Int) + public func specializedDoStuffWith2(_ t: BoxedThing) {} +} + +@_specializeExtension +extension InternalThing2 { + public var specializeComputedZ : T { + @_specialize(exported: true, target: computedZ, where T == Int) + _modify { + fatalError("don't call") + } + @_specialize(exported: true, target: computedZ, where T == Int) + _read { + fatalError("don't call") + } + } + + public subscript(specialized i: Int) -> T { + @_specialize(exported: true, target: subscript(_:), where T == Int) + get { + fatalError("don't call") + } + @_specialize(exported: true, target: subscript(_:), where T == Int) + set { + fatalError("don't call") + } + } +} diff --git a/test/SILGen/specialize_attr.swift b/test/SILGen/specialize_attr.swift index 33185d824ba9c..dfc55cd4894ac 100644 --- a/test/SILGen/specialize_attr.swift +++ b/test/SILGen/specialize_attr.swift @@ -1,5 +1,26 @@ -// RUN: %target-swift-emit-silgen -module-name specialize_attr -emit-verbose-sil %s | %FileCheck %s -// RUN: %target-swift-emit-sil -sil-verify-all -O -module-name specialize_attr -emit-verbose-sil %s | %FileCheck -check-prefix=CHECK-OPT %s +// Test .swiftmodule with library-evolution +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -module-name A -emit-module-path %t/A.swiftmodule -enable-library-evolution -swift-version 5 %S/Inputs/specialize_attr_module.swift +// RUN: %target-swift-frontend -I %t -module-name B -emit-module-path %t/B.swiftmodule -enable-library-evolution -swift-version 5 %S/Inputs/specialize_attr_module2.swift +// RUN: %target-swift-emit-silgen -I %t -module-name specialize_attr -emit-verbose-sil %s -swift-version 5 | %FileCheck %s +// RUN: %target-swift-emit-sil -I %t -sil-verify-all -O -module-name specialize_attr -emit-verbose-sil %s | %FileCheck -check-prefix=CHECK-OPT -check-prefix=CHECK-OPT-EVO %s + +// Test .swiftinterface +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -o /dev/null -module-name A -emit-module-interface-path %t/A.swiftinterface -enable-library-evolution -swift-version 5 %S/Inputs/specialize_attr_module.swift +// RUN: %target-swift-frontend -emit-module -o /dev/null -I %t -module-name B -emit-module-interface-path %t/B.swiftinterface -enable-library-evolution -swift-version 5 %S/Inputs/specialize_attr_module2.swift +// RUN: %target-swift-emit-silgen -I %t -module-name specialize_attr -emit-verbose-sil %s -swift-version 5 | %FileCheck %s +// RUN: %target-swift-emit-sil -I %t -sil-verify-all -O -module-name specialize_attr -emit-verbose-sil %s | %FileCheck -check-prefix=CHECK-OPT -check-prefix=CHECK-OPT-EVO %s + +// Test .swiftmodule without library-evolution +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -module-name A -emit-module-path %t/A.swiftmodule -swift-version 5 %S/Inputs/specialize_attr_module.swift +// RUN: %target-swift-frontend -I %t -module-name B -emit-module-path %t/B.swiftmodule -swift-version 5 %S/Inputs/specialize_attr_module2.swift +// RUN: %target-swift-emit-silgen -I %t -module-name specialize_attr -emit-verbose-sil %s -swift-version 5 | %FileCheck %s +// RUN: %target-swift-emit-sil -I %t -sil-verify-all -O -module-name specialize_attr -emit-verbose-sil %s | %FileCheck -check-prefix=CHECK-OPT -check-prefix=CHECK-OPT-NOEVO %s + +import A +import B // CHECK: @_specialize(exported: false, kind: full, where T == Int, U == Float) // CHECK-NEXT: @_specialize(exported: false, kind: full, where T == Klass1, U == FakeString) @@ -102,13 +123,23 @@ public struct CC2 { } } +// Import from module A and B. +// CHECK-LABEL: sil [serialized] [_specialize exported: true, kind: full, where T == Double] [_specialize exported: true, kind: full, where T == Int] @$s1A11PublicThingV11doStuffWithyyxF : $@convention(method) <τ_0_0> (@in_guaranteed τ_0_0, PublicThing<τ_0_0>) -> () +// CHECK-LABEL: sil [serialized] [_specialize exported: true, kind: full, where T == Double] [_specialize exported: true, kind: full, where T == Int] @$s1A13InternalThingV11doStuffWith5boxedyAA05BoxedB0VyxG_tF : $@convention(method) <τ_0_0> (BoxedThing<τ_0_0>, InternalThing<τ_0_0>) -> () + +// CHECK-DAG: sil [serialized] [_specialize exported: true, kind: full, where T == Int] @$s1A14InternalThing2V9computedZxvM : $@yield_once @convention(method) <τ_0_0> (@inout InternalThing2<τ_0_0>) -> @yields @inout τ_0_0 +// CHECK-DAG: sil [serialized] [_specialize exported: true, kind: full, where T == Int] @$s1A14InternalThing2V9computedZxvr : $@yield_once @convention(method) <τ_0_0> (@in_guaranteed InternalThing2<τ_0_0>) -> @yields @in_guaranteed τ_0_0 + +// CHECK-DAG: sil [serialized] [_specialize exported: true, kind: full, where T == Int] @$s1A14InternalThing2VyxSicig : $@convention(method) <τ_0_0> (Int, @in_guaranteed InternalThing2<τ_0_0>) -> @out τ_0_0 +// CHECK-DAG: sil [serialized] [_specialize exported: true, kind: full, where T == Int] @$s1A14InternalThing2VyxSicis : $@convention(method) <τ_0_0> (@in τ_0_0, Int, @inout InternalThing2<τ_0_0>) -> () + // CHECK-LABEL: sil [_specialize exported: false, kind: full, where T == Klass1, U == FakeString] [_specialize exported: false, kind: full, where T == Int, U == Float] [ossa] @$s15specialize_attr0A4This_1uyx_q_tr0_lF : $@convention(thin) (@in_guaranteed T, @in_guaranteed U) -> () { -// CHECK-OPT-LABEL: sil shared [noinline] @$s15specialize_attr2CCC3foo_1gqd___AA2GGVyxGtqd___AHtAA2QQRd__lFAA12RRNonTrivialV_AA05SSNonH0VTg5 : $@convention(method) (@guaranteed SSNonTrivial, @guaranteed GG, @guaranteed CC) -> (@owned SSNonTrivial, @out GG) { +// CHECK-OPT-DAG: sil shared [noinline] @$s15specialize_attr2CCC3foo_1gqd___AA2GGVyxGtqd___AHtAA2QQRd__lFAA12RRNonTrivialV_AA05SSNonH0VTg5 : $@convention(method) (@guaranteed SSNonTrivial, @guaranteed GG, @guaranteed CC) -> (@owned SSNonTrivial, @out GG) { -// CHECK-OPT-LABEL: sil shared [noinline] @$s15specialize_attr2CCC4foo2_1gqd___AA2GGVyxGtqd__n_AHntAA2QQRd__lFAA2RRV_AA2SSVTg5 : $@convention(method) (SS, GG, @guaranteed CC) -> (SS, @out GG) { +// CHECK-OPT-DAG: sil shared [noinline] @$s15specialize_attr2CCC4foo2_1gqd___AA2GGVyxGtqd__n_AHntAA2QQRd__lFAA2RRV_AA2SSVTg5 : $@convention(method) (SS, GG, @guaranteed CC) -> (SS, @out GG) { -// CHECK-OPT-LABEL: sil [noinline] @$s15specialize_attr2CCC4foo2_1gqd___AA2GGVyxGtqd__n_AHntAA2QQRd__lF : $@convention(method) (@in U, @in GG, @guaranteed CC) -> (@out U, @out GG) { +// CHECK-OPT-DAG: sil [noinline] @$s15specialize_attr2CCC4foo2_1gqd___AA2GGVyxGtqd__n_AHntAA2QQRd__lF : $@convention(method) (@in U, @in GG, @guaranteed CC) -> (@out U, @out GG) { // CHECK-LABEL: sil [noinline] [_specialize exported: false, kind: full, where T == RRNonTrivial, U == SSNonTrivial] [_specialize exported: false, kind: full, where T == RR, U == SS] [ossa] @$s15specialize_attr2CCC3foo_1gqd___AA2GGVyxGtqd___AHtAA2QQRd__lF : $@convention(method) (@in_guaranteed U, @in_guaranteed GG, @guaranteed CC) -> (@out U, @out GG) { @@ -188,3 +219,125 @@ public class Addressable : TestSubscriptable { // Addressable.subscript.modify with no attribute // CHECK-LABEL: sil [transparent] [serialized] [ossa] @$s15specialize_attr11AddressableCyxSiciM : $@yield_once @convention(method) (Int, @guaranteed Addressable) -> @yields @inout Element { + +public struct TestPrespecialized { + // CHECK-LABEL: sil [_specialize exported: true, kind: full, where T == Double] [_specialize exported: true, kind: full, where T == Int] [ossa] @$s15specialize_attr18TestPrespecializedV5aFuncyyF : $@convention(method) (TestPrespecialized) -> () { + @_specialize(exported: true, where T == Int) + @_specialize(exported: true, where T == Double) + public func aFunc() {} +} + +extension TestPrespecialized { + // CHECK-LABEL: sil [_specialize exported: true, kind: full, target: "$s15specialize_attr18TestPrespecializedV5aFuncyyF", where T == Int32] [ossa] @$s15specialize_attr18TestPrespecializedV0A6TargetyyF : $@convention(method) (TestPrespecialized) -> () { + @_specialize(exported: true, kind: full, target: aFunc(), where T == Int32) + public func specializeTarget() {} +} + +extension PublicThing { + // CHECK-LABEL: sil [_specialize exported: true, kind: full, target: "$s1A11PublicThingV11doStuffWithyyxF", where T == PublicPlainThing] [_specialize exported: true, kind: full, target: "$s1A11PublicThingV11doStuffWithyyxF", where T == Float] [ossa] @$s1A11PublicThingV15specialize_attrE17specializedoStuffyyxF : $@convention(method) (@in_guaranteed T, PublicThing) -> () { + + // CHECK-OPT-DAG: sil @$s1A11PublicThingV11doStuffWithyyxFAA0a5PlainB0V_Ts5 : $@convention(method) (PublicPlainThing, PublicThing) -> () { + // CHECK-OPT-DAG: sil @$s1A11PublicThingV11doStuffWithyyxFSf_Ts5 : $@convention(method) (Float, PublicThing) -> () { + + @_specialize(exported: true, kind: full, target: doStuffWith(_:), where T == Float) + @_specialize(exported: true, kind: full, target: doStuffWith(_:), where T == PublicPlainThing) + public func specializedoStuff(_ t: T) {} +} + +@_specializeExtension +extension InternalThing { + + // CHECK-LABEL: sil [_specialize exported: true, kind: full, target: "$s1A13InternalThingV11doStuffWithyyxF", where T == PublicPlainThing] [ossa] @$s1A13InternalThingV15specialize_attrE18specializedDoStuffyyxF : $@convention(method) (@in_guaranteed T, InternalThing) -> () { + // CHECK-OPT-DAG: sil @$s1A13InternalThingV11doStuffWithyyxFAA011PublicPlainB0V_Ts5 : $@convention(method) (PublicPlainThing, InternalThing) -> () { + + @_specialize(exported: true, kind: full, target: doStuffWith(_:), where T == PublicPlainThing) + public func specializedDoStuff(_ t: T) {} + + // CHECK-LABEL: sil [_specialize exported: true, kind: full, target: "$s1A13InternalThingV11doStuffWith5boxedyAA05BoxedB0VyxG_tF", where T == PublicPlainThing] [ossa] @$s1A13InternalThingV15specialize_attrE22specializedDoStuffWith5boxedyAA05BoxedB0VyxG_tF : $@convention(method) (BoxedThing, InternalThing) -> () { + // CHECK-OPT-DAG: sil @$s1A13InternalThingV11doStuffWith5boxedyAA05BoxedB0VyxG_tFAA011PublicPlainB0V_Ts5 : $@convention(method) (BoxedThing, InternalThing) -> () { + + @_specialize(exported: true, target:doStuffWith(boxed:), where T == PublicPlainThing) + public func specializedDoStuffWith(boxed: BoxedThing) {} + + // CHECK-LABEL: sil [_specialize exported: true, kind: full, target: "$s1A13InternalThingV11doStuffWith6boxed2yAA11BoxedThing2VyxG_tF", where T == ResilientThing] [_specialize exported: true, kind: full, target: "$s1A13InternalThingV11doStuffWith6boxed2yAA11BoxedThing2VyxG_tF", where T == Int16] [_specialize exported: true, kind: full, target: "$s1A13InternalThingV11doStuffWith6boxed2yAA11BoxedThing2VyxG_tF", where T == Klass1] [ossa] @$s1A13InternalThingV15specialize_attrE23specializedDoStuffWith26boxed2yAA11BoxedThing2VyxG_tF : $@convention(method) (@in_guaranteed BoxedThing2, InternalThing) -> () { + // CHECK-OPT-EVO-DAG: sil @$s1A13InternalThingV11doStuffWith6boxed2yAA11BoxedThing2VyxG_tFAA09ResilientB0V_Ts5 : $@convention(method) (@in_guaranteed BoxedThing2, InternalThing) -> () { + // CHECK-OPT-NOEVO-DAG: sil @$s1A13InternalThingV11doStuffWith6boxed2yAA11BoxedThing2VyxG_tFAA09ResilientB0V_Ts5 : $@convention(method) (BoxedThing2, InternalThing) -> () { + // CHECK-OPT-DAG: sil @$s1A13InternalThingV11doStuffWith6boxed2yAA11BoxedThing2VyxG_tFs5Int16V_Ts5 : $@convention(method) (BoxedThing2, InternalThing) -> () { + // CHECK-OPT-DAG: sil @$s1A13InternalThingV11doStuffWith6boxed2yAA11BoxedThing2VyxG_tF15specialize_attr6Klass1C_Ts5 : $@convention(method) (@guaranteed BoxedThing2, InternalThing) -> () { + + @_specialize(exported: true, target:doStuffWith(boxed2:), where T == Klass1) + @_specialize(exported: true, target:doStuffWith(boxed2:), where T == Int16) + @_specialize(exported: true, target:doStuffWith(boxed2:), where T == ResilientThing) + public func specializedDoStuffWith2(boxed2: BoxedThing2) {} +} + + +@_specializeExtension +extension InternalThing2 { + + public var specializeComputedX : T { + + // CHECK-LABEL: sil [_specialize exported: true, kind: full, target: "$s1A14InternalThing2V9computedXxvg", where T == Klass1] [ossa] @$s1A14InternalThing2V15specialize_attrE0C9ComputedXxvg : $@convention(method) (@in_guaranteed InternalThing2) -> @out T { + // CHECK-OPT-DAG: sil @$s1A14InternalThing2V9computedXxvg15specialize_attr6Klass1C_Ts5 : $@convention(method) (@guaranteed InternalThing2) -> @owned Klass1 { + + @_specialize(exported: true, target: computedX, where T == Klass1) + get { + fatalError("don't call") + } + } + + public var specializeComputedY : T { + + // CHECK-LABEL: sil [_specialize exported: true, kind: full, target: "$s1A14InternalThing2V9computedYxvg", where T == Klass1] [ossa] @$s1A14InternalThing2V15specialize_attrE0C9ComputedYxvg : $@convention(method) (@in_guaranteed InternalThing2) -> @out T { + // CHECK-OPT-DAG: sil @$s1A14InternalThing2V9computedYxvg15specialize_attr6Klass1C_Ts5 : $@convention(method) (@guaranteed InternalThing2) -> @owned Klass1 { + + @_specialize(exported: true, target: computedY, where T == Klass1) + get { + fatalError("don't call") + } + + // CHECK-LABEL: sil [_specialize exported: true, kind: full, target: "$s1A14InternalThing2V9computedYxvs", where T == Klass1] [ossa] @$s1A14InternalThing2V15specialize_attrE0C9ComputedYxvs : $@convention(method) (@in T, @inout InternalThing2) -> () { + // CHECK-OPT-DAG: sil @$s1A14InternalThing2V9computedYxvs15specialize_attr6Klass1C_Ts5 : $@convention(method) (@owned Klass1, @inout InternalThing2) -> () { + + @_specialize(exported: true, target: computedY, where T == Klass1) + set {} + } + + public var specializeComputedZ : T { + + // CHECK-LABEL: sil [_specialize exported: true, kind: full, target: "$s1A14InternalThing2V9computedZxvM", where T == Klass1] [ossa] @$s1A14InternalThing2V15specialize_attrE0C9ComputedZxvM : $@yield_once @convention(method) (@inout InternalThing2) -> @yields @inout T { + // CHECK-OPT-DAG: sil @$s1A14InternalThing2V9computedZxvM15specialize_attr6Klass1C_Ts5 : $@yield_once @convention(method) (@inout InternalThing2) -> @yields @inout Klass1 { + + @_specialize(exported: true, target: computedZ, where T == Klass1) + _modify { + fatalError("don't call") + } + + // CHECK-LABEL: sil [_specialize exported: true, kind: full, target: "$s1A14InternalThing2V9computedZxvr", where T == Klass1] [ossa] @$s1A14InternalThing2V15specialize_attrE0C9ComputedZxvr : $@yield_once @convention(method) (@in_guaranteed InternalThing2) -> @yields @in_guaranteed T { + // CHECK-OPT-DAG: sil @$s1A14InternalThing2V9computedZxvr15specialize_attr6Klass1C_Ts5 : $@yield_once @convention(method) (@guaranteed InternalThing2) -> @yields @in_guaranteed Klass1 { + + @_specialize(exported: true, target: computedZ, where T == Klass1) + _read { + fatalError("don't call") + } + } + + public subscript(specialized i: Int) -> T { + + // CHECK-LABEL: sil [_specialize exported: true, kind: full, target: "$s1A14InternalThing2VyxSicig", where T == Klass1] [ossa] @$s1A14InternalThing2V15specialize_attrE11specializedxSi_tcig : $@convention(method) (Int, @in_guaranteed InternalThing2) -> @out T { + // CHECK-OPT-DAG: sil @$s1A14InternalThing2VyxSicig15specialize_attr6Klass1C_Ts5 : $@convention(method) (Int, @guaranteed InternalThing2) -> @owned Klass1 { + + @_specialize(exported: true, target: subscript(_:), where T == Klass1) + get { + fatalError("don't call") + } + + // CHECK-LABEL: sil [_specialize exported: true, kind: full, target: "$s1A14InternalThing2VyxSicis", where T == Klass1] [ossa] @$s1A14InternalThing2V15specialize_attrE11specializedxSi_tcis : $@convention(method) (@in T, Int, @inout InternalThing2) -> () { + // CHECK-OPT-DAG: sil @$s1A14InternalThing2VyxSicis15specialize_attr6Klass1C_Ts5 : $@convention(method) (@owned Klass1, Int, @inout InternalThing2) -> () { + + @_specialize(exported: true, target: subscript(_:), where T == Klass1) + set { + fatalError("don't call") + } + } +} diff --git a/test/SILOptimizer/Inputs/pre_specialized_module.swift b/test/SILOptimizer/Inputs/pre_specialized_module.swift new file mode 100644 index 0000000000000..3eee2f982ad45 --- /dev/null +++ b/test/SILOptimizer/Inputs/pre_specialized_module.swift @@ -0,0 +1,96 @@ +@_specialize(exported: true, where T == Int) +@_specialize(exported: true, where T == Double) +public func publicPrespecialized(_ t: T) { +} + +@_specialize(exported: true, where T == Int) +@_specialize(exported: true, where T == Double) +@_alwaysEmitIntoClient +internal func internalEmitIntoClientPrespecialized(_ t: T) { +} + +@inlinable +public func useInternalEmitIntoClientPrespecialized(_ t: T) { + internalEmitIntoClientPrespecialized(t) +} + +@inlinable +public func publicInlineable(_ t: T) { +} + +public struct ResilientThing { + public init() {} +} + +@usableFromInline +@frozen +internal struct InternalThing2 { + @usableFromInline + var x : T + + @usableFromInline + init(_ t: T) { + x = t + } + + @_specialize(exported: true, where T == Int) + @inlinable + func compute() -> T { + return x + } + + @inlinable + var computedX : T { + @_specialize(exported: true, where T == Int) + get { + return x + } + } + + @inlinable + var computedY : T { + @_specialize(exported: true, where T == Int) + get { + return x + } + @_specialize(exported: true, where T == Int) + set { + x = newValue + } + } + + @inlinable + var computedZ : T { + @_specialize(exported: true, where T == Int) + _modify { + yield &x + } + @_specialize(exported: true, where T == Int) + _read { + yield x + } + } + @inlinable + subscript(_ i: Int) -> T { + @_specialize(exported: true, where T == Int) + get { + return x + } + @_specialize(exported: true, where T == Int) + set { + } + } +} + +@inlinable +public func useInternalThing(_ t: T) { + var x = InternalThing2(t) + print(x.compute()) + print(x.computedX) + x.computedY = t + print(x.computedY) + x.computedZ = t + print(x.computedZ) + x[1] = t + print(x[1]) +} diff --git a/test/SILOptimizer/pre_specialize.swift b/test/SILOptimizer/pre_specialize.swift new file mode 100644 index 0000000000000..32eadcf33d85a --- /dev/null +++ b/test/SILOptimizer/pre_specialize.swift @@ -0,0 +1,90 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module-path %t/pre_specialized_module.swiftmodule %S/Inputs/pre_specialized_module.swift +// RUN: %target-swift-frontend -I %t -O -emit-sil %s | %FileCheck %s --check-prefix=OPT +// RUN: %target-swift-frontend -I %t -Onone -emit-sil %s | %FileCheck %s --check-prefix=NONE + + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -O -emit-module-path %t/pre_specialized_module.swiftmodule %S/Inputs/pre_specialized_module.swift +// RUN: %target-swift-frontend -I %t -O -emit-sil %s | %FileCheck %s --check-prefix=OPT + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -O -enable-library-evolution -emit-module-path %t/pre_specialized_module.swiftmodule %S/Inputs/pre_specialized_module.swift +// RUN: %target-swift-frontend -I %t -O -emit-sil %s | %FileCheck %s --check-prefix=OPT + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -O -swift-version 5 -enable-library-evolution -emit-module -o /dev/null -emit-module-interface-path %t/pre_specialized_module.swiftinterface %S/Inputs/pre_specialized_module.swift +// RUN: %target-swift-frontend -I %t -O -emit-sil %s | %FileCheck %s --check-prefix=OPT + +import pre_specialized_module + +// Make sure we generate the public pre-specialized entry points. + +// OPT: sil @$s14pre_specialize10testPublic1tyx_tlFSf_Ts5 : $@convention(thin) (Float) -> () { +// OPT: sil @$s14pre_specialize10testPublic1tyx_tlFSi_Ts5 : $@convention(thin) (Int) -> () { + +// NONE: sil @$s14pre_specialize10testPublic1tyx_tlFSf_Ts5 : $@convention(thin) (Float) -> () { +// NONE: sil @$s14pre_specialize10testPublic1tyx_tlFSi_Ts5 : $@convention(thin) (Int) -> () { + +@_specialize(exported: true, where T == Int) +@_specialize(exported: true, where T == Float) +public func testPublic(t: T) { + print(t) +} + +// OPT: sil @$s14pre_specialize18testEmitIntoClient1tyx_tlFSf_Ts5 : $@convention(thin) (Float) -> () { +// OPT: sil @$s14pre_specialize18testEmitIntoClient1tyx_tlFSi_Ts5 : $@convention(thin) (Int) -> () { + +// NONE: sil @$s14pre_specialize18testEmitIntoClient1tyx_tlFSf_Ts5 : $@convention(thin) (Float) -> () { +// NONE: sil @$s14pre_specialize18testEmitIntoClient1tyx_tlFSi_Ts5 : $@convention(thin) (Int) -> () { + +@_specialize(exported: true, where T == Int) +@_specialize(exported: true, where T == Float) +@_alwaysEmitIntoClient +internal func testEmitIntoClient(t: T) { + print(t) +} + +// OPT: sil @$s14pre_specialize28usePrespecializedEntryPointsyyF : $@convention(thin) () -> () { +// OPT: [[F1:%.*]] = function_ref @$s22pre_specialized_module20publicPrespecializedyyxlFSi_Ts5 : $@convention(thin) (Int) -> () +// OPT: apply [[F1]] +// OPT: [[F2:%.*]] = function_ref @$s22pre_specialized_module20publicPrespecializedyyxlFSd_Ts5 : $@convention(thin) (Double) -> () +// OPT: apply [[F2]] +// OPT: [[F3:%.*]] = function_ref @$s22pre_specialized_module36internalEmitIntoClientPrespecializedyyxlFSi_Ts5 : $@convention(thin) (Int) -> () +// OPT: apply [[F3]] +// OPT: [[F4:%.*]] = function_ref @$s22pre_specialized_module36internalEmitIntoClientPrespecializedyyxlFSd_Ts5 : $@convention(thin) (Double) -> () +// OPT: apply [[F4]] +// OPT: [[F5:%.*]] = function_ref @$s22pre_specialized_module16useInternalThingyyxlFSi_Tg5 +// OPT: apply [[F5]]({{.*}}) : $@convention(thin) (Int) -> () +// OPT: } // end sil function '$s14pre_specialize28usePrespecializedEntryPointsyyF' + +// OPT: sil {{.*}} @$s22pre_specialized_module16useInternalThingyyxlFSi_Tg5 : $@convention(thin) (Int) -> () { +// OPT: [[F1:%.*]] = function_ref @$s22pre_specialized_module14InternalThing2V7computexyFSi_Ts5 : $@convention(method) (InternalThing2) -> Int +// OPT: apply [[F1]]( +// OPT: [[F2:%.*]] = function_ref @$s22pre_specialized_module14InternalThing2V9computedXxvgSi_Ts5 : $@convention(method) (InternalThing2) -> Int +// OPT: apply [[F2]]( +// OPT: [[F3:%.*]] = function_ref @$s22pre_specialized_module14InternalThing2V9computedYxvsSi_Ts5 : $@convention(method) (Int, @inout InternalThing2) -> () +// OPT: apply [[F3]]( +// OPT: [[F4:%.*]] = function_ref @$s22pre_specialized_module14InternalThing2V9computedYxvgSi_Ts5 : $@convention(method) (InternalThing2) -> Int +// OPT: apply [[F4]]( +// OPT: [[F5:%.*]] = function_ref @$s22pre_specialized_module14InternalThing2V9computedZxvMSi_Ts5 : $@yield_once @convention(method) (@inout InternalThing2) -> @yields @inout Int +// OPT: begin_apply [[F5]]( +// OPT: [[F6:%.*]] = function_ref @$s22pre_specialized_module14InternalThing2V9computedZxvrSi_Ts5 : $@yield_once @convention(method) (InternalThing2) -> @yields @in_guaranteed Int +// OPT: begin_apply [[F6]]( +// OPT: [[F7:%.*]] = function_ref @$s22pre_specialized_module14InternalThing2VyxSicisSi_Ts5 : $@convention(method) (Int, Int, @inout InternalThing2) -> () +// OPT: apply [[F7]]( +// OPT: [[F8:%.*]] = function_ref @$s22pre_specialized_module14InternalThing2VyxSicigSi_Ts5 : $@convention(method) (Int, InternalThing2) -> Int +// OPT: apply [[F8]]( +// OPT: } // end sil function '$s22pre_specialized_module16useInternalThingyyxlFSi_Tg5' + +public func usePrespecializedEntryPoints() { + publicPrespecialized(1) + publicPrespecialized(1.0) + useInternalEmitIntoClientPrespecialized(2) + useInternalEmitIntoClientPrespecialized(2.0) + useInternalThing(2) +} +// OPT: sil [signature_optimized_thunk] [always_inline] @$s22pre_specialized_module16publicInlineableyyxlFSd_Ts5 : $@convention(thin) (Double) -> () { +// NONE: sil @$s22pre_specialized_module16publicInlineableyyxlFSd_Ts5 : $@convention(thin) (Double) -> () { +@_specialize(exported: true, target: publicInlineable(_:), where T == Double) +public func specializeTarget(_ t: T) {} diff --git a/test/TBD/specialize_verify.swift b/test/TBD/specialize_verify.swift index 4b3620424d2d1..e7942965a5c73 100644 --- a/test/TBD/specialize_verify.swift +++ b/test/TBD/specialize_verify.swift @@ -1,7 +1,7 @@ // REQUIRES: VENDOR=apple // RUN: %target-swift-frontend -emit-ir -o/dev/null -O -module-name test -validate-tbd-against-ir=missing %s -@_specialize(exported: true, where T: _Trivial) +@_specialize(exported: true, where T == Float) public func foo(_ x : T) -> T { return x } diff --git a/test/attr/attr_specialize.swift b/test/attr/attr_specialize.swift index 007b0639d45ce..9c329e603ce5a 100644 --- a/test/attr/attr_specialize.swift +++ b/test/attr/attr_specialize.swift @@ -128,7 +128,7 @@ public func funcWithTwoGenericParameters(x: X, y: Y) { } @_specialize(where X == Int, Y == Int) -@_specialize(exported: true, where X == Int, Y == Int) // expected-warning{{'exported: true' has no effect in '_specialize' attribute}} +@_specialize(exported: true, where X == Int, Y == Int) @_specialize(exported: false, where X == Int, Y == Int) @_specialize(exported: false where X == Int, Y == Int) // expected-error{{missing ',' in '_specialize' attribute}} @_specialize(exported: yes, where X == Int, Y == Int) // expected-error{{expected a boolean true or false value in '_specialize' attribute}} @@ -143,9 +143,9 @@ public func funcWithTwoGenericParameters(x: X, y: Y) { @_specialize(kind: partial, where X == Int, Y == Int) @_specialize(kind: , where X == Int, Y == Int) -@_specialize(exported: true, kind: partial, where X == Int, Y == Int) // expected-warning{{'exported: true' has no effect in '_specialize' attribute}} -@_specialize(exported: true, exported: true, where X == Int, Y == Int) // expected-error{{parameter 'exported' was already defined in '_specialize' attribute}} expected-warning2{{'exported: true' has no effect in '_specialize' attribute}} -@_specialize(kind: partial, exported: true, where X == Int, Y == Int) // expected-warning{{'exported: true' has no effect in '_specialize' attribute}} +@_specialize(exported: true, kind: partial, where X == Int, Y == Int) +@_specialize(exported: true, exported: true, where X == Int, Y == Int) // expected-error{{parameter 'exported' was already defined in '_specialize' attribute}} +@_specialize(kind: partial, exported: true, where X == Int, Y == Int) @_specialize(kind: partial, kind: partial, where X == Int, Y == Int) // expected-error{{parameter 'kind' was already defined in '_specialize' attribute}} @_specialize(where X == Int, Y == Int, exported: true, kind: partial) // expected-error{{cannot find type 'exported' in scope}} expected-error{{cannot find type 'kind' in scope}} expected-error{{cannot find type 'partial' in scope}} expected-error{{expected type}} @@ -200,22 +200,22 @@ public func simpleGeneric(t: T) -> T { } -@_specialize(exported: true, where S: _Trivial(64)) // expected-warning{{'exported: true' has no effect in '_specialize' attribute}} +@_specialize(exported: true, where S: _Trivial(64)) // Check that any bitsize size is OK, not only powers of 8. @_specialize(where S: _Trivial(60)) -@_specialize(exported: true, where S: _RefCountedObject) // expected-warning{{'exported: true' has no effect in '_specialize' attribute}} +@_specialize(exported: true, where S: _RefCountedObject) @inline(never) public func copyValue(_ t: S, s: inout S) -> Int64 where S: P{ return 1 } -@_specialize(exported: true, where S: _Trivial) // expected-warning{{'exported: true' has no effect in '_specialize' attribute}} -@_specialize(exported: true, where S: _Trivial(64)) // expected-warning{{'exported: true' has no effect in '_specialize' attribute}} -@_specialize(exported: true, where S: _Trivial(32)) // expected-warning{{'exported: true' has no effect in '_specialize' attribute}} -@_specialize(exported: true, where S: _RefCountedObject) // expected-warning{{'exported: true' has no effect in '_specialize' attribute}} -@_specialize(exported: true, where S: _NativeRefCountedObject) // expected-warning{{'exported: true' has no effect in '_specialize' attribute}} -@_specialize(exported: true, where S: _Class) // expected-warning{{'exported: true' has no effect in '_specialize' attribute}} -@_specialize(exported: true, where S: _NativeClass) // expected-warning{{'exported: true' has no effect in '_specialize' attribute}} +@_specialize(exported: true, where S: _Trivial) +@_specialize(exported: true, where S: _Trivial(64)) +@_specialize(exported: true, where S: _Trivial(32)) +@_specialize(exported: true, where S: _RefCountedObject) +@_specialize(exported: true, where S: _NativeRefCountedObject) +@_specialize(exported: true, where S: _Class) +@_specialize(exported: true, where S: _NativeClass) @inline(never) public func copyValueAndReturn(_ t: S, s: inout S) -> S where S: P{ return s @@ -234,7 +234,7 @@ struct OuterStruct { } // Check _TrivialAtMostN constraints. -@_specialize(exported: true, where S: _TrivialAtMost(64)) // expected-warning{{'exported: true' has no effect in '_specialize' attribute}} +@_specialize(exported: true, where S: _TrivialAtMost(64)) @inline(never) public func copy2(_ t: S, s: inout S) -> S where S: P{ return s @@ -279,3 +279,21 @@ public struct MyStruct4 : P2 { @_specialize(where T==MyStruct4) public func foo(_ t: T) where T.DP2.DP11 == H { } + +public func targetFun(_ t: T) {} + +@_specialize(exported: true, target: targetFun(_:), where T == Int) +public func specifyTargetFunc(_ t: T) { +} + +public struct Container { + public func targetFun(_ t: T) {} +} + +extension Container { + @_specialize(exported: true, target: targetFun(_:), where T == Int) + public func specifyTargetFunc(_ t: T) { } + + @_specialize(exported: true, target: targetFun2(_:), where T == Int) // expected-error{{target function 'targetFun2' could not be found}} + public func specifyTargetFunc2(_ t: T) { } +} diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp index 746c13ea30f9f..f6c0fbd42f7bd 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp @@ -510,7 +510,8 @@ void walkRelatedDecls(const ValueDecl *VD, const FnTy &Fn) { VD->getBaseName(), results, NLKind::UnqualifiedLookup, namelookup::ResolutionKind::Overloadable, - DC->getModuleScopeContext()); + DC->getModuleScopeContext(), + NL_UnqualifiedDefault); } SmallVector RelatedDecls; diff --git a/tools/swift-demangle/swift-demangle.cpp b/tools/swift-demangle/swift-demangle.cpp index 86a849a67e09e..4c501846fe9a4 100644 --- a/tools/swift-demangle/swift-demangle.cpp +++ b/tools/swift-demangle/swift-demangle.cpp @@ -118,6 +118,7 @@ static void stripSpecialization(NodePointer Node) { switch (Node->getFirstChild()->getKind()) { case Node::Kind::FunctionSignatureSpecialization: case Node::Kind::GenericSpecialization: + case Node::Kind::GenericSpecializationPrespecialized: case Node::Kind::GenericSpecializationNotReAbstracted: case Node::Kind::GenericPartialSpecialization: case Node::Kind::GenericPartialSpecializationNotReAbstracted: diff --git a/utils/gyb_syntax_support/AttributeNodes.py b/utils/gyb_syntax_support/AttributeNodes.py index 7d5bdabaead7d..e24bee579ec7c 100644 --- a/utils/gyb_syntax_support/AttributeNodes.py +++ b/utils/gyb_syntax_support/AttributeNodes.py @@ -102,6 +102,7 @@ element='Syntax', element_name='SpecializeAttribute', element_choices=[ 'LabeledSpecializeEntry', + 'TargetFunctionEntry', 'GenericWhereClause', ]), @@ -125,6 +126,28 @@ A trailing comma if this argument is followed by another one '''), ]), + # Representation of e.g. 'exported: true,' + # labeled-specialize-entry -> identifier ':' token ','? + Node('TargetFunctionEntry', kind='Syntax', + description=''' + A labeled argument for the `@_specialize` attribute with a function + decl value like + `target: myFunc(_:)` + ''', + traits=['WithTrailingComma'], + children=[ + Child('Label', kind='IdentifierToken', + description='The label of the argument'), + Child('Colon', kind='ColonToken', + description='The colon separating the label and the value'), + Child('Delcname', kind='DeclName', + description='The value for this argument'), + Child('TrailingComma', kind='CommaToken', + is_optional=True, description=''' + A trailing comma if this argument is followed by another one + '''), + ]), + # The argument of '@_dynamic_replacement(for:)' or '@_private(sourceFile:)' # named-attribute-string-arg -> 'name': string-literal Node('NamedAttributeStringArgument', kind='Syntax', diff --git a/utils/gyb_syntax_support/NodeSerializationCodes.py b/utils/gyb_syntax_support/NodeSerializationCodes.py index 77caef24eacfa..18b17227a4967 100644 --- a/utils/gyb_syntax_support/NodeSerializationCodes.py +++ b/utils/gyb_syntax_support/NodeSerializationCodes.py @@ -250,6 +250,7 @@ 'MultipleTrailingClosureElementList': 245, 'MultipleTrailingClosureElement': 246, 'PoundFileIDExpr': 247, + 'TargetFunctionEntry': 248, } From 2a2cf91dcdf3fd5e50624eebfd3a0dc57b50ae0a Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Fri, 9 Oct 2020 07:34:05 -0700 Subject: [PATCH 414/745] Add support for marking a _specialize attribute as SPI ``` @_specialize(exported: true, spi: SPIGroupName, where T == Int) public func myFunc() { } ``` The specialized entry point is only visible for modules that import using `_spi(SPIGroupName) import ModuleDefiningMyFunc `. rdar://64993425 --- include/swift/AST/Attr.h | 24 ++- include/swift/AST/DiagnosticsParse.def | 2 + include/swift/AST/Module.h | 7 + include/swift/Parse/Parser.h | 11 +- include/swift/SIL/SILFunction.h | 16 +- lib/AST/Attr.cpp | 63 ++++--- lib/AST/Module.cpp | 25 +++ lib/Parse/ParseDecl.cpp | 40 +++- lib/SIL/IR/SILFunction.cpp | 24 +-- lib/SIL/IR/SILFunctionBuilder.cpp | 24 ++- lib/SIL/IR/SILPrinter.cpp | 6 + lib/SIL/Parser/ParseSIL.cpp | 19 +- lib/SILGen/SILGen.cpp | 17 +- lib/SILOptimizer/Utils/Generics.cpp | 11 ++ lib/Serialization/Deserialization.cpp | 25 ++- lib/Serialization/DeserializeSIL.cpp | 18 +- lib/Serialization/ModuleFormat.h | 7 +- lib/Serialization/SILFormat.h | 4 +- lib/Serialization/Serialization.cpp | 23 ++- lib/Serialization/Serialization.h | 3 + lib/Serialization/SerializeSIL.cpp | 12 +- test/SPI/Inputs/spi_helper.swift | 17 +- ...lient_use_multiple_module_specialize.swift | 172 ++++++++++++++++++ test/SPI/private_swiftinterface.swift | 16 ++ test/SPI/spi_symbols.swift | 2 + 25 files changed, 508 insertions(+), 80 deletions(-) create mode 100644 test/SPI/client_use_multiple_module_specialize.swift diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index f5fee5fa36c12..b442cb8cf934a 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -1408,8 +1408,11 @@ class SynthesizedProtocolAttr : public DeclAttribute { /// The @_specialize attribute, which forces specialization on the specified /// type list. -class SpecializeAttr : public DeclAttribute { +class SpecializeAttr final + : public DeclAttribute, + private llvm::TrailingObjects { friend class SpecializeAttrTargetDeclRequest; + friend TrailingObjects; public: // NOTE: When adding new kinds, you must update the inline bitfield macro. @@ -1425,32 +1428,45 @@ class SpecializeAttr : public DeclAttribute { DeclNameRef targetFunctionName; LazyMemberLoader *resolver = nullptr; uint64_t resolverContextData; + size_t numSPIGroups; SpecializeAttr(SourceLoc atLoc, SourceRange Range, TrailingWhereClause *clause, bool exported, - SpecializationKind kind, - GenericSignature specializedSignature, - DeclNameRef targetFunctionName); + SpecializationKind kind, GenericSignature specializedSignature, + DeclNameRef targetFunctionName, + ArrayRef spiGroups); public: static SpecializeAttr *create(ASTContext &Ctx, SourceLoc atLoc, SourceRange Range, TrailingWhereClause *clause, bool exported, SpecializationKind kind, DeclNameRef targetFunctionName, + ArrayRef spiGroups, GenericSignature specializedSignature = nullptr); static SpecializeAttr *create(ASTContext &ctx, bool exported, SpecializationKind kind, + ArrayRef spiGroups, GenericSignature specializedSignature, DeclNameRef replacedFunction); static SpecializeAttr *create(ASTContext &ctx, bool exported, SpecializationKind kind, + ArrayRef spiGroups, GenericSignature specializedSignature, DeclNameRef replacedFunction, LazyMemberLoader *resolver, uint64_t data); + /// Name of SPIs declared by the attribute. + /// + /// Note: A single SPI name per attribute is currently supported but this + /// may change with the syntax change. + ArrayRef getSPIGroups() const { + return { this->template getTrailingObjects(), + numSPIGroups }; + } + TrailingWhereClause *getTrailingWhereClause() const; GenericSignature getSpecializedSignature() const { diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 392241cf039a2..be7c1d83ee465 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1583,6 +1583,8 @@ ERROR(attr_specialize_expected_partial_or_full,none, "expected 'partial' or 'full' as values of the 'kind' parameter in '_specialize' attribute", ()) ERROR(attr_specialize_expected_function,none, "expected a function name as the value of the 'target' parameter in '_specialize' attribute", ()) +ERROR(attr_specialize_expected_spi_name,none, + "expected an SPI identifier as the value of the 'spi' parameter in '_specialize' attribute", ()) // _implements ERROR(attr_implements_expected_member_name,PointsToFirstBadToken, diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index d9c6dc675b40d..a4cf40c60f6fa 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -578,6 +578,13 @@ class ModuleDecl : public DeclContext, public TypeDecl { const ModuleDecl *importedModule, llvm::SmallSetVector &spiGroups) const; + // Is \p attr accessible as an explictly imported SPI from this module? + bool isImportedAsSPI(const SpecializeAttr *attr, + const ValueDecl *targetDecl) const; + + // Is \p spiGroup accessible as an explictly imported SPI from this module? + bool isImportedAsSPI(Identifier spiGroup, const ModuleDecl *fromModule) const; + /// \sa getImportedModules enum class ImportFilterKind { /// Include imports declared with `@_exported`. diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 5118e6c00bd22..e719b4ed183b6 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1017,16 +1017,19 @@ class Parser { bool parseSpecializeAttribute( swift::tok ClosingBrace, SourceLoc AtLoc, SourceLoc Loc, SpecializeAttr *&Attr, - llvm::function_ref parseSILTargetName = [](Parser &) { - return false; - }); + llvm::function_ref parseSILTargetName = + [](Parser &) { return false; }, + llvm::function_ref parseSILSIPModule = + [](Parser &) { return false; }); /// Parse the arguments inside the @_specialize attribute bool parseSpecializeAttributeArguments( swift::tok ClosingBrace, bool &DiscardAttribute, Optional &Exported, Optional &Kind, TrailingWhereClause *&TrailingWhereClause, DeclNameRef &targetFunction, - llvm::function_ref parseSILTargetName); + SmallVectorImpl &spiGroups, + llvm::function_ref parseSILTargetName, + llvm::function_ref parseSILSIPModule); /// Parse the @_implements attribute. /// \p Attr is where to store the parsed attribute diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index 782e85c96be91..e5a518e5c8cfe 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -72,7 +72,8 @@ class SILSpecializeAttr final { static SILSpecializeAttr *create(SILModule &M, GenericSignature specializedSignature, bool exported, SpecializationKind kind, - SILFunction *target); + SILFunction *target, Identifier spiGroup, + const ModuleDecl *spiModule); bool isExported() const { return exported; @@ -102,17 +103,28 @@ class SILSpecializeAttr final { return targetFunction; } + Identifier getSPIGroup() const { + return spiGroup; + } + + const ModuleDecl *getSPIModule() const { + return spiModule; + } + void print(llvm::raw_ostream &OS) const; private: SpecializationKind kind; bool exported; GenericSignature specializedSignature; + Identifier spiGroup; + const ModuleDecl *spiModule = nullptr; SILFunction *F = nullptr; SILFunction *targetFunction = nullptr; SILSpecializeAttr(bool exported, SpecializationKind kind, - GenericSignature specializedSignature, SILFunction *target); + GenericSignature specializedSignature, SILFunction *target, + Identifier spiGroup, const ModuleDecl *spiModule); }; /// SILFunction - A function body that has been lowered to SIL. This consists of diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 47223ea350caa..fd9ecb13d9116 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -898,12 +898,20 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, } case DAK_Specialize: { - Printer << "@" << getAttrName() << "("; auto *attr = cast(this); + // Don't print the _specialize attribute if it is marked spi and we are + // asked to skip SPI. + if (!Options.PrintSPIs && !attr->getSPIGroups().empty()) + return false; + + Printer << "@" << getAttrName() << "("; auto exported = attr->isExported() ? "true" : "false"; auto kind = attr->isPartialSpecialization() ? "partial" : "full"; auto target = attr->getTargetFunctionName(); Printer << "exported: "<< exported << ", "; + for (auto id : attr->getSPIGroups()) { + Printer << "spi: " << id << ", "; + } Printer << "kind: " << kind << ", "; if (target) Printer << "target: " << target << ", "; @@ -1558,16 +1566,17 @@ const AvailableAttr *AvailableAttr::isUnavailable(const Decl *D) { } SpecializeAttr::SpecializeAttr(SourceLoc atLoc, SourceRange range, - TrailingWhereClause *clause, - bool exported, + TrailingWhereClause *clause, bool exported, SpecializationKind kind, GenericSignature specializedSignature, - DeclNameRef targetFunctionName) + DeclNameRef targetFunctionName, + ArrayRef spiGroups) : DeclAttribute(DAK_Specialize, atLoc, range, /*Implicit=*/clause == nullptr), - trailingWhereClause(clause), - specializedSignature(specializedSignature), - targetFunctionName(targetFunctionName) { + trailingWhereClause(clause), specializedSignature(specializedSignature), + targetFunctionName(targetFunctionName), numSPIGroups(spiGroups.size()) { + std::uninitialized_copy(spiGroups.begin(), spiGroups.end(), + getTrailingObjects()); Bits.SpecializeAttr.exported = exported; Bits.SpecializeAttr.kind = unsigned(kind); } @@ -1579,32 +1588,38 @@ TrailingWhereClause *SpecializeAttr::getTrailingWhereClause() const { SpecializeAttr *SpecializeAttr::create(ASTContext &Ctx, SourceLoc atLoc, SourceRange range, TrailingWhereClause *clause, - bool exported, - SpecializationKind kind, + bool exported, SpecializationKind kind, DeclNameRef targetFunctionName, + ArrayRef spiGroups, GenericSignature specializedSignature) { - return new (Ctx) SpecializeAttr(atLoc, range, clause, exported, kind, - specializedSignature, targetFunctionName); + unsigned size = totalSizeToAlloc(spiGroups.size()); + void *mem = Ctx.Allocate(size, alignof(SpecializeAttr)); + return new (mem) + SpecializeAttr(atLoc, range, clause, exported, kind, specializedSignature, + targetFunctionName, spiGroups); } SpecializeAttr *SpecializeAttr::create(ASTContext &ctx, bool exported, - SpecializationKind kind, - GenericSignature specializedSignature, - DeclNameRef targetFunctionName) { - return new (ctx) + SpecializationKind kind, + ArrayRef spiGroups, + GenericSignature specializedSignature, + DeclNameRef targetFunctionName) { + unsigned size = totalSizeToAlloc(spiGroups.size()); + void *mem = ctx.Allocate(size, alignof(SpecializeAttr)); + return new (mem) SpecializeAttr(SourceLoc(), SourceRange(), nullptr, exported, kind, - specializedSignature, targetFunctionName); + specializedSignature, targetFunctionName, spiGroups); } -SpecializeAttr *SpecializeAttr::create(ASTContext &ctx, bool exported, - SpecializationKind kind, - GenericSignature specializedSignature, - DeclNameRef targetFunctionName, - LazyMemberLoader *resolver, - uint64_t data) { - auto *attr = new (ctx) +SpecializeAttr *SpecializeAttr::create( + ASTContext &ctx, bool exported, SpecializationKind kind, + ArrayRef spiGroups, GenericSignature specializedSignature, + DeclNameRef targetFunctionName, LazyMemberLoader *resolver, uint64_t data) { + unsigned size = totalSizeToAlloc(spiGroups.size()); + void *mem = ctx.Allocate(size, alignof(SpecializeAttr)); + auto *attr = new (mem) SpecializeAttr(SourceLoc(), SourceRange(), nullptr, exported, kind, - specializedSignature, targetFunctionName); + specializedSignature, targetFunctionName, spiGroups); attr->resolver = resolver; attr->resolverContextData = data; return attr; diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 6095d2d8c0008..6b4a60c42d24f 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -2034,6 +2034,31 @@ bool SourceFile::isImportedAsSPI(const ValueDecl *targetDecl) const { return false; } +bool ModuleDecl::isImportedAsSPI(const SpecializeAttr *attr, + const ValueDecl *targetDecl) const { + auto targetModule = targetDecl->getModuleContext(); + llvm::SmallSetVector importedSPIGroups; + lookupImportedSPIGroups(targetModule, importedSPIGroups); + if (importedSPIGroups.empty()) return false; + + auto declSPIGroups = attr->getSPIGroups(); + + for (auto declSPI : declSPIGroups) + if (importedSPIGroups.count(declSPI)) + return true; + + return false; +} + +bool ModuleDecl::isImportedAsSPI(Identifier spiGroup, + const ModuleDecl *fromModule) const { + llvm::SmallSetVector importedSPIGroups; + lookupImportedSPIGroups(fromModule, importedSPIGroups); + if (importedSPIGroups.empty()) + return false; + return importedSPIGroups.count(spiGroup); +} + bool Decl::isSPI() const { return !getSPIGroups().empty(); } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 18826b1c606a4..74c485161dfe8 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -582,8 +582,9 @@ bool Parser::parseSpecializeAttributeArguments( swift::tok ClosingBrace, bool &DiscardAttribute, Optional &Exported, Optional &Kind, swift::TrailingWhereClause *&TrailingWhereClause, - DeclNameRef &targetFunction, - llvm::function_ref parseSILTargetName) { + DeclNameRef &targetFunction, SmallVectorImpl &spiGroups, + llvm::function_ref parseSILTargetName, + llvm::function_ref parseSILSIPModule) { SyntaxParsingContext ContentContext(SyntaxContext, SyntaxKind::SpecializeAttributeSpecList); // Parse optional "exported" and "kind" labeled parameters. @@ -595,7 +596,8 @@ bool Parser::parseSpecializeAttributeArguments( ? SyntaxKind::TargetFunctionEntry : SyntaxKind::LabeledSpecializeEntry); if (ParamLabel != "exported" && ParamLabel != "kind" && - ParamLabel != "target") { + ParamLabel != "target" && ParamLabel != "spi" && + ParamLabel != "spiModule") { diagnose(Tok.getLoc(), diag::attr_specialize_unknown_parameter_name, ParamLabel); } @@ -616,7 +618,8 @@ bool Parser::parseSpecializeAttributeArguments( return false; } if ((ParamLabel == "exported" && Exported.hasValue()) || - (ParamLabel == "kind" && Kind.hasValue())) { + (ParamLabel == "kind" && Kind.hasValue()) || + (ParamLabel == "spi" && !spiGroups.empty())) { diagnose(Tok.getLoc(), diag::attr_specialize_parameter_already_defined, ParamLabel); } @@ -673,6 +676,23 @@ bool Parser::parseSpecializeAttributeArguments( DeclNameFlag::AllowOperators); } } + if (ParamLabel == "spiModule") { + if (!parseSILSIPModule(*this)) { + diagnose(Tok.getLoc(), diag::attr_specialize_unknown_parameter_name, + ParamLabel); + return false; + } + } + if (ParamLabel == "spi") { + if (!Tok.is(tok::identifier)) { + diagnose(Tok.getLoc(), diag::attr_specialize_expected_spi_name); + consumeToken(); + return false; + } + auto text = Tok.getText(); + spiGroups.push_back(Context.getIdentifier(text)); + consumeToken(); + } if (!consumeIf(tok::comma)) { diagnose(Tok.getLoc(), diag::attr_specialize_missing_comma); skipUntil(tok::comma, tok::kw_where); @@ -712,7 +732,8 @@ bool Parser::parseSpecializeAttributeArguments( bool Parser::parseSpecializeAttribute( swift::tok ClosingBrace, SourceLoc AtLoc, SourceLoc Loc, SpecializeAttr *&Attr, - llvm::function_ref parseSILTargetName) { + llvm::function_ref parseSILTargetName, + llvm::function_ref parseSILSIPModule) { assert(ClosingBrace == tok::r_paren || ClosingBrace == tok::r_square); SourceLoc lParenLoc = consumeToken(); @@ -725,9 +746,10 @@ bool Parser::parseSpecializeAttribute( TrailingWhereClause *trailingWhereClause = nullptr; DeclNameRef targetFunction; - if (!parseSpecializeAttributeArguments(ClosingBrace, DiscardAttribute, - exported, kind, trailingWhereClause, - targetFunction, parseSILTargetName)) { + SmallVector spiGroups; + if (!parseSpecializeAttributeArguments( + ClosingBrace, DiscardAttribute, exported, kind, trailingWhereClause, + targetFunction, spiGroups, parseSILTargetName, parseSILSIPModule)) { return false; } @@ -756,7 +778,7 @@ bool Parser::parseSpecializeAttribute( // Store the attribute. Attr = SpecializeAttr::create(Context, AtLoc, SourceRange(Loc, rParenLoc), trailingWhereClause, exported.getValue(), - kind.getValue(), targetFunction); + kind.getValue(), targetFunction, spiGroups); return true; } diff --git a/lib/SIL/IR/SILFunction.cpp b/lib/SIL/IR/SILFunction.cpp index 4aa3d857b380a..6ad37d656c225 100644 --- a/lib/SIL/IR/SILFunction.cpp +++ b/lib/SIL/IR/SILFunction.cpp @@ -33,20 +33,22 @@ using namespace Lowering; SILSpecializeAttr::SILSpecializeAttr(bool exported, SpecializationKind kind, GenericSignature specializedSig, - SILFunction *target) + SILFunction *target, Identifier spiGroup, + const ModuleDecl *spiModule) : kind(kind), exported(exported), specializedSignature(specializedSig), - targetFunction(target) { - if (targetFunction) - targetFunction->incrementRefCount(); - } + spiGroup(spiGroup), spiModule(spiModule), targetFunction(target) { + if (targetFunction) + targetFunction->incrementRefCount(); +} -SILSpecializeAttr *SILSpecializeAttr::create(SILModule &M, - GenericSignature specializedSig, - bool exported, - SpecializationKind kind, - SILFunction *target) { +SILSpecializeAttr * +SILSpecializeAttr::create(SILModule &M, GenericSignature specializedSig, + bool exported, SpecializationKind kind, + SILFunction *target, Identifier spiGroup, + const ModuleDecl *spiModule) { void *buf = M.allocate(sizeof(SILSpecializeAttr), alignof(SILSpecializeAttr)); - return ::new (buf) SILSpecializeAttr(exported, kind, specializedSig, target); + return ::new (buf) SILSpecializeAttr(exported, kind, specializedSig, target, + spiGroup, spiModule); } void SILFunction::addSpecializeAttr(SILSpecializeAttr *Attr) { diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index b53b9d4f20d8e..460be1d8889d5 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -56,15 +56,31 @@ void SILFunctionBuilder::addFunctionAttributes( SILFunction *targetFunction = nullptr; auto *attributedFuncDecl = constant.getDecl(); auto *targetFunctionDecl = SA->getTargetFunctionDecl(attributedFuncDecl); + // Filter out _spi. + auto spiGroups = SA->getSPIGroups(); + bool hasSPI = !spiGroups.empty(); + if (hasSPI) { + if (attributedFuncDecl->getModuleContext() != M.getSwiftModule() && + !M.getSwiftModule()->isImportedAsSPI(SA, attributedFuncDecl)) { + continue; + } + } + assert(spiGroups.size() <= 1 && "SIL does not support multiple SPI groups"); + Identifier spiGroupIdent; + if (hasSPI) { + spiGroupIdent = spiGroups[0]; + } if (targetFunctionDecl) { SILDeclRef declRef(targetFunctionDecl, constant.kind, false); targetFunction = getOrCreateDeclaration(targetFunctionDecl, declRef); - F->addSpecializeAttr( - SILSpecializeAttr::create(M, SA->getSpecializedSignature(), - SA->isExported(), kind, targetFunction)); + F->addSpecializeAttr(SILSpecializeAttr::create( + M, SA->getSpecializedSignature(), SA->isExported(), kind, + targetFunction, spiGroupIdent, + attributedFuncDecl->getModuleContext())); } else { F->addSpecializeAttr(SILSpecializeAttr::create( - M, SA->getSpecializedSignature(), SA->isExported(), kind, nullptr)); + M, SA->getSpecializedSignature(), SA->isExported(), kind, nullptr, + spiGroupIdent, attributedFuncDecl->getModuleContext())); } } diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index faaa7bdc6d253..6fd8616f21be7 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -3465,6 +3465,12 @@ void SILSpecializeAttr::print(llvm::raw_ostream &OS) const { OS << "exported: " << exported << ", "; OS << "kind: " << kind << ", "; + if (!getSPIGroup().empty()) { + OS << "spi: " << getSPIGroup() << ", "; + OS << "spiModule: "; + getSPIModule()->getReverseFullModuleName().printForward(OS); + OS << ", "; + } auto *genericEnv = getFunction()->getGenericEnvironment(); GenericSignature genericSig; diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 0059f912e8683..1f1d8e3c5b85c 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -137,6 +137,8 @@ namespace { bool exported; SILSpecializeAttr::SpecializationKind kind; SILFunction *target = nullptr; + Identifier spiGroupID; + ModuleDecl *spiModule; }; class SILParser { @@ -1052,6 +1054,7 @@ static bool parseDeclSILOptional(bool *isTransparent, SpecAttr.kind = SILSpecializeAttr::SpecializationKind::Full; SpecializeAttr *Attr; StringRef targetFunctionName; + ModuleDecl *module = nullptr; if (!SP.P.parseSpecializeAttribute( tok::r_square, AtLoc, Loc, Attr, @@ -1065,6 +1068,17 @@ static bool parseDeclSILOptional(bool *isTransparent, P.consumeToken(tok::string_literal); return true; + }, + [&module](Parser &P) -> bool { + if (P.Tok.getKind() != tok::identifier) { + P.diagnose(P.Tok, diag::expected_in_attribute_list); + return true; + } + auto ident = P.Context.getIdentifier(P.Tok.getText()); + module = P.Context.getModuleByIdentifier(ident); + assert(module); + P.consumeToken(); + return true; })) return true; SILFunction *targetFunction = nullptr; @@ -1086,6 +1100,9 @@ static bool parseDeclSILOptional(bool *isTransparent, SpecAttr.exported = Attr->isExported(); SpecAttr.target = targetFunction; SpecAttrs->emplace_back(SpecAttr); + if (!Attr->getSPIGroups().empty()) { + SpecAttr.spiGroupID = Attr->getSPIGroups()[0]; + } continue; } else if (ClangDecl && SP.P.Tok.getText() == "clang") { @@ -5872,7 +5889,7 @@ bool SILParserState::parseDeclSIL(Parser &P) { GenericSignature()); FunctionState.F->addSpecializeAttr(SILSpecializeAttr::create( FunctionState.F->getModule(), genericSig, Attr.exported, - Attr.kind, Attr.target)); + Attr.kind, Attr.target, Attr.spiGroupID, Attr.spiModule)); } } diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 07aa464d9468e..17b1d981ece8e 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1998,6 +1998,15 @@ static void transferSpecializeAttributeTargets(SILGenModule &SGM, SILModule &M, } else if (auto *vd = dyn_cast(d)) { for (auto *A : vd->getAttrs().getAttributes()) { auto *SA = cast(A); + // Filter _spi. + auto spiGroups = SA->getSPIGroups(); + auto hasSPIGroup = !spiGroups.empty(); + if (hasSPIGroup) { + if (vd->getModuleContext() != M.getSwiftModule() && + !M.getSwiftModule()->isImportedAsSPI(SA, vd)) { + continue; + } + } if (auto *targetFunctionDecl = SA->getTargetFunctionDecl(vd)) { auto target = SILDeclRef(targetFunctionDecl); auto targetSILFunction = SGM.getFunction(target, NotForDefinition); @@ -2005,9 +2014,13 @@ static void transferSpecializeAttributeTargets(SILGenModule &SGM, SILModule &M, SpecializeAttr::SpecializationKind::Full ? SILSpecializeAttr::SpecializationKind::Full : SILSpecializeAttr::SpecializationKind::Partial; - + Identifier spiGroupIdent; + if (hasSPIGroup) { + spiGroupIdent = spiGroups[0]; + } targetSILFunction->addSpecializeAttr(SILSpecializeAttr::create( - M, SA->getSpecializedSignature(), SA->isExported(), kind, nullptr)); + M, SA->getSpecializedSignature(), SA->isExported(), kind, nullptr, + spiGroupIdent, vd->getModuleContext())); } } } diff --git a/lib/SILOptimizer/Utils/Generics.cpp b/lib/SILOptimizer/Utils/Generics.cpp index 15bca08352a17..389d06b559cfb 100644 --- a/lib/SILOptimizer/Utils/Generics.cpp +++ b/lib/SILOptimizer/Utils/Generics.cpp @@ -2448,6 +2448,17 @@ usePrespecialized(SILOptFunctionBuilder &funcBuilder, ApplySite apply, for (auto *SA : refF->getSpecializeAttrs()) { if (!SA->isExported()) continue; + // Check whether SPI allows using this function. + auto spiGroup = SA->getSPIGroup(); + if (!spiGroup.empty()) { + auto currentModule = funcBuilder.getModule().getSwiftModule(); + auto funcModule = SA->getSPIModule(); + // Don't use this SPI if the current module does not import the function's + // module with @_spi(). + if (currentModule != funcModule && + !currentModule->isImportedAsSPI(spiGroup, funcModule)) + continue; + } ReabstractionInfo reInfo(funcBuilder.getModule().getSwiftModule(), funcBuilder.getModule().isWholeModule(), refF, SA->getSpecializedSignature(), diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 93ea69092739f..0d853f04053b9 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -4257,31 +4257,46 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { ArrayRef rawPieceIDs; uint64_t numArgs; + uint64_t numSPIGroups; DeclID targetFunID; serialization::decls_block::SpecializeDeclAttrLayout::readRecord( scratch, exported, specializationKindVal, specializedSigID, - targetFunID, numArgs, rawPieceIDs); + targetFunID, numArgs, numSPIGroups, rawPieceIDs); + assert(rawPieceIDs.size() == numArgs + numSPIGroups || + rawPieceIDs.size() == (numArgs - 1 + numSPIGroups)); specializationKind = specializationKindVal ? SpecializeAttr::SpecializationKind::Partial : SpecializeAttr::SpecializationKind::Full; // The 'target' parameter. DeclNameRef replacedFunctionName; if (numArgs) { + bool numArgumentLabels = (numArgs == 1) ? 0 : numArgs - 2; auto baseName = MF.getDeclBaseName(rawPieceIDs[0]); SmallVector pieces; - for (auto pieceID : rawPieceIDs.slice(1)) - pieces.push_back(MF.getIdentifier(pieceID)); + if (numArgumentLabels) { + for (auto pieceID : rawPieceIDs.slice(1, numArgumentLabels)) + pieces.push_back(MF.getIdentifier(pieceID)); + } replacedFunctionName = (numArgs == 1) ? DeclNameRef({baseName}) // simple name : DeclNameRef({ctx, baseName, pieces}); } + SmallVector spis; + if (numSPIGroups) { + auto numTargetFunctionPiecesToSkip = + (rawPieceIDs.size() == numArgs + numSPIGroups) ? numArgs + : numArgs - 1; + for (auto id : rawPieceIDs.slice(numTargetFunctionPiecesToSkip)) + spis.push_back(MF.getIdentifier(id)); + } + auto specializedSig = MF.getGenericSignature(specializedSigID); Attr = SpecializeAttr::create(ctx, exported != 0, specializationKind, - specializedSig, replacedFunctionName, &MF, - targetFunID); + spis, specializedSig, + replacedFunctionName, &MF, targetFunID); break; } diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 7df513082ae47..6b5cff8a157b3 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -703,15 +703,24 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, unsigned specializationKindVal; GenericSignatureID specializedSigID; IdentifierID targetFunctionID; - SILSpecializeAttrLayout::readRecord(scratch, exported, - specializationKindVal, specializedSigID, - targetFunctionID); + IdentifierID spiGroupID; + ModuleID spiModuleID; + SILSpecializeAttrLayout::readRecord( + scratch, exported, specializationKindVal, specializedSigID, + targetFunctionID, spiGroupID, spiModuleID); SILFunction *target = nullptr; if (targetFunctionID) { target = getFuncForReference(MF->getIdentifier(targetFunctionID).str()); } + Identifier spiGroup; + const ModuleDecl *spiModule = nullptr; + if (spiGroupID) { + spiGroup = MF->getIdentifier(spiGroupID); + spiModule = MF->getModule(spiModuleID); + } + SILSpecializeAttr::SpecializationKind specializationKind = specializationKindVal ? SILSpecializeAttr::SpecializationKind::Partial : SILSpecializeAttr::SpecializationKind::Full; @@ -721,7 +730,8 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, if (shouldAddAtttributes) { // Read the substitution list and construct a SILSpecializeAttr. fn->addSpecializeAttr(SILSpecializeAttr::create( - SILMod, specializedSig, exported != 0, specializationKind, target)); + SILMod, specializedSig, exported != 0, specializationKind, target, + spiGroup, spiModule)); } } diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 75089d867e7a6..5c35093f2a9b8 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 581; // @_specialize target parameter +const uint16_t SWIFTMODULE_VERSION_MINOR = 582; // sil specialize attribute module parameter /// A standard hash seed used for all string hashes in a serialized module. /// @@ -1850,8 +1850,9 @@ namespace decls_block { BCFixed<1>, // specialization kind GenericSignatureIDField, // specialized signature DeclIDField, // target function - BCVBR<4>, // # of arguments (+1) or zero if no name - BCArray + BCVBR<4>, // # of arguments (+1) or 1 if simple decl name, 0 if no target + BCVBR<4>, // # of SPI groups + BCArray // target function pieces, spi groups >; using DifferentiableDeclAttrLayout = BCRecordLayout< diff --git a/lib/Serialization/SILFormat.h b/lib/Serialization/SILFormat.h index 2f7bcc83b9d08..28b60b3264c31 100644 --- a/lib/Serialization/SILFormat.h +++ b/lib/Serialization/SILFormat.h @@ -303,7 +303,9 @@ namespace sil_block { BCFixed<1>, // exported BCFixed<1>, // specialization kind GenericSignatureIDField, // specialized signature - DeclIDField // Target SILFunction name or 0. + DeclIDField, // Target SILFunction name or 0. + DeclIDField, // SPIGroup or 0. + DeclIDField // SPIGroup Module name id. >; // Has an optional argument list where each argument is a typed valueref. diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index a00e0df83cb31..5c81c6b5855d6 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -689,6 +689,16 @@ IdentifierID Serializer::addContainingModuleRef(const DeclContext *DC) { return addDeclBaseNameRef(exportedModuleID); } +IdentifierID Serializer::addModuleRef(const ModuleDecl *module) { + if (module == this->M) + return CURRENT_MODULE_ID; + if (module == this->M->getASTContext().TheBuiltinModule) + return BUILTIN_MODULE_ID; + auto moduleName = + module->getASTContext().getIdentifier(module->getName().str()); + return addDeclBaseNameRef(moduleName); +} + SILLayoutID Serializer::addSILLayoutRef(const SILLayout *layout) { return SILLayoutsToSerialize.addRef(layout); } @@ -2367,6 +2377,7 @@ class Serializer::DeclSerializer : public DeclVisitor { auto *targetFunDecl = attr->getTargetFunctionDecl(cast(D)); SmallVector pieces; + // encodes whether this a a simple or compound name by adding one. size_t numArgs = 0; if (targetFun) { @@ -2380,12 +2391,20 @@ class Serializer::DeclSerializer : public DeclVisitor { numArgs = pieces.size() + 1; } + for (auto spi : attr->getSPIGroups()) { + assert(!spi.empty() && "Empty SPI name"); + pieces.push_back(S.addDeclBaseNameRef(spi)); + } + + auto numSPIGroups = attr->getSPIGroups().size(); + assert(pieces.size() == numArgs + numSPIGroups || + pieces.size() == (numArgs - 1 + numSPIGroups)); + SpecializeDeclAttrLayout::emitRecord( S.Out, S.ScratchRecord, abbrCode, (unsigned)attr->isExported(), (unsigned)attr->getSpecializationKind(), S.addGenericSignatureRef(attr->getSpecializedSignature()), - S.addDeclRef(targetFunDecl), numArgs, - pieces); + S.addDeclRef(targetFunDecl), numArgs, numSPIGroups, pieces); return; } diff --git a/lib/Serialization/Serialization.h b/lib/Serialization/Serialization.h index 757c55b159d0c..47ca711966245 100644 --- a/lib/Serialization/Serialization.h +++ b/lib/Serialization/Serialization.h @@ -504,6 +504,9 @@ class Serializer : public SerializerBase { /// \see FileUnit::getExportedModuleName IdentifierID addContainingModuleRef(const DeclContext *DC); + /// Records the module \m. + IdentifierID addModuleRef(const ModuleDecl *m); + /// Write a normal protocol conformance. void writeASTBlockEntity(const NormalProtocolConformance *conformance); diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 95c72eecc5a3a..70fa8597c5470 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -457,12 +457,18 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) { addReferencedSILFunction(target, true); targetFunctionNameID = S.addUniquedStringRef(target->getName()); } + IdentifierID spiGroupID = 0; + IdentifierID spiModuleDeclID = 0; + auto ident = SA->getSPIGroup(); + if (!ident.empty()) { + spiGroupID = S.addUniquedStringRef(ident.str()); + spiModuleDeclID = S.addModuleRef(SA->getSPIModule()); + } SILSpecializeAttrLayout::emitRecord( - Out, ScratchRecord, specAttrAbbrCode, - (unsigned)SA->isExported(), + Out, ScratchRecord, specAttrAbbrCode, (unsigned)SA->isExported(), (unsigned)SA->getSpecializationKind(), S.addGenericSignatureRef(SA->getSpecializedSignature()), - targetFunctionNameID); + targetFunctionNameID, spiGroupID, spiModuleDeclID); } // Assign a unique ID to each basic block of the SILFunction. diff --git a/test/SPI/Inputs/spi_helper.swift b/test/SPI/Inputs/spi_helper.swift index b0340cbd49c65..ab2db3177e656 100644 --- a/test/SPI/Inputs/spi_helper.swift +++ b/test/SPI/Inputs/spi_helper.swift @@ -1,4 +1,4 @@ -/// Library defining SPI decls + /// Library defining SPI decls public protocol PublicProto { associatedtype Assoc @@ -8,6 +8,9 @@ public func publicFunc() { print("publicFunc") } func internalFunc() {} +@_specialize(exported: true, spi: HelperSPI, where T == Int) +public func genericFunc(_ t: T) { print(t) } + @_spi(HelperSPI) public func spiFunc() { print("spiFunc") } @_spi(HelperSPI) public class SPIStruct { @@ -28,6 +31,9 @@ func internalFunc() {} public func spiInherit() {} @_spi(DifferentSPI) public func spiDontInherit() {} + + @_specialize(exported: true, spi: HelperSPI, where T == Int) + @_spi(HelperSPI) public func genericFunc2(_ t: T) { print(t) } } public extension SPIStruct { @@ -43,6 +49,9 @@ public extension SPIStruct { @_spi(HelperSPI) public init() { print("SPIClass.init") } @_spi(HelperSPI) public func spiMethod() { print("SPIClass.spiMethod") } @_spi(HelperSPI) public var spiVar = "text" + + @_specialize(exported: true, spi: HelperSPI, where T == Int) + @_spi(HelperSPI) public func genericFunc3(_ t: T) { print(t) } } @_spi(HelperSPI) public enum SPIEnum { @@ -55,6 +64,9 @@ public extension SPIStruct { } @_spi(HelperSPI) public func spiMethod() { print("SPIEnum.spiMethod") } + + @_specialize(exported: true, spi: HelperSPI, where T == Int) + public func genericFunc4(_ t: T) { print(t) } } public struct PublicStruct { @@ -63,6 +75,9 @@ public struct PublicStruct { @_spi(HelperSPI) public init(alt_init: Int) { print("PublicStruct.init alt_init") } @_spi(HelperSPI) public func spiMethod() { print("PublicStruct.spiMethod") } @_spi(HelperSPI) public var spiVar = "text" + + @_specialize(exported: true, spi: HelperSPI, where T == Int) + public func prespecializedMethod(_ t: T) { print(t) } } @_spi(OtherSPI) public func otherApiFunc() {} diff --git a/test/SPI/client_use_multiple_module_specialize.swift b/test/SPI/client_use_multiple_module_specialize.swift new file mode 100644 index 0000000000000..aea12a06527ba --- /dev/null +++ b/test/SPI/client_use_multiple_module_specialize.swift @@ -0,0 +1,172 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -DLIB_A %s -module-name A -emit-module-path %t/A.swiftmodule +// RUN: %target-swift-frontend -emit-module -DLIB_B %s -module-name B -emit-module-path %t/B.swiftmodule -I %t +// RUN: %target-swift-frontend -module-name C -emit-sil -O -DLIB_C %s -I %t | %FileCheck %s +// RUN: %target-swift-frontend -module-name C -emit-sil -O -DLIB_C_NO_SPI %s -I %t | %FileCheck %s --check-prefix=NOSPI + +// Test using the public swiftinterface +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -swift-version 5 -enable-library-evolution -emit-module -DLIB_A %s -module-name A -emit-module-path %t/A.swiftmodule -emit-module-interface-path %t/A.swiftinterface +// RUN: %target-swift-frontend -swift-version 5 -enable-library-evolution -emit-module -DLIB_B %s -module-name B -emit-module-path %t/B.swiftmodule -I %t -emit-module-interface-path %t/B.swiftinterface +// RUN: rm %t/A.swiftmodule %t/B.swiftmodule +// RUN: %target-swift-frontend -module-name C -emit-sil -O -DLIB_C %s -I %t | %FileCheck %s --check-prefix=PUBLIC + +// Test using the private swiftinterface +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -swift-version 5 -enable-library-evolution -emit-module -DLIB_A %s -module-name A -emit-module-path %t/A.swiftmodule -emit-module-interface-path %t/A.swiftinterface -emit-private-module-interface-path %t/A.private.swiftinterface +// RUN: %target-swift-frontend -swift-version 5 -enable-library-evolution -emit-module -DLIB_B %s -module-name B -emit-module-path %t/B.swiftmodule -I %t -emit-module-interface-path %t/B.swiftinterface -emit-private-module-interface-path %t/B.private.swiftinterface +// RUN: rm %t/A.swiftmodule %t/B.swiftmodule +// RUN: %target-swift-frontend -module-name C -emit-sil -O -DLIB_C %s -I %t | %FileCheck %s + +#if LIB_A + +@_specialize(exported: true, spi: A, where T == Int) +public func genericFuncLibA(_ t: T) { print(t) } + +public struct SomeStruct { + var x : T + + public init(_ t: T) { self.x = t } + + @_specialize(exported: true, spi: A, where T == Int) + @inlinable + public func genericFuncInSomeStruct(_ t: T) { print(t) } +} + +public struct SomeClass { + var x : T + + public init(_ t: T) { self.x = t } + + @_specialize(exported: true, spi: A, where T == Int) + @inlinable + public func genericFuncInSomeClass(_ t: T) { print(t) } +} + +public enum SomeEnum { + case A + case B(T) + + @_specialize(exported: true, spi: A, where T == Int) + @inlinable + public func genericFuncInSomeEnum(_ t: T) { print(SomeEnum.B(t)) } +} + +@usableFromInline +struct SomeInternalStruct { + var x : T + + @usableFromInline + init(_ t: T) { self.x = t } + + @_specialize(exported: true, spi: A, where T == Int) + @inlinable + func genericFuncInSomeStruct(_ t: T) { print(t) } +} + +@inlinable +@inline(__always) +public func testSomeInternalStruct(_ t: T) { + SomeInternalStruct(t).genericFuncInSomeStruct(t); +} + +#elseif LIB_B + +import A + +@_specialize(exported: true, spi: A, where T == Int) +public func genericFuncLibB(_ t: T) { print(t) } + +@_specializeExtension +extension SomeStruct { + @_specialize(exported: true, spi: A, target: genericFuncInSomeStruct(_:), where T == Double) + public func genericFuncInSomeStruct_specialized(_ t: T) { fatalError("don't call") } +} + +extension SomeClass { + @_specialize(exported: true, spi: A, target: genericFuncInSomeClass(_:), where T == Double) + public func genericFunc_specialized(_ t: T) { fatalError("don't call") } +} + +extension SomeEnum { + @_specialize(exported: true, spi: A, target: genericFuncInSomeEnum(_:), where T == Double) + public func genericFunc_specialized(_ t: T) { fatalError("don't call") } +} + +@_specializeExtension +extension SomeInternalStruct { + @_specialize(exported: true, spi: A, target: genericFuncInSomeStruct(_:), where T == Double) + public func genericFuncInSomeStruct_specialized(_ t: T) { fatalError("don't call") } +} + +#elseif LIB_C + +@_spi(A) import A +@_spi(A) import B + +// CHECK-LABEL: sil @$s1C21testUseSpecializedSPIyyF : $@convention(thin) () -> () { +// CHECK: [[F1:%.*]] = function_ref @$s1A15genericFuncLibAyyxlFSi_Ts5 +// CHECK: apply [[F1]]({{.*}}) : $@convention(thin) (Int) -> () +// CHECK: [[F2:%.*]] = function_ref @$s1B15genericFuncLibByyxlFSi_Ts5 +// CHECK: apply [[F2]]({{.*}}) : $@convention(thin) (Int) -> () +// CHECK: function_ref @$s1A10SomeStructV013genericFuncInaB0yyxFSi_Ts5 +// CHECK: function_ref @$s1A10SomeStructV013genericFuncInaB0yyxFSd_Ts5 +// CHECK: function_ref @$s1A9SomeClassV013genericFuncInaB0yyxFSi_Ts5 +// CHECK: function_ref @$s1A9SomeClassV013genericFuncInaB0yyxFSd_Ts5 +// CHECK: function_ref @$s1A8SomeEnumO013genericFuncInaB0yyxFSi_Ts5 +// CHECK: function_ref @$s1A8SomeEnumO013genericFuncInaB0yyxFSd_Ts5 +// CHECK: } // end sil function '$s1C21testUseSpecializedSPIyyF' + +// PUBLIC-LABEL: sil @$s1C21testUseSpecializedSPIyyF : $@convention(thin) () -> () { +// PUBLIC-NOT: function_ref @$s1A15genericFuncLibAyyxlFSi_Ts5 +// PUBLIC-NOT: function_ref @$s1B15genericFuncLibByyxlFSi_Ts5 +// PUBLIC-NOT: function_ref @$s1A10SomeStructV013genericFuncInaB0yyxFSi_Ts5 +// PUBLIC-NOT: function_ref @$s1A10SomeStructV013genericFuncInaB0yyxFSd_Ts5 +// PUBLIC-NOT: function_ref @$s1A9SomeClassV013genericFuncInaB0yyxFSi_Ts5 +// PUBLIC-NOT: function_ref @$s1A9SomeClassV013genericFuncInaB0yyxFSd_Ts5 +// PUBLIC-NOT: function_ref @$s1A8SomeEnumO013genericFuncInaB0yyxFSi_Ts5 +// PUBLIC-NOT: function_ref @$s1A8SomeEnumO013genericFuncInaB0yyxFSd_Ts5 +// PUBLIC: } // end sil function '$s1C21testUseSpecializedSPIyyF' + +public func testUseSpecializedSPI() { + genericFuncLibA(1) + genericFuncLibB(2) + SomeStruct(1).genericFuncInSomeStruct(5) + SomeStruct(1.0).genericFuncInSomeStruct(5.0) + SomeClass(1).genericFuncInSomeClass(5) + SomeClass(1.0).genericFuncInSomeClass(5.0) + SomeEnum.B(1).genericFuncInSomeEnum(1) + SomeEnum.B(1.0).genericFuncInSomeEnum(1.0) + testSomeInternalStruct(1) + testSomeInternalStruct(1.0) +} + +#elseif LIB_C_NO_SPI + +import A +import B + +// NOSPI-LABEL: sil @$s1C21testUseSpecializedSPIyyF : $@convention(thin) () -> () { +// NOSPI-NOT: function_ref @$s1A15genericFuncLibAyyxlFSi_Ts5 +// NOSPI-NOT: function_ref @$s1B15genericFuncLibByyxlFSi_Ts5 +// NOSPI-NOT: function_ref @$s1A10SomeStructV013genericFuncInaB0yyxFSi_Ts5 +// NOSPI-NOT: function_ref @$s1A10SomeStructV013genericFuncInaB0yyxFSd_Ts5 +// NOSPI-NOT: function_ref @$s1A9SomeClassV013genericFuncInaB0yyxFSi_Ts5 +// NOSPI-NOT: function_ref @$s1A9SomeClassV013genericFuncInaB0yyxFSd_Ts5 +// NOSPI-NOT: function_ref @$s1A8SomeEnumO013genericFuncInaB0yyxFSi_Ts5 +// NOSPI-NOT: function_ref @$s1A8SomeEnumO013genericFuncInaB0yyxFSd_Ts5 +// NOSPI: } // end sil function '$s1C21testUseSpecializedSPIyyF' + +public func testUseSpecializedSPI() { + genericFuncLibA(1) + genericFuncLibB(2) + SomeStruct(1).genericFuncInSomeStruct(5) + SomeStruct(1.0).genericFuncInSomeStruct(5.0) + SomeClass(1).genericFuncInSomeClass(5) + SomeClass(1.0).genericFuncInSomeClass(5.0) + SomeEnum.B(1).genericFuncInSomeEnum(1) + SomeEnum.B(1.0).genericFuncInSomeEnum(1.0) + testSomeInternalStruct(1) + testSomeInternalStruct(1.0) +} +#endif diff --git a/test/SPI/private_swiftinterface.swift b/test/SPI/private_swiftinterface.swift index 14353b41bd15f..84322a572dd3f 100644 --- a/test/SPI/private_swiftinterface.swift +++ b/test/SPI/private_swiftinterface.swift @@ -9,6 +9,22 @@ // RUN: %FileCheck -check-prefix=CHECK-HELPER %s < %t/SPIHelper.swiftinterface // CHECK-HELPER-NOT: HelperSPI // CHECK-HELPER-NOT: @_spi +// CHECK-HELPER-NOT: @_specialize + +// Test the spi parameter of the _specialize attribute in the private interface. +// RUN: %FileCheck -check-prefix=CHECK-HELPER-PRIVATE %s < %t/SPIHelper.private.swiftinterface +// CHECK-HELPER-PRIVATE: @_specialize(exported: true, spi: HelperSPI, kind: full, where T == Swift.Int) +// CHECK-HELPER-PRIVATE-NEXT: public func genericFunc(_ t: T) +// CHECK-HELPER-PRIVATE: @_specialize(exported: true, spi: HelperSPI, kind: full, where T == Swift.Int) +// CHECK-HELPER-PRIVATE-NEXT: public func genericFunc2(_ t: T) +// CHECK-HELPER-PRIVATE: @_specialize(exported: true, spi: HelperSPI, kind: full, where T == Swift.Int) +// CHECK-HELPER-PRIVATE-NEXT: public func genericFunc3(_ t: T) +// CHECK-HELPER-PRIVATE: @_specialize(exported: true, spi: HelperSPI, kind: full, where T == Swift.Int) +// CHECK-HELPER-PRIVATE-NEXT: public func genericFunc4(_ t: T) +// CHECK-HELPER-PRIVATE: @_specialize(exported: true, spi: HelperSPI, kind: full, where T == Swift.Int) +// CHECK-HELPER-PRIVATE-NEXT: public func prespecializedMethod(_ t: T) + + // RUN: %target-swift-frontend -emit-module %t/SPIHelper.swiftinterface -emit-module-path %t/SPIHelper-from-public-swiftinterface.swiftmodule -swift-version 5 -module-name SPIHelper -enable-library-evolution /// Test the textual interfaces generated from this test. diff --git a/test/SPI/spi_symbols.swift b/test/SPI/spi_symbols.swift index 8763f82924c1f..8e44a754994b9 100644 --- a/test/SPI/spi_symbols.swift +++ b/test/SPI/spi_symbols.swift @@ -12,11 +12,13 @@ // CHECK-IR: define swiftcc void @"$s10spi_helper8SPIClassC0A6MethodyyF" // CHECK-IR: define swiftcc void @"$s10spi_helper7SPIEnumO0A6MethodyyF" // CHECK-IR: define swiftcc void @"$s10spi_helper12PublicStructV0A6MethodyyF" +// CHECK-IR: define swiftcc void @"$s10spi_helper12PublicStructV20prespecializedMethodyyxlFSi_Ts5" // CHECK-IR: define swiftcc void @"$s10spi_helper12otherApiFuncyyF" // Look for the SPI symbols in the TBD file, these are sorted // CHECK-TBD: _$s10spi_helper0A4FuncyyF // CHECK-TBD: _$s10spi_helper12PublicStructV0A6MethodyyF +// CHECK-TBD: _$s10spi_helper12PublicStructV20prespecializedMethodyyxlFSi_Ts5 // CHECK-TBD: _$s10spi_helper12otherApiFuncyyF // CHECK-TBD: _$s10spi_helper7SPIEnumO0A6MethodyyF // CHECK-TBD: _$s10spi_helper8SPIClassC0A6MethodyyF From d6d79c66aa9bfa484547fa3be568d09bf21054bf Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Fri, 9 Oct 2020 11:14:00 -0700 Subject: [PATCH 415/745] Merge two fields into a PointerUnion in SILDeclRef to save space --- include/swift/SIL/SILDeclRef.h | 40 +++++++++++++++++++++++----------- lib/IRGen/GenMeta.cpp | 2 +- lib/SIL/IR/SILDeclRef.cpp | 34 ++++++++++++++++------------- lib/SIL/IR/SILFunctionType.cpp | 2 +- lib/SIL/IR/SILPrinter.cpp | 8 +++---- lib/SIL/IR/TypeLowering.cpp | 2 +- lib/SILGen/SILGenPoly.cpp | 6 ++--- lib/SILGen/SILGenThunk.cpp | 2 +- lib/SILGen/SILGenType.cpp | 6 ++--- 9 files changed, 60 insertions(+), 42 deletions(-) diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index e463d857e0229..b3f6b0d8f5551 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -150,15 +150,28 @@ struct SILDeclRef { unsigned isForeign : 1; /// The default argument index for a default argument getter. unsigned defaultArgIndex : 10; + + PointerUnion + pointer; + /// The derivative function identifier. - AutoDiffDerivativeFunctionIdentifier *derivativeFunctionIdentifier = nullptr; + AutoDiffDerivativeFunctionIdentifier * getDerivativeFunctionIdentifier() const { + if (!pointer.is()) + return nullptr; + return pointer.get(); + } - GenericSignature specializedSignature; + GenericSignature getSpecializedSignature() const { + if (!pointer.is()) + return GenericSignature(); + else + return GenericSignature(pointer.get()); + } /// Produces a null SILDeclRef. SILDeclRef() - : loc(), kind(Kind::Func), isForeign(0), defaultArgIndex(0), - derivativeFunctionIdentifier(nullptr) {} + : loc(), kind(Kind::Func), isForeign(0), defaultArgIndex(0) {} /// Produces a SILDeclRef of the given kind for the given decl. explicit SILDeclRef( @@ -294,7 +307,7 @@ struct SILDeclRef { return loc.getOpaqueValue() == rhs.loc.getOpaqueValue() && kind == rhs.kind && isForeign == rhs.isForeign && defaultArgIndex == rhs.defaultArgIndex && - derivativeFunctionIdentifier == rhs.derivativeFunctionIdentifier; + pointer == rhs.pointer; } bool operator!=(SILDeclRef rhs) const { return !(*this == rhs); @@ -309,7 +322,7 @@ struct SILDeclRef { /// decl. SILDeclRef asForeign(bool foreign = true) const { return SILDeclRef(loc.getOpaqueValue(), kind, foreign, defaultArgIndex, - derivativeFunctionIdentifier); + pointer.get()); } /// Returns the entry point for the corresponding autodiff derivative @@ -318,16 +331,16 @@ struct SILDeclRef { AutoDiffDerivativeFunctionIdentifier *derivativeId) const { assert(derivativeId); SILDeclRef declRef = *this; - declRef.derivativeFunctionIdentifier = derivativeId; + declRef.pointer = derivativeId; return declRef; } /// Returns the entry point for the original function corresponding to an /// autodiff derivative function. SILDeclRef asAutoDiffOriginalFunction() const { - assert(derivativeFunctionIdentifier); + assert(pointer.get()); SILDeclRef declRef = *this; - declRef.derivativeFunctionIdentifier = nullptr; + declRef.pointer = (AutoDiffDerivativeFunctionIdentifier *)nullptr; return declRef; } @@ -405,13 +418,14 @@ struct SILDeclRef { bool canBeDynamicReplacement() const; bool isAutoDiffDerivativeFunction() const { - return derivativeFunctionIdentifier != nullptr; + return pointer.is() && + pointer.get() != nullptr; } AutoDiffDerivativeFunctionIdentifier * getAutoDiffDerivativeFunctionIdentifier() const { assert(isAutoDiffDerivativeFunction()); - return derivativeFunctionIdentifier; + return pointer.get(); } private: @@ -422,7 +436,7 @@ struct SILDeclRef { AutoDiffDerivativeFunctionIdentifier *derivativeId) : loc(Loc::getFromOpaqueValue(opaqueLoc)), kind(kind), isForeign(isForeign), defaultArgIndex(defaultArgIndex), - derivativeFunctionIdentifier(derivativeId) {} + pointer(derivativeId) {} }; inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, SILDeclRef C) { @@ -457,7 +471,7 @@ template<> struct DenseMapInfo { ? UnsignedInfo::getHashValue(Val.defaultArgIndex) : 0; unsigned h4 = UnsignedInfo::getHashValue(Val.isForeign); - unsigned h5 = PointerInfo::getHashValue(Val.derivativeFunctionIdentifier); + unsigned h5 = PointerInfo::getHashValue(Val.pointer.getOpaqueValue()); return h1 ^ (h2 << 4) ^ (h3 << 9) ^ (h4 << 7) ^ (h5 << 11); } static bool isEqual(swift::SILDeclRef const &LHS, diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 7dfa9a3bd3094..d9e94bd048652 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -1683,7 +1683,7 @@ namespace { void emitNonoverriddenMethod(SILDeclRef fn) { // TODO: Derivative functions do not distinguish themselves in the mangled // names of method descriptor symbols yet, causing symbol name collisions. - if (fn.derivativeFunctionIdentifier) + if (fn.getDerivativeFunctionIdentifier()) return; HasNonoverriddenMethods = true; diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index d303956b23d23..d829af2b72e12 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -119,10 +119,11 @@ bool swift::requiresForeignEntryPoint(ValueDecl *vd) { SILDeclRef::SILDeclRef(ValueDecl *vd, SILDeclRef::Kind kind, bool isForeign, AutoDiffDerivativeFunctionIdentifier *derivativeId) : loc(vd), kind(kind), isForeign(isForeign), defaultArgIndex(0), - derivativeFunctionIdentifier(derivativeId) {} + pointer(derivativeId) {} SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign) - : defaultArgIndex(0), derivativeFunctionIdentifier(nullptr) { + : defaultArgIndex(0), + pointer((AutoDiffDerivativeFunctionIdentifier *)nullptr) { if (auto *vd = baseLoc.dyn_cast()) { if (auto *fd = dyn_cast(vd)) { // Map FuncDecls directly to Func SILDeclRefs. @@ -164,7 +165,7 @@ SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign) SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, GenericSignature prespecializedSig) : SILDeclRef(baseLoc, false) { - specializedSignature = prespecializedSig; + pointer = prespecializedSig.getPointer(); } Optional SILDeclRef::getAnyFunctionRef() const { @@ -232,7 +233,7 @@ bool SILDeclRef::isImplicit() const { SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const { // Prespecializations are public. - if (specializedSignature) { + if (getSpecializedSignature()) { return SILLinkage::Public; } @@ -678,6 +679,7 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { using namespace Mangle; ASTMangler mangler; + auto *derivativeFunctionIdentifier = getDerivativeFunctionIdentifier(); if (derivativeFunctionIdentifier) { std::string originalMangled = asAutoDiffOriginalFunction().mangle(MKind); auto *silParameterIndices = autodiff::getLoweredParameterIndices( @@ -716,14 +718,15 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { } // Mangle prespecializations. - if (specializedSignature) { + if (getSpecializedSignature()) { SILDeclRef nonSpecializedDeclRef = *this; - nonSpecializedDeclRef.specializedSignature = GenericSignature(); + nonSpecializedDeclRef.pointer = + (AutoDiffDerivativeFunctionIdentifier *)nullptr; auto mangledNonSpecializedString = nonSpecializedDeclRef.mangle(); auto *funcDecl = cast(getDecl()); auto genericSig = funcDecl->getGenericSignature(); return GenericSpecializationMangler::manglePrespecialization( - mangledNonSpecializedString, genericSig, specializedSignature); + mangledNonSpecializedString, genericSig, getSpecializedSignature()); } ASTMangler::SymbolKind SKind = ASTMangler::SymbolKind::Default; @@ -818,7 +821,7 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { // Returns true if the given JVP/VJP SILDeclRef requires a new vtable entry. // FIXME(TF-1213): Also consider derived declaration `@derivative` attributes. static bool derivativeFunctionRequiresNewVTableEntry(SILDeclRef declRef) { - assert(declRef.derivativeFunctionIdentifier && + assert(declRef.getDerivativeFunctionIdentifier() && "Expected a derivative function SILDeclRef"); auto overridden = declRef.getOverridden(); if (!overridden) @@ -828,7 +831,7 @@ static bool derivativeFunctionRequiresNewVTableEntry(SILDeclRef declRef) { declRef.getDecl()->getAttrs().getAttributes(), [&](const DifferentiableAttr *derivedDiffAttr) { return derivedDiffAttr->getParameterIndices() == - declRef.derivativeFunctionIdentifier->getParameterIndices(); + declRef.getDerivativeFunctionIdentifier()->getParameterIndices(); }); assert(derivedDiffAttr && "Expected `@differentiable` attribute"); // Otherwise, if the base `@differentiable` attribute specifies a derivative @@ -838,7 +841,7 @@ static bool derivativeFunctionRequiresNewVTableEntry(SILDeclRef declRef) { overridden.getDecl()->getAttrs().getAttributes(); for (auto *baseDiffAttr : baseDiffAttrs) { if (baseDiffAttr->getParameterIndices() == - declRef.derivativeFunctionIdentifier->getParameterIndices()) + declRef.getDerivativeFunctionIdentifier()->getParameterIndices()) return false; } // Otherwise, if there is no base `@differentiable` attribute exists, then a @@ -847,7 +850,7 @@ static bool derivativeFunctionRequiresNewVTableEntry(SILDeclRef declRef) { } bool SILDeclRef::requiresNewVTableEntry() const { - if (derivativeFunctionIdentifier) + if (getDerivativeFunctionIdentifier()) if (derivativeFunctionRequiresNewVTableEntry(*this)) return true; if (!hasDecl()) @@ -928,15 +931,16 @@ SILDeclRef SILDeclRef::getNextOverriddenVTableEntry() const { // JVPs/VJPs are overridden only if the base declaration has a // `@differentiable` attribute with the same parameter indices. - if (derivativeFunctionIdentifier) { + if (getDerivativeFunctionIdentifier()) { auto overriddenAttrs = overridden.getDecl()->getAttrs().getAttributes(); for (const auto *attr : overriddenAttrs) { if (attr->getParameterIndices() != - derivativeFunctionIdentifier->getParameterIndices()) + getDerivativeFunctionIdentifier()->getParameterIndices()) continue; - auto *overriddenDerivativeId = overridden.derivativeFunctionIdentifier; - overridden.derivativeFunctionIdentifier = + auto *overriddenDerivativeId = + overridden.getDerivativeFunctionIdentifier(); + overridden.pointer = AutoDiffDerivativeFunctionIdentifier::get( overriddenDerivativeId->getKind(), overriddenDerivativeId->getParameterIndices(), diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index fc175f4460017..f2746d232d0bc 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -3266,7 +3266,7 @@ TypeConverter::getConstantInfo(TypeExpansionContext expansion, // preserving SIL typing invariants. // // Always use (ad) to compute lowered derivative function types. - if (auto *derivativeId = constant.derivativeFunctionIdentifier) { + if (auto *derivativeId = constant.getDerivativeFunctionIdentifier()) { // Get lowered original function type. auto origFnConstantInfo = getConstantInfo( TypeExpansionContext::minimal(), constant.asAutoDiffOriginalFunction()); diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 6fd8616f21be7..3b057395d2156 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -349,9 +349,9 @@ void SILDeclRef::print(raw_ostream &OS) const { if (isForeign) OS << (isDot ? '.' : '!') << "foreign"; - if (derivativeFunctionIdentifier) { + if (getDerivativeFunctionIdentifier()) { OS << ((isDot || isForeign) ? '.' : '!'); - switch (derivativeFunctionIdentifier->getKind()) { + switch (getDerivativeFunctionIdentifier()->getKind()) { case AutoDiffDerivativeFunctionKind::JVP: OS << "jvp."; break; @@ -359,9 +359,9 @@ void SILDeclRef::print(raw_ostream &OS) const { OS << "vjp."; break; } - OS << derivativeFunctionIdentifier->getParameterIndices()->getString(); + OS << getDerivativeFunctionIdentifier()->getParameterIndices()->getString(); if (auto derivativeGenSig = - derivativeFunctionIdentifier->getDerivativeGenericSignature()) { + getDerivativeFunctionIdentifier()->getDerivativeGenericSignature()) { OS << "." << derivativeGenSig; } } diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index af7eca86b8330..2320cedb9b549 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -2520,7 +2520,7 @@ getFunctionInterfaceTypeWithCaptures(TypeConverter &TC, } CanAnyFunctionType TypeConverter::makeConstantInterfaceType(SILDeclRef c) { - if (auto *derivativeId = c.derivativeFunctionIdentifier) { + if (auto *derivativeId = c.getDerivativeFunctionIdentifier()) { auto originalFnTy = makeConstantInterfaceType(c.asAutoDiffOriginalFunction()); auto *derivativeFnTy = originalFnTy->getAutoDiffDerivativeFunctionType( diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index 0ee11d74f6439..7abac232b499b 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -4577,7 +4577,7 @@ getWitnessFunctionRef(SILGenFunction &SGF, SILLocation loc) { switch (witnessKind) { case WitnessDispatchKind::Static: - if (auto *derivativeId = witness.derivativeFunctionIdentifier) { + if (auto *derivativeId = witness.getDerivativeFunctionIdentifier()) { auto originalFn = SGF.emitGlobalFunctionRef(loc, witness.asAutoDiffOriginalFunction()); auto *loweredParamIndices = autodiff::getLoweredParameterIndices( @@ -4594,7 +4594,7 @@ getWitnessFunctionRef(SILGenFunction &SGF, } return SGF.emitGlobalFunctionRef(loc, witness); case WitnessDispatchKind::Dynamic: - assert(!witness.derivativeFunctionIdentifier); + assert(!witness.getDerivativeFunctionIdentifier()); return SGF.emitDynamicMethodRef(loc, witness, witnessFTy).getValue(); case WitnessDispatchKind::Witness: { auto typeAndConf = @@ -4609,7 +4609,7 @@ getWitnessFunctionRef(SILGenFunction &SGF, // If `witness` is a derivative function `SILDeclRef`, replace the // derivative function identifier's generic signature with the witness thunk // substitution map's generic signature. - if (auto *derivativeId = witness.derivativeFunctionIdentifier) { + if (auto *derivativeId = witness.getDerivativeFunctionIdentifier()) { auto *newDerivativeId = AutoDiffDerivativeFunctionIdentifier::get( derivativeId->getKind(), derivativeId->getParameterIndices(), witnessSubs.getGenericSignature(), SGF.getASTContext()); diff --git a/lib/SILGen/SILGenThunk.cpp b/lib/SILGen/SILGenThunk.cpp index d51ec1dfad687..74d1efeceec69 100644 --- a/lib/SILGen/SILGenThunk.cpp +++ b/lib/SILGen/SILGenThunk.cpp @@ -175,7 +175,7 @@ getOrCreateReabstractionThunk(CanSILFunctionType thunkType, SILFunction *SILGenModule::getOrCreateAutoDiffClassMethodThunk( SILDeclRef derivativeFnDeclRef, CanSILFunctionType constantTy) { - auto *derivativeId = derivativeFnDeclRef.derivativeFunctionIdentifier; + auto *derivativeId = derivativeFnDeclRef.getDerivativeFunctionIdentifier(); assert(derivativeId); auto *derivativeFnDecl = derivativeFnDeclRef.getDecl(); diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 2dd3de4156090..3f4313f4374b1 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -90,7 +90,7 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, implFn = getDynamicThunk( derived, Types.getConstantInfo(TypeExpansionContext::minimal(), derived) .SILFnType); - } else if (auto *derivativeId = derived.derivativeFunctionIdentifier) { + } else if (auto *derivativeId = derived.getDerivativeFunctionIdentifier()) { // For JVP/VJP methods, create a vtable entry thunk. The thunk contains an // `differentiable_function` instruction, which is later filled during the // differentiation transform. @@ -168,7 +168,7 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, base.kind == SILDeclRef::Kind::Allocator); } // TODO(TF-685): Use proper autodiff thunk mangling. - if (auto *derivativeId = derived.derivativeFunctionIdentifier) { + if (auto *derivativeId = derived.getDerivativeFunctionIdentifier()) { switch (derivativeId->getKind()) { case AutoDiffDerivativeFunctionKind::JVP: name += "_jvp"; @@ -743,7 +743,7 @@ SILFunction *SILGenModule::emitProtocolWitness( std::string nameBuffer = NewMangler.mangleWitnessThunk(manglingConformance, requirement.getDecl()); // TODO(TF-685): Proper mangling for derivative witness thunks. - if (auto *derivativeId = requirement.derivativeFunctionIdentifier) { + if (auto *derivativeId = requirement.getDerivativeFunctionIdentifier()) { std::string kindString; switch (derivativeId->getKind()) { case AutoDiffDerivativeFunctionKind::JVP: From f9d6c6a6196751b36e8efed736ba4d4ebae3a526 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Thu, 8 Oct 2020 14:18:00 -0700 Subject: [PATCH 416/745] [Dependency Scanner] Refactor ModuleDependencies to represent binary-only Swift modules explicitly This matches the behavior of the current client (`swift-driver`) and reduces ambiguity in how the nodes in the graph are to be treated. Swift dependencies with a textual interface, for example, must be built into a binary module by clients. Swift dependencies without a textual interface, with only a binary module, are to be used directly, without any up-to-date checks. Note, this is distinct from Swift dependencies that have a textual interface, for which we also detect potential pre-build binary module candidates. Those are still reported in the `details` field of textual Swift dependencies as `prebuiltModuleCandidates`. --- include/swift/AST/ModuleDependencies.h | 154 +++++++++++------- .../Serialization/ModuleDependencyScanner.h | 10 +- lib/AST/ModuleDependencies.cpp | 78 ++++++--- lib/AST/ModuleLoader.cpp | 13 +- .../ClangModuleDependencyScanner.cpp | 10 +- lib/FrontendTool/ScanDependencies.cpp | 142 +++++++++------- lib/Serialization/ModuleDependencyScanner.cpp | 12 +- lib/Serialization/SerializedModuleLoader.cpp | 7 +- .../Inputs/ModuleDependencyGraph.swift | 96 +++++++---- .../ScanDependencies/binary_module_only.swift | 25 +++ .../module_deps_external.swift | 2 +- 11 files changed, 358 insertions(+), 191 deletions(-) create mode 100644 test/ScanDependencies/binary_module_only.swift diff --git a/include/swift/AST/ModuleDependencies.h b/include/swift/AST/ModuleDependencies.h index 5eab3844b32d8..af43842bcc6e1 100644 --- a/include/swift/AST/ModuleDependencies.h +++ b/include/swift/AST/ModuleDependencies.h @@ -34,7 +34,8 @@ class Identifier; /// Which kind of module dependencies we are looking for. enum class ModuleDependenciesKind : int8_t { - Swift, + SwiftTextual, + SwiftBinary, // Placeholder dependencies are a kind of dependencies used only by the // dependency scanner. They are swift modules that the scanner will not be // able to locate in its search paths and which are the responsibility of the @@ -68,18 +69,13 @@ class ModuleDependenciesStorageBase { public: const ModuleDependenciesKind dependencyKind; - ModuleDependenciesStorageBase(ModuleDependenciesKind dependencyKind, - const std::string &compiledModulePath) - : dependencyKind(dependencyKind), - compiledModulePath(compiledModulePath) { } + ModuleDependenciesStorageBase(ModuleDependenciesKind dependencyKind) + : dependencyKind(dependencyKind) { } virtual ModuleDependenciesStorageBase *clone() const = 0; virtual ~ModuleDependenciesStorageBase(); - /// The path to the compiled module file. - const std::string compiledModulePath; - /// The set of modules on which this module depends. std::vector moduleDependencies; }; @@ -87,7 +83,8 @@ class ModuleDependenciesStorageBase { /// Describes the dependencies of a Swift module. /// /// This class is mostly an implementation detail for \c ModuleDependencies. -class SwiftModuleDependenciesStorage : public ModuleDependenciesStorageBase { +class SwiftTextualModuleDependenciesStorage : + public ModuleDependenciesStorageBase { public: /// The Swift interface file, if it can be used to generate the module file. const Optional swiftInterfaceFile; @@ -122,16 +119,14 @@ class SwiftModuleDependenciesStorage : public ModuleDependenciesStorageBase { /// (Clang) modules on which the bridging header depends. std::vector bridgingModuleDependencies; - SwiftModuleDependenciesStorage( - const std::string &compiledModulePath, + SwiftTextualModuleDependenciesStorage( const Optional &swiftInterfaceFile, ArrayRef compiledModuleCandidates, ArrayRef buildCommandLine, ArrayRef extraPCMArgs, StringRef contextHash, bool isFramework - ) : ModuleDependenciesStorageBase(ModuleDependenciesKind::Swift, - compiledModulePath), + ) : ModuleDependenciesStorageBase(ModuleDependenciesKind::SwiftTextual), swiftInterfaceFile(swiftInterfaceFile), compiledModuleCandidates(compiledModuleCandidates.begin(), compiledModuleCandidates.end()), @@ -140,11 +135,47 @@ class SwiftModuleDependenciesStorage : public ModuleDependenciesStorageBase { contextHash(contextHash), isFramework(isFramework) { } ModuleDependenciesStorageBase *clone() const override { - return new SwiftModuleDependenciesStorage(*this); + return new SwiftTextualModuleDependenciesStorage(*this); + } + + static bool classof(const ModuleDependenciesStorageBase *base) { + return base->dependencyKind == ModuleDependenciesKind::SwiftTextual; } +}; + +/// Describes the dependencies of a pre-built Swift module (with no .swiftinterface). +/// +/// This class is mostly an implementation detail for \c ModuleDependencies. +class SwiftBinaryModuleDependencyStorage : public ModuleDependenciesStorageBase { +public: + SwiftBinaryModuleDependencyStorage(const std::string &compiledModulePath, + const std::string &moduleDocPath, + const std::string &sourceInfoPath, + const bool isFramework) + : ModuleDependenciesStorageBase(ModuleDependenciesKind::SwiftBinary), + compiledModulePath(compiledModulePath), + moduleDocPath(moduleDocPath), + sourceInfoPath(sourceInfoPath), + isFramework(isFramework) {} + + ModuleDependenciesStorageBase *clone() const override { + return new SwiftBinaryModuleDependencyStorage(*this); + } + + /// The path to the .swiftmodule file. + const std::string compiledModulePath; + + /// The path to the .swiftModuleDoc file. + const std::string moduleDocPath; + + /// The path to the .swiftSourceInfo file. + const std::string sourceInfoPath; + + /// A flag that indicates this dependency is a framework + const bool isFramework; static bool classof(const ModuleDependenciesStorageBase *base) { - return base->dependencyKind == ModuleDependenciesKind::Swift; + return base->dependencyKind == ModuleDependenciesKind::SwiftBinary; } }; @@ -166,13 +197,11 @@ class ClangModuleDependenciesStorage : public ModuleDependenciesStorageBase { const std::vector fileDependencies; ClangModuleDependenciesStorage( - const std::string &compiledModulePath, const std::string &moduleMapFile, const std::string &contextHash, const std::vector &nonPathCommandLine, const std::vector &fileDependencies - ) : ModuleDependenciesStorageBase(ModuleDependenciesKind::Clang, - compiledModulePath), + ) : ModuleDependenciesStorageBase(ModuleDependenciesKind::Clang), moduleMapFile(moduleMapFile), contextHash(contextHash), nonPathCommandLine(nonPathCommandLine), @@ -190,20 +219,24 @@ class ClangModuleDependenciesStorage : public ModuleDependenciesStorageBase { /// Describes an placeholder Swift module dependency module stub. /// /// This class is mostly an implementation detail for \c ModuleDependencies. -class PlaceholderSwiftModuleDependencyStorage : public ModuleDependenciesStorageBase { + +class SwiftPlaceholderModuleDependencyStorage : public ModuleDependenciesStorageBase { public: - PlaceholderSwiftModuleDependencyStorage(const std::string &compiledModulePath, - const std::string &moduleDocPath, - const std::string &sourceInfoPath) - : ModuleDependenciesStorageBase(ModuleDependenciesKind::SwiftPlaceholder, - compiledModulePath), + SwiftPlaceholderModuleDependencyStorage(const std::string &compiledModulePath, + const std::string &moduleDocPath, + const std::string &sourceInfoPath) + : ModuleDependenciesStorageBase(ModuleDependenciesKind::SwiftPlaceholder), + compiledModulePath(compiledModulePath), moduleDocPath(moduleDocPath), sourceInfoPath(sourceInfoPath) {} ModuleDependenciesStorageBase *clone() const override { - return new PlaceholderSwiftModuleDependencyStorage(*this); + return new SwiftPlaceholderModuleDependencyStorage(*this); } + /// The path to the .swiftmodule file. + const std::string compiledModulePath; + /// The path to the .swiftModuleDoc file. const std::string moduleDocPath; @@ -248,43 +281,41 @@ class ModuleDependencies { ArrayRef extraPCMArgs, StringRef contextHash, bool isFramework) { - std::string compiledModulePath; return ModuleDependencies( - std::make_unique( - compiledModulePath, swiftInterfaceFile, compiledCandidates, buildCommands, + std::make_unique( + swiftInterfaceFile, compiledCandidates, buildCommands, extraPCMArgs, contextHash, isFramework)); } /// Describe the module dependencies for a serialized or parsed Swift module. - static ModuleDependencies forSwiftModule( - const std::string &compiledModulePath, bool isFramework) { + static ModuleDependencies forSwiftBinaryModule( + const std::string &compiledModulePath, + const std::string &moduleDocPath, + const std::string &sourceInfoPath, + bool isFramework) { return ModuleDependencies( - std::make_unique( - compiledModulePath, None, ArrayRef(), ArrayRef(), - ArrayRef(), StringRef(), isFramework)); + std::make_unique( + compiledModulePath, moduleDocPath, sourceInfoPath, isFramework)); } /// Describe the main Swift module. static ModuleDependencies forMainSwiftModule(ArrayRef extraPCMArgs) { - std::string compiledModulePath; return ModuleDependencies( - std::make_unique( - compiledModulePath, None, ArrayRef(), - ArrayRef(), extraPCMArgs, StringRef(), false)); + std::make_unique( + None, ArrayRef(), ArrayRef(), + extraPCMArgs, StringRef(), false)); } /// Describe the module dependencies for a Clang module that can be /// built from a module map and headers. static ModuleDependencies forClangModule( - const std::string &compiledModulePath, const std::string &moduleMapFile, const std::string &contextHash, const std::vector &nonPathCommandLine, const std::vector &fileDependencies) { return ModuleDependencies( std::make_unique( - compiledModulePath, moduleMapFile, contextHash, nonPathCommandLine, - fileDependencies)); + moduleMapFile, contextHash, nonPathCommandLine, fileDependencies)); } /// Describe a placeholder dependency swift module. @@ -293,38 +324,45 @@ class ModuleDependencies { const std::string &moduleDocPath, const std::string &sourceInfoPath) { return ModuleDependencies( - std::make_unique( + std::make_unique( compiledModulePath, moduleDocPath, sourceInfoPath)); } - /// Retrieve the path to the compiled module. - const std::string getCompiledModulePath() const { - return storage->compiledModulePath; - } - /// Retrieve the module-level dependencies. ArrayRef getModuleDependencies() const { return storage->moduleDependencies; } - /// Whether the dependencies are for a Swift module. + /// Whether the dependencies are for a Swift module: either Textual, Binary, or Placeholder. bool isSwiftModule() const; + /// Whether the dependencies are for a textual Swift module. + bool isSwiftTextualModule() const; + + /// Whether the dependencies are for a binary Swift module. + bool isSwiftBinaryModule() const; + /// Whether this represents a placeholder module stub - bool isPlaceholderSwiftModule() const; + bool isSwiftPlaceholderModule() const; + + /// Whether the dependencies are for a Clang module. + bool isClangModule() const; ModuleDependenciesKind getKind() const { return storage->dependencyKind; } /// Retrieve the dependencies for a Swift module. - const SwiftModuleDependenciesStorage *getAsSwiftModule() const; + const SwiftTextualModuleDependenciesStorage *getAsSwiftTextualModule() const; + + /// Retrieve the dependencies for a binary Swift module. + const SwiftBinaryModuleDependencyStorage *getAsSwiftBinaryModule() const; /// Retrieve the dependencies for a Clang module. const ClangModuleDependenciesStorage *getAsClangModule() const; /// Retrieve the dependencies for a placeholder dependency module stub. - const PlaceholderSwiftModuleDependencyStorage * - getAsPlaceholderDependencyModule() const; + const SwiftPlaceholderModuleDependencyStorage * + getAsPlaceholderDependencyModule() const; /// Add a dependency on the given module, if it was not already in the set. void addModuleDependency(StringRef module, @@ -363,11 +401,14 @@ class ModuleDependenciesCache { /// encountered. std::vector AllModules; - /// Dependencies for Swift modules that have already been computed. - llvm::StringMap SwiftModuleDependencies; + /// Dependencies for Textual Swift modules that have already been computed. + llvm::StringMap SwiftTextualModuleDependencies; - /// Dependencies for Swift placeholder dependency modules. - llvm::StringMap PlaceholderSwiftModuleDependencies; + /// Dependencies for Binary Swift modules that have already been computed. + llvm::StringMap SwiftBinaryModuleDependencies; + + /// Dependencies for Swift placeholder dependency modules that have already been computed. + llvm::StringMap SwiftPlaceholderModuleDependencies; /// Dependencies for Clang modules that have already been computed. llvm::StringMap ClangModuleDependencies; @@ -429,8 +470,7 @@ class ModuleDependenciesCache { /// Record dependencies for the given module. void recordDependencies(StringRef moduleName, - ModuleDependencies dependencies, - ModuleDependenciesKind kind); + ModuleDependencies dependencies); /// Update stored dependencies for the given module. void updateDependencies(ModuleDependencyID moduleID, diff --git a/include/swift/Serialization/ModuleDependencyScanner.h b/include/swift/Serialization/ModuleDependencyScanner.h index 0528b8ccc3364..86b39490435ff 100644 --- a/include/swift/Serialization/ModuleDependencyScanner.h +++ b/include/swift/Serialization/ModuleDependencyScanner.h @@ -40,18 +40,13 @@ namespace swift { public: Optional dependencies; - /// Describes the kind of dependencies this scanner is able to identify - ModuleDependenciesKind dependencyKind; - ModuleDependencyScanner( ASTContext &ctx, ModuleLoadingMode LoadMode, Identifier moduleName, InterfaceSubContextDelegate &astDelegate, - ModuleDependenciesKind dependencyKind = ModuleDependenciesKind::Swift, ScannerKind kind = MDS_plain) : SerializedModuleLoaderBase(ctx, nullptr, LoadMode, /*IgnoreSwiftSourceInfoFile=*/true), - kind(kind), moduleName(moduleName), astDelegate(astDelegate), - dependencyKind(dependencyKind) {} + kind(kind), moduleName(moduleName), astDelegate(astDelegate) {} std::error_code findModuleFilesInDirectory( ImportPath::Element ModuleID, @@ -60,7 +55,7 @@ namespace swift { std::unique_ptr *ModuleBuffer, std::unique_ptr *ModuleDocBuffer, std::unique_ptr *ModuleSourceInfoBuffer, - bool IsFramework) override; + bool IsFramework) override; virtual void collectVisibleTopLevelModuleNames( SmallVectorImpl &names) const override { @@ -106,7 +101,6 @@ namespace swift { StringRef PlaceholderDependencyModuleMap, InterfaceSubContextDelegate &astDelegate) : ModuleDependencyScanner(ctx, LoadMode, moduleName, astDelegate, - ModuleDependenciesKind::SwiftPlaceholder, MDS_placeholder) { // FIXME: Find a better place for this map to live, to avoid diff --git a/lib/AST/ModuleDependencies.cpp b/lib/AST/ModuleDependencies.cpp index 45821a23424a8..b59aeb59e3929 100644 --- a/lib/AST/ModuleDependencies.cpp +++ b/lib/AST/ModuleDependencies.cpp @@ -21,17 +21,37 @@ using namespace swift; ModuleDependenciesStorageBase::~ModuleDependenciesStorageBase() { } bool ModuleDependencies::isSwiftModule() const { - return isa(storage.get()); + return isSwiftTextualModule() || + isSwiftBinaryModule() || + isSwiftPlaceholderModule(); } -bool ModuleDependencies::isPlaceholderSwiftModule() const { - return isa(storage.get()); +bool ModuleDependencies::isSwiftTextualModule() const { + return isa(storage.get()); +} + +bool ModuleDependencies::isSwiftBinaryModule() const { + return isa(storage.get()); +} + +bool ModuleDependencies::isSwiftPlaceholderModule() const { + return isa(storage.get()); +} + +bool ModuleDependencies::isClangModule() const { + return isa(storage.get()); } /// Retrieve the dependencies for a Swift module. -const SwiftModuleDependenciesStorage * -ModuleDependencies::getAsSwiftModule() const { - return dyn_cast(storage.get()); +const SwiftTextualModuleDependenciesStorage * +ModuleDependencies::getAsSwiftTextualModule() const { + return dyn_cast(storage.get()); +} + +/// Retrieve the dependencies for a binary Swift dependency module. +const SwiftBinaryModuleDependencyStorage * +ModuleDependencies::getAsSwiftBinaryModule() const { + return dyn_cast(storage.get()); } /// Retrieve the dependencies for a Clang module. @@ -41,9 +61,9 @@ ModuleDependencies::getAsClangModule() const { } /// Retrieve the dependencies for a placeholder dependency module stub. -const PlaceholderSwiftModuleDependencyStorage * +const SwiftPlaceholderModuleDependencyStorage * ModuleDependencies::getAsPlaceholderDependencyModule() const { - return dyn_cast(storage.get()); + return dyn_cast(storage.get()); } void ModuleDependencies::addModuleDependency( @@ -72,7 +92,7 @@ void ModuleDependencies::addModuleDependencies( // If the storage is for an interface file, the only source file we // should see is that interface file. - auto swiftStorage = cast(storage.get()); + auto swiftStorage = cast(storage.get()); if (swiftStorage->swiftInterfaceFile) { assert(fileName == *swiftStorage->swiftInterfaceFile); return; @@ -83,26 +103,26 @@ void ModuleDependencies::addModuleDependencies( } Optional ModuleDependencies::getBridgingHeader() const { - auto swiftStorage = cast(storage.get()); + auto swiftStorage = cast(storage.get()); return swiftStorage->bridgingHeaderFile; } void ModuleDependencies::addBridgingHeader(StringRef bridgingHeader) { - auto swiftStorage = cast(storage.get()); + auto swiftStorage = cast(storage.get()); assert(!swiftStorage->bridgingHeaderFile); swiftStorage->bridgingHeaderFile = bridgingHeader.str(); } /// Add source files that the bridging header depends on. void ModuleDependencies::addBridgingSourceFile(StringRef bridgingSourceFile) { - auto swiftStorage = cast(storage.get()); + auto swiftStorage = cast(storage.get()); swiftStorage->bridgingSourceFiles.push_back(bridgingSourceFile.str()); } /// Add (Clang) module on which the bridging header depends. void ModuleDependencies::addBridgingModuleDependency( StringRef module, llvm::StringSet<> &alreadyAddedModules) { - auto swiftStorage = cast(storage.get()); + auto swiftStorage = cast(storage.get()); if (alreadyAddedModules.insert(module).second) swiftStorage->bridgingModuleDependencies.push_back(module.str()); } @@ -110,10 +130,12 @@ void ModuleDependencies::addBridgingModuleDependency( llvm::StringMap & ModuleDependenciesCache::getDependenciesMap(ModuleDependenciesKind kind) { switch (kind) { - case ModuleDependenciesKind::Swift: - return SwiftModuleDependencies; + case ModuleDependenciesKind::SwiftTextual: + return SwiftTextualModuleDependencies; + case ModuleDependenciesKind::SwiftBinary: + return SwiftBinaryModuleDependencies; case ModuleDependenciesKind::SwiftPlaceholder: - return PlaceholderSwiftModuleDependencies; + return SwiftPlaceholderModuleDependencies; case ModuleDependenciesKind::Clang: return ClangModuleDependencies; } @@ -123,10 +145,12 @@ ModuleDependenciesCache::getDependenciesMap(ModuleDependenciesKind kind) { const llvm::StringMap & ModuleDependenciesCache::getDependenciesMap(ModuleDependenciesKind kind) const { switch (kind) { - case ModuleDependenciesKind::Swift: - return SwiftModuleDependencies; + case ModuleDependenciesKind::SwiftTextual: + return SwiftTextualModuleDependencies; + case ModuleDependenciesKind::SwiftBinary: + return SwiftBinaryModuleDependencies; case ModuleDependenciesKind::SwiftPlaceholder: - return PlaceholderSwiftModuleDependencies; + return SwiftPlaceholderModuleDependencies; case ModuleDependenciesKind::Clang: return ClangModuleDependencies; } @@ -137,7 +161,8 @@ bool ModuleDependenciesCache::hasDependencies( StringRef moduleName, Optional kind) const { if (!kind) { - return hasDependencies(moduleName, ModuleDependenciesKind::Swift) || + return hasDependencies(moduleName, ModuleDependenciesKind::SwiftTextual) || + hasDependencies(moduleName, ModuleDependenciesKind::SwiftBinary) || hasDependencies(moduleName, ModuleDependenciesKind::SwiftPlaceholder) || hasDependencies(moduleName, ModuleDependenciesKind::Clang); } @@ -150,9 +175,12 @@ Optional ModuleDependenciesCache::findDependencies( StringRef moduleName, Optional kind) const { if (!kind) { - if (auto swiftDep = findDependencies( - moduleName, ModuleDependenciesKind::Swift)) - return swiftDep; + if (auto swiftTextualDep = findDependencies( + moduleName, ModuleDependenciesKind::SwiftTextual)) + return swiftTextualDep; + else if (auto swiftBinaryDep = findDependencies( + moduleName, ModuleDependenciesKind::SwiftBinary)) + return swiftBinaryDep; else if (auto swiftPlaceholderDep = findDependencies( moduleName, ModuleDependenciesKind::SwiftPlaceholder)) return swiftPlaceholderDep; @@ -170,8 +198,8 @@ Optional ModuleDependenciesCache::findDependencies( void ModuleDependenciesCache::recordDependencies( StringRef moduleName, - ModuleDependencies dependencies, - ModuleDependenciesKind kind) { + ModuleDependencies dependencies) { + auto kind = dependencies.getKind(); auto &map = getDependenciesMap(kind); assert(map.count(moduleName) == 0 && "Already added to map"); map.insert({moduleName, std::move(dependencies)}); diff --git a/lib/AST/ModuleLoader.cpp b/lib/AST/ModuleLoader.cpp index 0a62cbd1c07c2..56d8312dbc739 100644 --- a/lib/AST/ModuleLoader.cpp +++ b/lib/AST/ModuleLoader.cpp @@ -179,17 +179,22 @@ ModuleDependencies::collectCrossImportOverlayNames(ASTContext &ctx, // A map from secondary module name to a vector of overlay names. llvm::StringMap> result; // Mimic getModuleDefiningPath() for Swift and Clang module. - if (auto *swiftDep = dyn_cast(storage.get())) { + if (auto *swiftDep = getAsSwiftTextualModule()) { // Prefer interface path to binary module path if we have it. modulePath = swiftDep->swiftInterfaceFile; - if (!modulePath.hasValue()) - modulePath = swiftDep->compiledModulePath; assert(modulePath.hasValue()); StringRef parentDir = llvm::sys::path::parent_path(*modulePath); if (llvm::sys::path::extension(parentDir) == ".swiftmodule") { modulePath = parentDir.str(); } - } else if (auto *clangDep = dyn_cast(storage.get())){ + } else if (auto *swiftBinaryDep = getAsSwiftBinaryModule()) { + modulePath = swiftBinaryDep->compiledModulePath; + assert(modulePath.hasValue()); + StringRef parentDir = llvm::sys::path::parent_path(*modulePath); + if (llvm::sys::path::extension(parentDir) == ".swiftmodule") { + modulePath = parentDir.str(); + } + } else if (auto *clangDep = getAsClangModule()) { modulePath = clangDep->moduleMapFile; assert(modulePath.hasValue()); } else { // PlaceholderSwiftModuleDependencies diff --git a/lib/ClangImporter/ClangModuleDependencyScanner.cpp b/lib/ClangImporter/ClangModuleDependencyScanner.cpp index fbc693e5b22ef..3dd1673a16768 100644 --- a/lib/ClangImporter/ClangModuleDependencyScanner.cpp +++ b/lib/ClangImporter/ClangModuleDependencyScanner.cpp @@ -267,7 +267,6 @@ void ClangImporter::recordModuleDependencies( // Module-level dependencies. llvm::StringSet<> alreadyAddedModules; auto dependencies = ModuleDependencies::forClangModule( - clangModuleDep.ImplicitModulePCMPath, clangModuleDep.ClangModuleMapFile, clangModuleDep.ContextHash, swiftArgs, @@ -277,8 +276,7 @@ void ClangImporter::recordModuleDependencies( } cache.recordDependencies(clangModuleDep.ModuleName, - std::move(dependencies), - ModuleDependenciesKind::Clang); + std::move(dependencies)); } } @@ -327,10 +325,10 @@ bool ClangImporter::addBridgingHeaderDependencies( StringRef moduleName, ModuleDependenciesCache &cache) { auto targetModule = *cache.findDependencies( - moduleName, ModuleDependenciesKind::Swift); + moduleName, ModuleDependenciesKind::SwiftTextual); // If we've already recorded bridging header dependencies, we're done. - auto swiftDeps = targetModule.getAsSwiftModule(); + auto swiftDeps = targetModule.getAsSwiftTextualModule(); if (!swiftDeps->bridgingSourceFiles.empty() || !swiftDeps->bridgingModuleDependencies.empty()) return false; @@ -376,7 +374,7 @@ bool ClangImporter::addBridgingHeaderDependencies( // Update the cache with the new information for the module. cache.updateDependencies( - {moduleName.str(), ModuleDependenciesKind::Swift}, + {moduleName.str(), ModuleDependenciesKind::SwiftTextual}, std::move(targetModule)); return false; diff --git a/lib/FrontendTool/ScanDependencies.cpp b/lib/FrontendTool/ScanDependencies.cpp index a39468e13fb21..eef2b993fb428 100644 --- a/lib/FrontendTool/ScanDependencies.cpp +++ b/lib/FrontendTool/ScanDependencies.cpp @@ -158,7 +158,7 @@ static std::vector resolveDirectDependencies( InterfaceSubContextDelegate &ASTDelegate) { auto &ctx = instance.getASTContext(); auto knownDependencies = *cache.findDependencies(module.first, module.second); - auto isSwift = knownDependencies.isSwiftModule(); + auto isSwift = knownDependencies.isSwiftTextualModule(); // Find the dependencies of every module this module directly depends on. std::vector result; @@ -189,7 +189,7 @@ static std::vector resolveDirectDependencies( // Add the Clang modules referenced from the bridging header to the // set of Clang modules we know about. - auto swiftDeps = knownDependencies.getAsSwiftModule(); + auto swiftDeps = knownDependencies.getAsSwiftTextualModule(); for (const auto &clangDep : swiftDeps->bridgingModuleDependencies) { findAllImportedClangModules(ctx, clangDep, cache, allClangModules, knownModules); @@ -214,7 +214,9 @@ static std::vector resolveDirectDependencies( // ASTContext::getModuleDependencies returns dependencies for a module with a given name. // This Clang module may have the same name as the Swift module we are resolving, so we // need to make sure we don't add a dependency from a Swift module to itself. - if (found->getKind() == ModuleDependenciesKind::Swift && clangDep != module.first) + if ((found->getKind() == ModuleDependenciesKind::SwiftTextual || + found->getKind() == ModuleDependenciesKind::SwiftBinary) && + clangDep != module.first) result.push_back({clangDep, found->getKind()}); } } @@ -258,17 +260,18 @@ static void discoverCrosssImportOverlayDependencies( auto dummyMainDependencies = ModuleDependencies::forMainSwiftModule({}); // Update main module's dependencies to include these new overlays. - auto mainDep = *cache.findDependencies(mainModuleName, ModuleDependenciesKind::Swift); + auto mainDep = *cache.findDependencies(mainModuleName, + ModuleDependenciesKind::SwiftTextual); std::for_each(newOverlays.begin(), newOverlays.end(), [&](Identifier modName) { dummyMainDependencies.addModuleDependency(modName.str()); mainDep.addModuleDependency(modName.str()); }); - cache.updateDependencies({mainModuleName.str(), ModuleDependenciesKind::Swift}, mainDep); + cache.updateDependencies({mainModuleName.str(), + ModuleDependenciesKind::SwiftTextual}, mainDep); // Record the dummy main module's direct dependencies. The dummy main module // only directly depend on these newly discovered overlay modules. - cache.recordDependencies(dummyMainName, dummyMainDependencies, - ModuleDependenciesKind::Swift); + cache.recordDependencies(dummyMainName, dummyMainDependencies); llvm::SetVector, std::set> allModules; @@ -321,8 +324,11 @@ namespace { unsigned indentLevel) { out << "{\n"; std::string moduleKind; - if (module.second == ModuleDependenciesKind::Swift) + if (module.second == ModuleDependenciesKind::SwiftTextual) moduleKind = "swift"; + else if (module.second == ModuleDependenciesKind::SwiftBinary) + // FIXME: rename to be consistent in the clients (swift-driver) + moduleKind = "swiftPrebuiltExternal"; else if (module.second == ModuleDependenciesKind::SwiftPlaceholder) moduleKind = "swiftPlaceholder"; else @@ -434,23 +440,31 @@ static void writeJSON(llvm::raw_ostream &out, out.indent(2 * 2); out << "{\n"; - auto externalSwiftDep = moduleDeps.getAsPlaceholderDependencyModule(); - auto swiftDeps = moduleDeps.getAsSwiftModule(); + auto swiftPlaceholderDeps = moduleDeps.getAsPlaceholderDependencyModule(); + auto swiftTextualDeps = moduleDeps.getAsSwiftTextualModule(); + auto swiftBinaryDeps = moduleDeps.getAsSwiftBinaryModule(); auto clangDeps = moduleDeps.getAsClangModule(); // Module path. const char *modulePathSuffix = moduleDeps.isSwiftModule() ? ".swiftmodule" : ".pcm"; - std::string modulePath = externalSwiftDep - ? externalSwiftDep->compiledModulePath - : module.first + modulePathSuffix; + std::string modulePath; + if (swiftPlaceholderDeps) + modulePath = swiftPlaceholderDeps->compiledModulePath; + else if (swiftBinaryDeps) + modulePath = swiftBinaryDeps->compiledModulePath; + else + modulePath = module.first + modulePathSuffix; + writeJSONSingleField(out, "modulePath", modulePath, /*indentLevel=*/3, /*trailingComma=*/true); + // Artem Refactoring + { // Source files. - if (swiftDeps) { - writeJSONSingleField(out, "sourceFiles", swiftDeps->sourceFiles, 3, + if (swiftTextualDeps) { + writeJSONSingleField(out, "sourceFiles", swiftTextualDeps->sourceFiles, 3, /*trailingComma=*/true); } else if (clangDeps) { writeJSONSingleField(out, "sourceFiles", clangDeps->fileDependencies, 3, @@ -458,7 +472,7 @@ static void writeJSON(llvm::raw_ostream &out, } // Direct dependencies. - if (swiftDeps || clangDeps) + if (swiftTextualDeps || swiftBinaryDeps || clangDeps) writeJSONSingleField(out, "directDependencies", directDependencies, 3, /*trailingComma=*/true); @@ -466,25 +480,25 @@ static void writeJSON(llvm::raw_ostream &out, out.indent(3 * 2); out << "\"details\": {\n"; out.indent(4 * 2); - if (swiftDeps) { + if (swiftTextualDeps) { out << "\"swift\": {\n"; - /// Swift interface file, if any. - if (swiftDeps->swiftInterfaceFile) { - writeJSONSingleField( - out, "moduleInterfacePath", - *swiftDeps->swiftInterfaceFile, 5, - /*trailingComma=*/true); + /// Swift interface file, if there is one. The main module, for example, will not have + /// an interface file. + if (swiftTextualDeps->swiftInterfaceFile) { + writeJSONSingleField(out, "moduleInterfacePath", + *swiftTextualDeps->swiftInterfaceFile, 5, + /*trailingComma=*/true); writeJSONSingleField(out, "contextHash", - swiftDeps->contextHash, 5, + swiftTextualDeps->contextHash, 5, /*trailingComma=*/true); out.indent(5 * 2); out << "\"commandLine\": [\n"; - for (auto &arg :swiftDeps->buildCommandLine) { + for (auto &arg :swiftTextualDeps->buildCommandLine) { out.indent(6 * 2); out << "\"" << arg << "\""; - if (&arg != &swiftDeps->buildCommandLine.back()) + if (&arg != &swiftTextualDeps->buildCommandLine.back()) out << ","; out << "\n"; } @@ -492,70 +506,86 @@ static void writeJSON(llvm::raw_ostream &out, out << "],\n"; out.indent(5 * 2); out << "\"compiledModuleCandidates\": [\n"; - for (auto &candidate: swiftDeps->compiledModuleCandidates) { + for (auto &candidate: swiftTextualDeps->compiledModuleCandidates) { out.indent(6 * 2); out << "\"" << candidate << "\""; - if (&candidate != &swiftDeps->compiledModuleCandidates.back()) + if (&candidate != &swiftTextualDeps->compiledModuleCandidates.back()) out << ","; out << "\n"; } out.indent(5 * 2); out << "],\n"; - } else if (!swiftDeps->compiledModulePath.empty()) { - writeJSONSingleField( - out, "compiledModulePath", - swiftDeps->compiledModulePath, 5, - /*trailingComma=*/false); } writeJSONSingleField( out, "isFramework", - swiftDeps->isFramework, 5, - /*trailingComma=*/!swiftDeps->extraPCMArgs.empty() || - swiftDeps->bridgingHeaderFile.hasValue()); - if (!swiftDeps->extraPCMArgs.empty()) { + swiftTextualDeps->isFramework, 5, + /*trailingComma=*/!swiftTextualDeps->extraPCMArgs.empty() || + swiftTextualDeps->bridgingHeaderFile.hasValue()); + if (!swiftTextualDeps->extraPCMArgs.empty()) { out.indent(5 * 2); out << "\"extraPcmArgs\": [\n"; - for (auto &arg : swiftDeps->extraPCMArgs) { + for (auto &arg : swiftTextualDeps->extraPCMArgs) { out.indent(6 * 2); out << "\"" << arg << "\""; - if (&arg != &swiftDeps->extraPCMArgs.back()) + if (&arg != &swiftTextualDeps->extraPCMArgs.back()) out << ","; out << "\n"; } out.indent(5 * 2); - out << (swiftDeps->bridgingHeaderFile.hasValue() ? "],\n" : "]\n"); + out << (swiftTextualDeps->bridgingHeaderFile.hasValue() ? "],\n" : "]\n"); } /// Bridging header and its source file dependencies, if any. - if (swiftDeps->bridgingHeaderFile) { + if (swiftTextualDeps->bridgingHeaderFile) { out.indent(5 * 2); out << "\"bridgingHeader\": {\n"; - writeJSONSingleField(out, "path", *swiftDeps->bridgingHeaderFile, 6, + writeJSONSingleField(out, "path", *swiftTextualDeps->bridgingHeaderFile, 6, /*trailingComma=*/true); - writeJSONSingleField(out, "sourceFiles", swiftDeps->bridgingSourceFiles, + writeJSONSingleField(out, "sourceFiles", swiftTextualDeps->bridgingSourceFiles, 6, /*trailingComma=*/true); writeJSONSingleField(out, "moduleDependencies", - swiftDeps->bridgingModuleDependencies, 6, + swiftTextualDeps->bridgingModuleDependencies, 6, /*trailingComma=*/false); out.indent(5 * 2); out << "}\n"; } - } else if (externalSwiftDep) { + } else if (swiftPlaceholderDeps) { out << "\"swiftPlaceholder\": {\n"; // Module doc file - if (externalSwiftDep->moduleDocPath != "") + if (swiftPlaceholderDeps->moduleDocPath != "") writeJSONSingleField(out, "moduleDocPath", - externalSwiftDep->moduleDocPath, + swiftPlaceholderDeps->moduleDocPath, /*indentLevel=*/5, /*trailingComma=*/true); // Module Source Info file - if (externalSwiftDep->moduleDocPath != "") + if (swiftPlaceholderDeps->moduleDocPath != "") writeJSONSingleField(out, "moduleSourceInfoPath", - externalSwiftDep->sourceInfoPath, + swiftPlaceholderDeps->sourceInfoPath, + /*indentLevel=*/5, + /*trailingComma=*/false); + } else if (swiftBinaryDeps) { + out << "\"swiftPrebuiltExternal\": {\n"; + assert(swiftBinaryDeps->compiledModulePath != "" && + "Expected .swiftmodule for a Binary Swift Module Dependency."); + writeJSONSingleField(out, "compiledModulePath", + swiftBinaryDeps->compiledModulePath, + /*indentLevel=*/5, + /*trailingComma=*/true); + // Module doc file + if (swiftBinaryDeps->moduleDocPath != "") + writeJSONSingleField(out, "moduleDocPath", + swiftBinaryDeps->moduleDocPath, /*indentLevel=*/5, /*trailingComma=*/true); + + // Module Source Info file + if (swiftBinaryDeps->moduleDocPath != "") + writeJSONSingleField(out, "moduleSourceInfoPath", + swiftBinaryDeps->sourceInfoPath, + /*indentLevel=*/5, + /*trailingComma=*/false); } else { out << "\"clang\": {\n"; @@ -583,6 +613,7 @@ static void writeJSON(llvm::raw_ostream &out, out << ","; out << "\n"; } + } } static bool diagnoseCycle(CompilerInstance &instance, @@ -611,12 +642,14 @@ static bool diagnoseCycle(CompilerInstance &instance, llvm::SmallString<64> buffer; for (auto it = startIt; it != openSet.end(); ++ it) { buffer.append(it->first); - buffer.append(it->second == ModuleDependenciesKind::Swift? + buffer.append((it->second == ModuleDependenciesKind::SwiftTextual || + it->second == ModuleDependenciesKind::SwiftBinary)? ".swiftmodule": ".pcm"); buffer.append(" -> "); } buffer.append(startIt->first); - buffer.append(startIt->second == ModuleDependenciesKind::Swift? + buffer.append((startIt->second == ModuleDependenciesKind::SwiftTextual || + startIt->second == ModuleDependenciesKind::SwiftBinary)? ".swiftmodule": ".pcm"); instance.getASTContext().Diags.diagnose(SourceLoc(), diag::scanner_find_cycle, @@ -677,7 +710,7 @@ static bool scanModuleDependencies(CompilerInstance &instance, } // Add the main module. allModules.insert({moduleName.str(), isClang ? ModuleDependenciesKind::Clang: - ModuleDependenciesKind::Swift}); + ModuleDependenciesKind::SwiftTextual}); // Output module prescan. if (FEOpts.ImportPrescan) { @@ -859,8 +892,7 @@ bool swift::scanDependencies(CompilerInstance &instance) { // Create the module dependency cache. ModuleDependenciesCache cache; - cache.recordDependencies(mainModuleName, std::move(mainDependencies), - ModuleDependenciesKind::Swift); + cache.recordDependencies(mainModuleName, std::move(mainDependencies)); auto &ctx = instance.getASTContext(); auto ModuleCachePath = getModuleCachePathFromClang(ctx @@ -908,7 +940,7 @@ bool swift::scanDependencies(CompilerInstance &instance) { if (!deps) continue; - if (auto swiftDeps = deps->getAsSwiftModule()) { + if (auto swiftDeps = deps->getAsSwiftTextualModule()) { if (auto swiftInterfaceFile = swiftDeps->swiftInterfaceFile) depTracker->addDependency(*swiftInterfaceFile, /*IsSystem=*/false); for (const auto &sourceFile : swiftDeps->sourceFiles) diff --git a/lib/Serialization/ModuleDependencyScanner.cpp b/lib/Serialization/ModuleDependencyScanner.cpp index 600744840648e..68847f67a4ebf 100644 --- a/lib/Serialization/ModuleDependencyScanner.cpp +++ b/lib/Serialization/ModuleDependencyScanner.cpp @@ -166,10 +166,13 @@ Optional SerializedModuleLoaderBase::getModuleDependencies( InterfaceSubContextDelegate &delegate) { // Check whether we've cached this result. if (auto found = cache.findDependencies( - moduleName, ModuleDependenciesKind::Swift)) + moduleName, ModuleDependenciesKind::SwiftTextual)) return found; - if (auto found = - cache.findDependencies(moduleName, ModuleDependenciesKind::SwiftPlaceholder)) + if (auto found = cache.findDependencies( + moduleName, ModuleDependenciesKind::SwiftBinary)) + return found; + if (auto found = cache.findDependencies( + moduleName, ModuleDependenciesKind::SwiftPlaceholder)) return found; auto moduleId = Ctx.getIdentifier(moduleName); @@ -191,8 +194,7 @@ Optional SerializedModuleLoaderBase::getModuleDependencies( for (auto &scanner : scanners) { if (scanner->canImportModule({moduleId, SourceLoc()})) { // Record the dependencies. - cache.recordDependencies(moduleName, *(scanner->dependencies), - scanner->dependencyKind); + cache.recordDependencies(moduleName, *(scanner->dependencies)); return std::move(scanner->dependencies); } } diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index 98e499b6aebba..0fec188d89b73 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -394,8 +394,13 @@ llvm::ErrorOr SerializedModuleLoaderBase::scanModuleFile( nullptr, isFramework, loadedModuleFile); + const std::string moduleDocPath; + const std::string sourceInfoPath; // Map the set of dependencies over to the "module dependencies". - auto dependencies = ModuleDependencies::forSwiftModule(modulePath.str(), isFramework); + auto dependencies = ModuleDependencies::forSwiftBinaryModule(modulePath.str(), + moduleDocPath, + sourceInfoPath, + isFramework); llvm::StringSet<> addedModuleNames; for (const auto &dependency : loadedModuleFile->getDependencies()) { // FIXME: Record header dependency? diff --git a/test/ScanDependencies/Inputs/ModuleDependencyGraph.swift b/test/ScanDependencies/Inputs/ModuleDependencyGraph.swift index 4c84230cd4efb..4ac30644f59e8 100644 --- a/test/ScanDependencies/Inputs/ModuleDependencyGraph.swift +++ b/test/ScanDependencies/Inputs/ModuleDependencyGraph.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -14,12 +14,14 @@ import Foundation enum ModuleDependencyId: Hashable { case swift(String) case swiftPlaceholder(String) + case swiftPrebuiltExternal(String) case clang(String) var moduleName: String { switch self { case .swift(let name): return name case .swiftPlaceholder(let name): return name + case .swiftPrebuiltExternal(let name): return name case .clang(let name): return name } } @@ -29,6 +31,7 @@ extension ModuleDependencyId: Codable { enum CodingKeys: CodingKey { case swift case swiftPlaceholder + case swiftPrebuiltExternal case clang } @@ -42,8 +45,13 @@ extension ModuleDependencyId: Codable { let moduleName = try container.decode(String.self, forKey: .swiftPlaceholder) self = .swiftPlaceholder(moduleName) } catch { - let moduleName = try container.decode(String.self, forKey: .clang) - self = .clang(moduleName) + do { + let moduleName = try container.decode(String.self, forKey: .swiftPrebuiltExternal) + self = .swiftPrebuiltExternal(moduleName) + } catch { + let moduleName = try container.decode(String.self, forKey: .clang) + self = .clang(moduleName) + } } } } @@ -54,7 +62,9 @@ extension ModuleDependencyId: Codable { case .swift(let moduleName): try container.encode(moduleName, forKey: .swift) case .swiftPlaceholder(let moduleName): - try container.encode(moduleName, forKey: .swift) + try container.encode(moduleName, forKey: .swiftPlaceholder) + case .swiftPrebuiltExternal(let moduleName): + try container.encode(moduleName, forKey: .swiftPrebuiltExternal) case .clang(let moduleName): try container.encode(moduleName, forKey: .clang) } @@ -76,27 +86,39 @@ struct SwiftModuleDetails: Codable { /// The paths of potentially ready-to-use compiled modules for the interface. var compiledModuleCandidates: [String]? - /// The compiled Swift module to use. - var compiledModulePath: String? - /// The bridging header, if any. - var bridgingHeader: BridgingHeader? + var bridgingHeaderPath: String? - /// The Swift command line arguments that need to be passed through - /// to the -compile-module-from-interface action to build this module. - var commandLine: [String]? + /// The source files referenced by the bridging header. + var bridgingSourceFiles: [String]? = [] + + /// Options to the compile command + var commandLine: [String]? = [] /// To build a PCM to be used by this Swift module, we need to append these /// arguments to the generic PCM build arguments reported from the dependency /// graph. - var extraPcmArgs: [String]? + var extraPcmArgs: [String] /// A flag to indicate whether or not this module is a framework. var isFramework: Bool } -/// Details specific to Swift external modules. -struct swiftPlaceholderModuleDetails: Codable { +/// Details specific to Swift placeholder dependencies. +struct SwiftPlaceholderModuleDetails: Codable { + /// The path to the .swiftModuleDoc file. + var moduleDocPath: String? + + /// The path to the .swiftSourceInfo file. + var moduleSourceInfoPath: String? +} + +/// Details specific to Swift externally-pre-built modules. +struct SwiftPrebuiltExternalModuleDetails: Codable { + /// The path to the already-compiled module that must be used instead of + /// generating a job to build this module. + var compiledModulePath: String + /// The path to the .swiftModuleDoc file. var moduleDocPath: String? @@ -107,25 +129,28 @@ struct swiftPlaceholderModuleDetails: Codable { /// Details specific to Clang modules. struct ClangModuleDetails: Codable { /// The path to the module map used to build this module. - var moduleMapPath: String + public var moduleMapPath: String + + /// Set of PCM Arguments of depending modules which + /// are covered by the directDependencies info of this module + public var dependenciesCapturedPCMArgs: Set<[String]>? - /// The context hash used to discriminate this module file. + /// clang-generated context hash var contextHash: String - /// The Clang command line arguments that need to be passed through - /// to the -emit-pcm action to build this module. + /// Options to the compile command var commandLine: [String] = [] } -struct ModuleDependencies: Codable { +struct ModuleInfo: Codable { /// The path for the module. var modulePath: String /// The source files used to build this module. - var sourceFiles: [String]? = [] + var sourceFiles: [String]? /// The set of direct module dependencies of this module. - var directDependencies: [ModuleDependencyId]? = [] + var directDependencies: [ModuleDependencyId]? /// Specific details of a particular kind of module. var details: Details @@ -136,19 +161,23 @@ struct ModuleDependencies: Codable { /// a bridging header. case swift(SwiftModuleDetails) - /// Swift external modules carry additional details that specify their + /// Swift placeholder modules carry additional details that specify their /// module doc path and source info paths. - case swiftPlaceholder(swiftPlaceholderModuleDetails) + case swiftPlaceholder(SwiftPlaceholderModuleDetails) + + /// Swift externally-prebuilt modules must communicate the path to pre-built binary artifacts + case swiftPrebuiltExternal(SwiftPrebuiltExternalModuleDetails) /// Clang modules are built from a module map file. case clang(ClangModuleDetails) } } -extension ModuleDependencies.Details: Codable { +extension ModuleInfo.Details: Codable { enum CodingKeys: CodingKey { case swift case swiftPlaceholder + case swiftPrebuiltExternal case clang } @@ -159,11 +188,18 @@ extension ModuleDependencies.Details: Codable { self = .swift(details) } catch { do { - let details = try container.decode(swiftPlaceholderModuleDetails.self, forKey: .swiftPlaceholder) + let details = try container.decode(SwiftPlaceholderModuleDetails.self, + forKey: .swiftPlaceholder) self = .swiftPlaceholder(details) } catch { - let details = try container.decode(ClangModuleDetails.self, forKey: .clang) - self = .clang(details) + do { + let details = try container.decode(SwiftPrebuiltExternalModuleDetails.self, + forKey: .swiftPrebuiltExternal) + self = .swiftPrebuiltExternal(details) + } catch { + let details = try container.decode(ClangModuleDetails.self, forKey: .clang) + self = .clang(details) + } } } } @@ -175,6 +211,8 @@ extension ModuleDependencies.Details: Codable { try container.encode(details, forKey: .swift) case .swiftPlaceholder(let details): try container.encode(details, forKey: .swiftPlaceholder) + case .swiftPrebuiltExternal(let details): + try container.encode(details, forKey: .swiftPrebuiltExternal) case .clang(let details): try container.encode(details, forKey: .clang) } @@ -188,8 +226,8 @@ struct ModuleDependencyGraph: Codable { var mainModuleName: String /// The complete set of modules discovered - var modules: [ModuleDependencyId: ModuleDependencies] = [:] + var modules: [ModuleDependencyId: ModuleInfo] = [:] /// Information about the main module. - var mainModule: ModuleDependencies { modules[.swift(mainModuleName)]! } + var mainModule: ModuleInfo { modules[.swift(mainModuleName)]! } } diff --git a/test/ScanDependencies/binary_module_only.swift b/test/ScanDependencies/binary_module_only.swift new file mode 100644 index 0000000000000..488c95cc526ed --- /dev/null +++ b/test/ScanDependencies/binary_module_only.swift @@ -0,0 +1,25 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/clang-module-cache) +// RUN: %empty-directory(%t/Foo.swiftmodule) +// RUN: %empty-directory(%t/binaryModuleOnly) +// RUN: echo "public func foo() {}" > %t/Foo.swift +// REQUIRES: executable_test +// REQUIRES: objc_interop + +import Foo + +// BINARY_MODULE_ONLY: "swiftPrebuiltExternal": "Foo" +// BINARY_MODULE_ONLY: "swiftPrebuiltExternal": { +// BINARY_MODULE_ONLY-NEXT: "compiledModulePath": "BUILD_DIR/{{.*}}/ScanDependencies/Output/binary_module_only.swift.tmp/binaryModuleOnly/Foo.swiftmodule", + +// HAS_NO_COMPILED-NOT: "{{.*}}Foo.swiftmodule{{.*}}.swiftmodule" + +// Step 1: build swift interface and swift module side by side +// RUN: %target-swift-frontend -emit-module %t/Foo.swift -emit-module-path %t/Foo.swiftmodule/%target-swiftmodule-name -module-name Foo -emit-module-interface-path %t/Foo.swiftmodule/%target-swiftinterface-name + +// Step 2: build module from interface and put it in a location separate from the interface file +// RUN: %target-swift-frontend -compile-module-from-interface -o %t/binaryModuleOnly/Foo.swiftmodule -module-name Foo -disable-interface-lock %t/Foo.swiftmodule/%target-swiftinterface-name + +// Step 3: scan dependencies, pointed only at the binary module file should detect it as a swiftBinaryModule kind of dependency +// RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -I %t/binaryModuleOnly -emit-dependencies -emit-dependencies-path %t/deps.d -sdk %t -prebuilt-module-cache-path %t/ResourceDir/%target-sdk-name/prebuilt-modules +// RUN: %FileCheck %s -check-prefix=BINARY_MODULE_ONLY < %t/deps.json diff --git a/test/ScanDependencies/module_deps_external.swift b/test/ScanDependencies/module_deps_external.swift index 4242028b6d114..ac72abbc37a9c 100644 --- a/test/ScanDependencies/module_deps_external.swift +++ b/test/ScanDependencies/module_deps_external.swift @@ -76,7 +76,7 @@ import SomeExternalModule // CHECK-NEXT: "details": { // CHECK-NEXT: "swiftPlaceholder": { // CHECK-NEXT: "moduleDocPath": "BUILD_DIR/{{.*}}/ScanDependencies/Output/module_deps_external.swift.tmp/inputs/SomeExternalModule.swiftdoc", -// CHECK-NEXT: "moduleSourceInfoPath": "BUILD_DIR/{{.*}}/ScanDependencies/Output/module_deps_external.swift.tmp/inputs/SomeExternalModule.swiftsourceinfo", +// CHECK-NEXT: "moduleSourceInfoPath": "BUILD_DIR/{{.*}}/ScanDependencies/Output/module_deps_external.swift.tmp/inputs/SomeExternalModule.swiftsourceinfo" /// --------Swift module Swift // CHECK-LABEL: "modulePath": "Swift.swiftmodule", From 6fce6d9363dee7c3675f94635453a46507e26821 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Mon, 12 Oct 2020 10:43:12 -0700 Subject: [PATCH 417/745] [Async CC] Pull poly params from entry point emission. Previously, EmitPolymorphicParameters dealt directly with an Explosion from which it pulled values. In one place, there was a conditional check for async which handled some cases. There was however another place where the polymorphic parameter was pulled directly from the explosion. That missed case resulted in attempting to pull a polymorphic parameter directly from an Explosion which contains only a %swift.context* per the async calling convention. Here, those parameters are now pulled from an EntryPointArgumentEmission subclasses of which are able to provide the relevant definition of what pulling a parameter means. rdar://problem/70144083 --- lib/IRGen/EntryPointArgumentEmission.h | 4 ++ lib/IRGen/GenProto.cpp | 60 +++++------------ lib/IRGen/GenProto.h | 8 +-- lib/IRGen/IRGenSIL.cpp | 64 ++++++++++++++++--- .../rdar70144083.swift | 37 +++++++++++ 5 files changed, 116 insertions(+), 57 deletions(-) create mode 100644 validation-test/compiler_crashers_2_fixed/rdar70144083.swift diff --git a/lib/IRGen/EntryPointArgumentEmission.h b/lib/IRGen/EntryPointArgumentEmission.h index 5358d24d233e1..f41961ebfb4fb 100644 --- a/lib/IRGen/EntryPointArgumentEmission.h +++ b/lib/IRGen/EntryPointArgumentEmission.h @@ -20,6 +20,7 @@ namespace swift { namespace irgen { class Explosion; +struct GenericRequirement; class EntryPointArgumentEmission { @@ -28,6 +29,9 @@ class EntryPointArgumentEmission { virtual bool requiresIndirectResult(SILType retType) = 0; virtual llvm::Value *getIndirectResultForFormallyDirectResult() = 0; virtual llvm::Value *getIndirectResult(unsigned index) = 0; + virtual llvm::Value *getNextPolymorphicParameterAsMetadata() = 0; + virtual llvm::Value * + getNextPolymorphicParameter(GenericRequirement &requirement) = 0; }; class NativeCCEntryPointArgumentEmission diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 96e387eecccf7..9fad29f9fd92a 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -489,7 +489,8 @@ class EmitPolymorphicParameters : public PolymorphicConvention { public: EmitPolymorphicParameters(IRGenFunction &IGF, SILFunction &Fn); - void emit(Explosion &in, WitnessMetadata *witnessMetadata, + void emit(EntryPointArgumentEmission &emission, + WitnessMetadata *witnessMetadata, const GetParameterFn &getParameter); private: @@ -499,7 +500,8 @@ class EmitPolymorphicParameters : public PolymorphicConvention { /// Fulfill local type data from any extra information associated with /// the given source. - void bindExtraSource(const MetadataSource &source, Explosion &in, + void bindExtraSource(const MetadataSource &source, + EntryPointArgumentEmission &emission, WitnessMetadata *witnessMetadata); void bindParameterSources(const GetParameterFn &getParameter); @@ -528,9 +530,9 @@ CanType EmitPolymorphicParameters::getArgTypeInContext(unsigned paramIndex) cons IGM.getSILModule(), FnType, IGM.getMaximalTypeExpansionContext())); } -void EmitPolymorphicParameters::bindExtraSource(const MetadataSource &source, - Explosion &in, - WitnessMetadata *witnessMetadata) { +void EmitPolymorphicParameters::bindExtraSource( + const MetadataSource &source, EntryPointArgumentEmission &emission, + WitnessMetadata *witnessMetadata) { switch (source.getKind()) { case MetadataSource::Kind::Metadata: case MetadataSource::Kind::ClassPointer: @@ -540,7 +542,7 @@ void EmitPolymorphicParameters::bindExtraSource(const MetadataSource &source, case MetadataSource::Kind::GenericLValueMetadata: { CanType argTy = getArgTypeInContext(source.getParamIndex()); - llvm::Value *metadata = in.claimNext(); + llvm::Value *metadata = emission.getNextPolymorphicParameterAsMetadata(); setTypeMetadataName(IGF.IGM, metadata, argTy); IGF.bindLocalTypeDataFromTypeMetadata(argTy, IsExact, metadata, @@ -2223,55 +2225,23 @@ bool irgen::hasPolymorphicParameters(CanSILFunctionType ty) { } /// Emit a polymorphic parameters clause, binding all the metadata necessary. -void EmitPolymorphicParameters::emit(Explosion &in, +void EmitPolymorphicParameters::emit(EntryPointArgumentEmission &emission, WitnessMetadata *witnessMetadata, const GetParameterFn &getParameter) { // Collect any early sources and bind local type data from them. for (auto &source : getSources()) { - bindExtraSource(source, in, witnessMetadata); + bindExtraSource(source, emission, witnessMetadata); } auto getInContext = [&](CanType type) -> CanType { return getTypeInContext(type); }; - unsigned index = 0; // Collect any concrete type metadata that's been passed separately. enumerateUnfulfilledRequirements([&](GenericRequirement requirement) { - llvm::Value *value; - if (Fn.isAsync()) { - auto *context = in.peek( - /*the index of the swift.context in the async function CC*/ 0); - auto layout = getAsyncContextLayout(IGF, &Fn); - Address dataAddr = layout.emitCastTo(IGF, context); - assert(layout.hasBindings()); - - auto bindingLayout = layout.getBindingsLayout(); - auto bindingsAddr = - bindingLayout.project(IGF, dataAddr, /*offsets*/ None); - auto erasedBindingsAddr = - IGF.Builder.CreateBitCast(bindingsAddr, IGF.IGM.Int8PtrPtrTy); - auto uncastBindingAddr = IGF.Builder.CreateConstArrayGEP( - erasedBindingsAddr, index, IGF.IGM.getPointerSize()); - if (requirement.Protocol) { - auto bindingAddrAddr = IGF.Builder.CreateBitCast( - uncastBindingAddr.getAddress(), IGF.IGM.WitnessTablePtrPtrTy); - auto bindingAddr = IGF.Builder.CreateLoad( - bindingAddrAddr, IGF.IGM.getPointerAlignment()); - value = bindingAddr; - } else { - auto bindingAddrAddr = IGF.Builder.CreateBitCast( - uncastBindingAddr.getAddress(), IGF.IGM.TypeMetadataPtrPtrTy); - auto bindingAddr = IGF.Builder.CreateLoad( - bindingAddrAddr, IGF.IGM.getPointerAlignment()); - value = bindingAddr; - } - } else { - value = in.claimNext(); - } + llvm::Value *value = emission.getNextPolymorphicParameter(requirement); bindGenericRequirement(IGF, requirement, value, MetadataState::Complete, getInContext); - ++index; }); // Bind all the fulfillments we can from the formal parameters. @@ -2689,12 +2659,12 @@ void irgen::collectTrailingWitnessMetadata( } /// Perform all the bindings necessary to emit the given declaration. -void irgen::emitPolymorphicParameters(IRGenFunction &IGF, - SILFunction &Fn, - Explosion &in, +void irgen::emitPolymorphicParameters(IRGenFunction &IGF, SILFunction &Fn, + EntryPointArgumentEmission &emission, WitnessMetadata *witnessMetadata, const GetParameterFn &getParameter) { - EmitPolymorphicParameters(IGF, Fn).emit(in, witnessMetadata, getParameter); + EmitPolymorphicParameters(IGF, Fn).emit(emission, witnessMetadata, + getParameter); } /// Given an array of polymorphic arguments as might be set up by diff --git a/lib/IRGen/GenProto.h b/lib/IRGen/GenProto.h index 0cd6b594ebe33..dada1eee4310e 100644 --- a/lib/IRGen/GenProto.h +++ b/lib/IRGen/GenProto.h @@ -40,6 +40,7 @@ namespace swift { namespace irgen { class Address; class DynamicMetadataRequest; + class EntryPointArgumentEmission; class Explosion; class FunctionPointer; class IRGenFunction; @@ -133,12 +134,11 @@ namespace irgen { /// /// \param witnessMetadata - can be omitted if the function is /// definitely not a witness method - void emitPolymorphicParameters(IRGenFunction &IGF, - SILFunction &Fn, - Explosion &args, + void emitPolymorphicParameters(IRGenFunction &IGF, SILFunction &Fn, + EntryPointArgumentEmission &emission, WitnessMetadata *witnessMetadata, const GetParameterFn &getParameter); - + void emitPolymorphicParametersFromArray(IRGenFunction &IGF, NominalTypeDecl *typeDecl, Address array, diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index a4c4544e1050f..542f6c0ac5e17 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1147,6 +1147,13 @@ class SyncEntryPointArgumentEmission llvm::Value *getIndirectResult(unsigned index) override { return allParamValues.claimNext(); }; + llvm::Value * + getNextPolymorphicParameter(GenericRequirement &requirement) override { + return allParamValues.claimNext(); + } + llvm::Value *getNextPolymorphicParameterAsMetadata() override { + return allParamValues.claimNext(); + } }; class AsyncEntryPointArgumentEmission : public virtual EntryPointArgumentEmission { @@ -1206,6 +1213,7 @@ class AsyncNativeCCEntryPointArgumentEmission final llvm::Value *context; /*const*/ AsyncContextLayout layout; const Address dataAddr; + unsigned polymorphicParameterIndex = 0; llvm::Value *loadValue(ElementLayout layout) { Address addr = layout.project(IGF, dataAddr, /*offsets*/ llvm::None); @@ -1248,6 +1256,46 @@ class AsyncNativeCCEntryPointArgumentEmission final "into indirect IR results because all results are already " "indirected through the context"); } + Address getNextUncastBinding() { + auto index = polymorphicParameterIndex; + ++polymorphicParameterIndex; + + assert(layout.hasBindings()); + + auto bindingLayout = layout.getBindingsLayout(); + auto bindingsAddr = bindingLayout.project(IGF, dataAddr, /*offsets*/ None); + auto erasedBindingsAddr = + IGF.Builder.CreateBitCast(bindingsAddr, IGF.IGM.Int8PtrPtrTy); + auto uncastBindingAddr = IGF.Builder.CreateConstArrayGEP( + erasedBindingsAddr, index, IGF.IGM.getPointerSize()); + return uncastBindingAddr; + } + llvm::Value *castUncastBindingToMetadata(Address uncastBindingAddr) { + auto bindingAddrAddr = IGF.Builder.CreateBitCast( + uncastBindingAddr.getAddress(), IGF.IGM.TypeMetadataPtrPtrTy); + auto bindingAddr = + IGF.Builder.CreateLoad(bindingAddrAddr, IGF.IGM.getPointerAlignment()); + return bindingAddr; + } + llvm::Value *castUncastBindingToWitnessTable(Address uncastBindingAddr) { + auto bindingAddrAddr = IGF.Builder.CreateBitCast( + uncastBindingAddr.getAddress(), IGF.IGM.WitnessTablePtrPtrTy); + auto bindingAddr = + IGF.Builder.CreateLoad(bindingAddrAddr, IGF.IGM.getPointerAlignment()); + return bindingAddr; + } + llvm::Value * + getNextPolymorphicParameter(GenericRequirement &requirement) override { + auto uncastBindingAddr = getNextUncastBinding(); + if (requirement.Protocol) { + return castUncastBindingToWitnessTable(uncastBindingAddr); + } else { + return castUncastBindingToMetadata(uncastBindingAddr); + } + } + llvm::Value *getNextPolymorphicParameterAsMetadata() override { + return castUncastBindingToMetadata(getNextUncastBinding()); + } llvm::Value *getIndirectResult(unsigned index) override { auto fieldLayout = layout.getIndirectReturnLayout(index); return loadValue(fieldLayout); @@ -1675,13 +1723,13 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, // Bind polymorphic arguments. This can only be done after binding // all the value parameters. if (hasPolymorphicParameters(funcTy)) { - emitPolymorphicParameters(IGF, *IGF.CurSILFn, allParamValues, - &witnessMetadata, - [&](unsigned paramIndex) -> llvm::Value* { - SILValue parameter = - IGF.CurSILFn->getArgumentsWithoutIndirectResults()[paramIndex]; - return IGF.getLoweredSingletonExplosion(parameter); - }); + emitPolymorphicParameters( + IGF, *IGF.CurSILFn, *emission, &witnessMetadata, + [&](unsigned paramIndex) -> llvm::Value * { + SILValue parameter = + IGF.CurSILFn->getArgumentsWithoutIndirectResults()[paramIndex]; + return IGF.getLoweredSingletonExplosion(parameter); + }); } assert(allParamValues.empty() && "didn't claim all parameters!"); @@ -1776,7 +1824,7 @@ static void emitEntryPointArgumentsCOrObjC(IRGenSILFunction &IGF, // all the value parameters, and must be done even for non-polymorphic // functions because of imported Objective-C generics. emitPolymorphicParameters( - IGF, *IGF.CurSILFn, params, nullptr, + IGF, *IGF.CurSILFn, *emission, nullptr, [&](unsigned paramIndex) -> llvm::Value * { SILValue parameter = entry->getArguments()[paramIndex]; return IGF.getLoweredSingletonExplosion(parameter); diff --git a/validation-test/compiler_crashers_2_fixed/rdar70144083.swift b/validation-test/compiler_crashers_2_fixed/rdar70144083.swift new file mode 100644 index 0000000000000..39566bfbe31a6 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar70144083.swift @@ -0,0 +1,37 @@ +// RUN: %target-swift-frontend -emit-ir %s -enable-experimental-concurrency + +public protocol AsyncIteratorProtocol { + associatedtype Element + associatedtype Failure: Error + + mutating func nextResult() async -> Result? + mutating func cancel() +} + +public protocol AsyncSequence { + associatedtype Element + associatedtype Failure: Error + associatedtype AsyncIterator: AsyncIteratorProtocol where AsyncIterator.Element == Element, AsyncIterator.Failure == Failure + + func makeAsyncIterator() -> AsyncIterator +} + +struct Just: AsyncSequence { + typealias Failure = Never + + struct AsyncIterator: AsyncIteratorProtocol { + var value: Element? + mutating func nextResult() async -> Result? { + defer { value = nil } + return value.map { .success($0) } + } + + mutating func cancel() { + value = nil + } + } + var value: Element + func makeAsyncIterator() -> AsyncIterator { + return AsyncIterator(value: value) + } +} From ac116cf56a512981939654babc80914ff63b82cf Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 12 Oct 2020 16:05:22 -0400 Subject: [PATCH 418/745] Parse: Remove AlreadyHandledDecls set --- include/swift/Parse/Parser.h | 11 ----------- lib/Parse/ParseDecl.cpp | 13 ++++++------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 224e5db97a524..e5aa39da81a62 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -943,17 +943,6 @@ class Parser { void consumeDecl(ParserPosition BeginParserPosition, ParseDeclOptions Flags, bool IsTopLevel); - /// FIXME: Remove this, it's vestigial. - llvm::SmallPtrSet AlreadyHandledDecls; - - void markWasHandled(Decl *D) { - AlreadyHandledDecls.insert(D); - } - - bool declWasHandledAlready(Decl *D) { - return AlreadyHandledDecls.erase(D); - } - ParserResult parseDecl(ParseDeclOptions Flags, bool IsAtStartOfLineOrPreviousHadSemi, llvm::function_ref Handler); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 637d2d5deb2ae..b9d63f08a7e64 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3849,6 +3849,8 @@ Parser::parseDecl(ParseDeclOptions Flags, auto OrigTok = Tok; bool MayNeedOverrideCompletion = false; + bool HandlerAlreadyCalled = false; + auto parseLetOrVar = [&](bool HasLetOrVarKeyword) { // Collect all modifiers into a modifier list. DeclParsingContext.setCreateSyntax(SyntaxKind::VariableDecl); @@ -3861,8 +3863,7 @@ Parser::parseDecl(ParseDeclOptions Flags, && isCodeCompletionFirstPass()) return; std::for_each(Entries.begin(), Entries.end(), Handler); - if (auto *D = DeclResult.getPtrOrNull()) - markWasHandled(D); + HandlerAlreadyCalled = true; }; auto parseFunc = [&](bool HasFuncKeyword) { @@ -3909,8 +3910,7 @@ Parser::parseDecl(ParseDeclOptions Flags, isCodeCompletionFirstPass()) break; std::for_each(Entries.begin(), Entries.end(), Handler); - if (auto *D = DeclResult.getPtrOrNull()) - markWasHandled(D); + HandlerAlreadyCalled = true; break; } case tok::kw_class: @@ -3955,8 +3955,7 @@ Parser::parseDecl(ParseDeclOptions Flags, break; std::for_each(Entries.begin(), Entries.end(), Handler); MayNeedOverrideCompletion = true; - if (auto *D = DeclResult.getPtrOrNull()) - markWasHandled(D); + HandlerAlreadyCalled = true; break; } @@ -4136,7 +4135,7 @@ Parser::parseDecl(ParseDeclOptions Flags, if (DeclResult.isNonNull()) { Decl *D = DeclResult.get(); - if (!declWasHandledAlready(D)) + if (!HandlerAlreadyCalled) Handler(D); setOriginalDeclarationForDifferentiableAttributes(D->getAttrs(), D); } From 0e05b51988366164d1b6d4825dcb2f9499bfaa03 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 12 Oct 2020 11:09:17 -0700 Subject: [PATCH 419/745] When converting load [copy] -> load_borrow, do not insert end_borrow if the block insert point is a dead end block. This is important to do since otherwise, we may be implicitly reducing the lifetime of a value which we can not do yet since we do not require all interior pointer instructions to be guarded by borrows (yet). Once that constraint is in place, we will not have this problem. Consider a situation where one has a @owned switch_enum on an indirect box case which is post-dominated by an unreachable that we want to convert to @guaranteed: enum MyEnum { indirect case FirstCase(Int) ... } bb0(%in_guaranteed_addr : $*MyEnum): ... %0 = load [copy] %in_guaranteed_addr : $*MyEnum switch_enum %0 : $MyEnum, case #MyEnum.FirstCase: bb1, ... bb1(%1 : @owned ${ var Int }): %2 = project_box %1 : ${ var Int }, 0 %3 = load [trivial] %2 : $*Int apply %log(%3) : $@convention(thin) (Int) -> () unreachable In this case, we will not have a destroy_value on the box, but we may have a project_box on the box. This is ok since we are going to leak the value. But since we are using all consuming uses to determine the lifetime, we will want to insert an end_borrow at the head of the switch_enum dest block like follows: bb0(%in_guaranteed_addr : $*MyEnum): ... %0 = load_borrow %in_guaranteed_addr : $*MyEnum switch_enum %0 : $MyEnum, case #MyEnum.FirstCase: bb1, ... bb1(%1 : @guaranteed ${ var Int }): end_borrow %1 : ${ var Int } %2 = project_box %1 : ${ var Int }, 0 %3 = load [trivial] %2 : $*Int apply %log(%3) : $@convention(thin) (Int) -> () unreachable which would violate ownership invariants. Instead, we need to realize that %1 is dominated by a dead end block so we may not have a destroy_value upon it meaning we should just not insert the end_borrow here. If we have a destroy_value upon it (since we did not get rid of a destroy_value), then we will still get rid of the destroy_value if we are going to optimize this, so we are still correct. rdar://68096662 --- .../SemanticARC/OwnershipLiveRange.cpp | 57 +++++++++++++ test/SILOptimizer/semantic-arc-opts.sil | 79 +++++++++++++++++++ 2 files changed, 136 insertions(+) diff --git a/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp b/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp index 187b28e68c27b..e7068997d289b 100644 --- a/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp +++ b/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp @@ -13,6 +13,8 @@ #include "OwnershipLiveRange.h" #include "OwnershipPhiOperand.h" +#include "swift/SIL/BasicBlockUtils.h" + using namespace swift; using namespace swift::semanticarc; @@ -162,6 +164,61 @@ void OwnershipLiveRange::insertEndBorrowsAtDestroys( auto loc = RegularLocation::getAutoGeneratedLocation(); while (!scratch.empty()) { auto *insertPoint = scratch.pop_back_val(); + + // Do not insert end_borrow if the block insert point is a dead end block. + // + // DISCUSSION: This is important to do since otherwise, we may be implicitly + // reducing the lifetime of a value which we can not do yet since we do not + // require all interior pointer instructions to be guarded by borrows + // (yet). Once that constraint is in place, we will not have this problem. + // + // Consider a situation where one has a @owned switch_enum on an + // indirect box case which is post-dominated by an unreachable that we want + // to convert to @guaranteed: + // + // enum MyEnum { + // indirect case FirstCase(Int) + // ... + // } + // + // bb0(%in_guaranteed_addr : $*MyEnum): + // ... + // %0 = load [copy] %in_guaranteed_addr : $*MyEnum + // switch_enum %0 : $MyEnum, case #MyEnum.FirstCase: bb1, ... + // + // bb1(%1 : @owned ${ var Int }): + // %2 = project_box %1 : ${ var Int }, 0 + // %3 = load [trivial] %2 : $*Int + // apply %log(%3) : $@convention(thin) (Int) -> () + // unreachable + // + // In this case, we will not have a destroy_value on the box, but we may + // have a project_box on the box. This is ok since we are going to leak the + // value. But since we are using all consuming uses to determine the + // lifetime, we will want to insert an end_borrow at the head of the + // switch_enum dest block like follows: + // + // bb0(%in_guaranteed_addr : $*MyEnum): + // ... + // %0 = load_borrow %in_guaranteed_addr : $*MyEnum + // switch_enum %0 : $MyEnum, case #MyEnum.FirstCase: bb1, ... + // + // bb1(%1 : @guaranteed ${ var Int }): + // end_borrow %1 : ${ var Int } + // %2 = project_box %1 : ${ var Int }, 0 + // %3 = load [trivial] %2 : $*Int + // apply %log(%3) : $@convention(thin) (Int) -> () + // unreachable + // + // which would violate ownership invariants. Instead, we need to realize + // that %1 is dominated by a dead end block so we may not have a + // destroy_value upon it meaning we should just not insert the end_borrow + // here. If we have a destroy_value upon it (since we did not get rid of a + // destroy_value), then we will still get rid of the destroy_value if we are + // going to optimize this, so we are still correct. + if (deadEndBlocks.isDeadEnd(insertPoint->getParent())) + continue; + SILBuilderWithScope builder(insertPoint); builder.createEndBorrow(loc, newGuaranteedValue); } diff --git a/test/SILOptimizer/semantic-arc-opts.sil b/test/SILOptimizer/semantic-arc-opts.sil index 1b79ff3e34775..6832c05e71cc1 100644 --- a/test/SILOptimizer/semantic-arc-opts.sil +++ b/test/SILOptimizer/semantic-arc-opts.sil @@ -79,6 +79,16 @@ class SubclassLet: ClassLet {} sil_global [let] @a_let_global : $Klass sil_global @a_var_global : $Klass +enum EnumWithIndirectCase { +case first +indirect case second(Builtin.NativeObject) +} + +struct StructWithEnumWithIndirectCaseField { + var i: Builtin.Int23 + var field : EnumWithIndirectCase +} + /////////// // Tests // /////////// @@ -2727,3 +2737,72 @@ bbEnd: return %9999 : $() } +// CHECK-LABEL: sil [ossa] @enum_with_indirect_case_projectbox_copyvalue_deadend : $@convention(thin) (@guaranteed StructWithEnumWithIndirectCaseField) -> () { +// CHECK-NOT: copy_value +// CHECK: } // end sil function 'enum_with_indirect_case_projectbox_copyvalue_deadend' +sil [ossa] @enum_with_indirect_case_projectbox_copyvalue_deadend : $@convention(thin) (@guaranteed StructWithEnumWithIndirectCaseField) -> () { +bb0(%0 : @guaranteed $StructWithEnumWithIndirectCaseField): + %1 = struct_extract %0 : $StructWithEnumWithIndirectCaseField, #StructWithEnumWithIndirectCaseField.field + %1a = copy_value %1 : $EnumWithIndirectCase + switch_enum %1a : $EnumWithIndirectCase, case #EnumWithIndirectCase.first!enumelt: bb1, case #EnumWithIndirectCase.second!enumelt: bb2 + +bb1: + %9999 = tuple() + return %9999 : $() + +// NOTE: Eventually this will need to be changed when project_box has to be +// guarded by begin_borrow. +bb2(%2 : @owned ${ var Builtin.NativeObject }): + %3 = project_box %2 : ${ var Builtin.NativeObject }, 0 + %4 = load [copy] %3 : $*Builtin.NativeObject + %user = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + apply %user(%4) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + unreachable +} + +// CHECK-LABEL: sil [ossa] @enum_with_indirect_case_projectbox_loadcopy_to_loadborrow_deadend : $@convention(thin) (@in_guaranteed EnumWithIndirectCase) -> () { +// CHECK: bb0 +// CHECK-NEXT: load_borrow +// CHECK: } // end sil function 'enum_with_indirect_case_projectbox_loadcopy_to_loadborrow_deadend' +sil [ossa] @enum_with_indirect_case_projectbox_loadcopy_to_loadborrow_deadend : $@convention(thin) (@in_guaranteed EnumWithIndirectCase) -> () { +bb0(%0 : $*EnumWithIndirectCase): + %1 = load [copy] %0 : $*EnumWithIndirectCase + switch_enum %1 : $EnumWithIndirectCase, case #EnumWithIndirectCase.first!enumelt: bb1, case #EnumWithIndirectCase.second!enumelt: bb2 + +bb1: + %9999 = tuple() + return %9999 : $() + +// NOTE: Eventually this will need to be changed when project_box has to be +// guarded by begin_borrow. +bb2(%2 : @owned ${ var Builtin.NativeObject }): + %3 = project_box %2 : ${ var Builtin.NativeObject }, 0 + %4 = load [copy] %3 : $*Builtin.NativeObject + %user = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + apply %user(%4) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + unreachable +} + +// CHECK-LABEL: sil [ossa] @enum_with_indirect_case_projectbox_loadcopy_to_loadborrow_deadend_2 : $@convention(thin) (@in_guaranteed EnumWithIndirectCase) -> () { +// CHECK: bb0 +// CHECK-NEXT: load_borrow +// CHECK: } // end sil function 'enum_with_indirect_case_projectbox_loadcopy_to_loadborrow_deadend_2' +sil [ossa] @enum_with_indirect_case_projectbox_loadcopy_to_loadborrow_deadend_2 : $@convention(thin) (@in_guaranteed EnumWithIndirectCase) -> () { +bb0(%0 : $*EnumWithIndirectCase): + %1 = load [copy] %0 : $*EnumWithIndirectCase + switch_enum %1 : $EnumWithIndirectCase, case #EnumWithIndirectCase.first!enumelt: bb1, case #EnumWithIndirectCase.second!enumelt: bb2 + +bb1: + %9999 = tuple() + return %9999 : $() + +// NOTE: Eventually this will need to be changed when project_box has to be +// guarded by begin_borrow. +bb2(%2 : @owned ${ var Builtin.NativeObject }): + %3 = project_box %2 : ${ var Builtin.NativeObject }, 0 + %4 = load [copy] %3 : $*Builtin.NativeObject + %user = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + apply %user(%4) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : ${ var Builtin.NativeObject } + unreachable +} \ No newline at end of file From d9963596eafeceb7d4c8ddae68e64af1797e03bd Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Mon, 12 Oct 2020 14:02:23 -0700 Subject: [PATCH 420/745] [Parse] Parse opaque result types in closure signature position 'canParseType()' didn't use to handle 'some'. Also, use 'isContexttualKeyword("some")' for checking 'some' keyword. https://bugs.swift.org/browse/SR-10769 --- lib/Parse/ParseType.cpp | 9 ++++++--- test/type/opaque.swift | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index b9c8c03f6f207..26e7b0999ea28 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -841,7 +841,7 @@ Parser::parseTypeSimpleOrComposition(Diag<> MessageID, // This is only semantically allowed in certain contexts, but we parse it // generally for diagnostics and recovery. SourceLoc opaqueLoc; - if (Tok.is(tok::identifier) && Tok.getRawText() == "some") { + if (Tok.isContextualKeyword("some")) { // Treat some as a keyword. TokReceiver->registerTokenKindChange(Tok.getLoc(), tok::contextual_keyword); opaqueLoc = consumeToken(); @@ -905,7 +905,7 @@ Parser::parseTypeSimpleOrComposition(Diag<> MessageID, } // Diagnose invalid `some` after an ampersand. - if (Tok.is(tok::identifier) && Tok.getRawText() == "some") { + if (Tok.isContextualKeyword("some")) { auto badLoc = consumeToken(); diagnose(badLoc, diag::opaque_mid_composition) @@ -1094,7 +1094,7 @@ ParserResult Parser::parseTypeTupleBody() { // If the label is "some", this could end up being an opaque type // description if there's `some ` without a following colon, // so we may need to backtrack as well. - if (Tok.getText().equals("some")) { + if (Tok.isContextualKeyword("some")) { Backtracking.emplace(*this); } @@ -1531,6 +1531,9 @@ bool Parser::canParseType() { // Accept 'inout' at for better recovery. consumeIf(tok::kw_inout); + if (Tok.isContextualKeyword("some")) + consumeToken(); + switch (Tok.getKind()) { case tok::kw_Self: case tok::kw_Any: diff --git a/test/type/opaque.swift b/test/type/opaque.swift index 5f3f34c7225e8..4b8190d7ff724 100644 --- a/test/type/opaque.swift +++ b/test/type/opaque.swift @@ -73,7 +73,7 @@ struct Test { let inferredOpaqueStructural2 = (bar(), bas()) // expected-error{{inferred type}} } -//let zingle = {() -> some P in 1 } // FIXME ex/pected-error{{'some' types are only implemented}} +let zingle = {() -> some P in 1 } // expected-error{{'some' types are only implemented}} // Invalid positions From 2d8f0733345acbb2b1a2308510dd1ba1308fb645 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 12 Oct 2020 16:04:43 -0700 Subject: [PATCH 421/745] [Concurrency] Implement global actor isolation checking for conformances. Witnesses and requirements need to agree on their global actor annotations. However, this is not true for 'async' or '@asyncHandler' witnesses, for which it does not matter what the actor annotation is because part of the contract is that the function will execute on the appropriate actor. --- include/swift/AST/DiagnosticsSema.def | 12 ++ lib/Sema/TypeCheckProtocol.cpp | 141 ++++++++++++++---- lib/Sema/TypeCheckProtocol.h | 5 + .../actor/global_actor_conformance.swift | 59 ++++++++ 4 files changed, 185 insertions(+), 32 deletions(-) create mode 100644 test/decl/class/actor/global_actor_conformance.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 13272e13a4858..6a3d213a2314f 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4194,6 +4194,18 @@ ERROR(actor_isolated_witness_could_be_async_handler,none, "actor-isolated %0 %1 cannot be used to satisfy a protocol requirement; " "did you mean to make it an asychronous handler?", (DescriptiveDeclKind, DeclName)) +ERROR(global_actor_isolated_requirement,none, + "%0 %1 must be isolated to the global actor %2 to satisfy corresponding " + "requirement from protocol %3", + (DescriptiveDeclKind, DeclName, Type, Identifier)) +ERROR(global_actor_isolated_witness,none, + "%0 %1 isolated to global actor %2 can not satisfy corresponding " + "requirement from protocol %3", + (DescriptiveDeclKind, DeclName, Type, Identifier)) +ERROR(global_actor_isolated_requirement_witness_conflict,none, + "%0 %1 isolated to global actor %2 can not satisfy corresponding " + "requirement from protocol %3 isolated to global actor %4", + (DescriptiveDeclKind, DeclName, Type, Identifier, Type)) ERROR(actorisolated_let,none, "'@actorIsolated' is meaningless on 'let' declarations because " diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 91e738fb3e384..91a5326f5394f 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -2656,6 +2656,114 @@ static void emitDeclaredHereIfNeeded(DiagnosticEngine &diags, diags.diagnose(value, diag::decl_declared_here, value->getName()); } +bool ConformanceChecker::checkActorIsolation( + ValueDecl *requirement, ValueDecl *witness) { + // Ensure that the witness is not actor-isolated in a manner that makes it + // unsuitable as a witness. + Type witnessGlobalActor; + switch (auto witnessRestriction = + ActorIsolationRestriction::forDeclaration(witness)) { + case ActorIsolationRestriction::ActorSelf: { + // Actor-isolated witnesses cannot conform to protocol requirements. + bool canBeAsyncHandler = false; + if (auto witnessFunc = dyn_cast(witness)) { + canBeAsyncHandler = !witnessFunc->isAsyncHandler() && + witnessFunc->canBeAsyncHandler(); + } + auto diag = witness->diagnose( + canBeAsyncHandler + ? diag::actor_isolated_witness_could_be_async_handler + : diag::actor_isolated_witness, + witness->getDescriptiveKind(), witness->getName()); + + if (canBeAsyncHandler) { + diag.fixItInsert( + witness->getAttributeInsertionLoc(false), "@asyncHandler "); + } + + return true; + } + + case ActorIsolationRestriction::GlobalActor: { + // Hang on to the global actor that's used for the witness. It will need + // to match that of the requirement. + witnessGlobalActor = witness->getDeclContext()->mapTypeIntoContext( + witnessRestriction.getGlobalActor()); + break; + } + + case ActorIsolationRestriction::Unsafe: + case ActorIsolationRestriction::LocalCapture: + break; + + case ActorIsolationRestriction::Unrestricted: + // The witness is completely unrestricted, so ignore any annotations on + // the requirement. + return false; + } + + // Check whether the requirement requires some particular actor isolation. + Type requirementGlobalActor; + switch (auto requirementIsolation = getActorIsolation(requirement)) { + case ActorIsolation::ActorInstance: + llvm_unreachable("There are not actor protocols"); + + case ActorIsolation::GlobalActor: { + auto requirementSubs = SubstitutionMap::getProtocolSubstitutions( + Proto, Adoptee, ProtocolConformanceRef(Conformance)); + requirementGlobalActor = requirementIsolation.getGlobalActor() + .subst(requirementSubs); + break; + } + + case ActorIsolation::Independent: + case ActorIsolation::Unspecified: + break; + } + + // If neither has a global actor, we're done. + if (!witnessGlobalActor && !requirementGlobalActor) + return false; + + // If the witness has a global actor but the requirement does not, we have + // an isolation error. + if (witnessGlobalActor && !requirementGlobalActor) { + witness->diagnose( + diag::global_actor_isolated_witness, witness->getDescriptiveKind(), + witness->getName(), witnessGlobalActor, Proto->getName()); + requirement->diagnose(diag::decl_declared_here, requirement->getName()); + return true; + } + + // If the requirement has a global actor but the witness does not, we have + // an isolation error. + // + // FIXME: Within a module, this will be an inference rule. + if (requirementGlobalActor && !witnessGlobalActor) { + witness->diagnose( + diag::global_actor_isolated_requirement, witness->getDescriptiveKind(), + witness->getName(), requirementGlobalActor, Proto->getName()) + .fixItInsert( + witness->getAttributeInsertionLoc(/*forModifier=*/false), + "@" + requirementGlobalActor.getString()); + requirement->diagnose(diag::decl_declared_here, requirement->getName()); + return true; + } + + // If both have global actors but they differ, this is an isolation error. + if (!witnessGlobalActor->isEqual(requirementGlobalActor)) { + witness->diagnose( + diag::global_actor_isolated_requirement_witness_conflict, + witness->getDescriptiveKind(), witness->getName(), witnessGlobalActor, + Proto->getName(), requirementGlobalActor); + requirement->diagnose(diag::decl_declared_here, requirement->getName()); + return true; + } + + // Everything is okay. + return false; +} + bool ConformanceChecker::checkObjCTypeErasedGenerics( AssociatedTypeDecl *assocType, Type type, @@ -4337,39 +4445,8 @@ void ConformanceChecker::resolveValueWitnesses() { return; } - // Check for actor-isolation consistency. - switch (auto restriction = - ActorIsolationRestriction::forDeclaration(witness)) { - case ActorIsolationRestriction::ActorSelf: { - // Actor-isolated witnesses cannot conform to protocol requirements. - bool canBeAsyncHandler = false; - if (auto witnessFunc = dyn_cast(witness)) { - canBeAsyncHandler = !witnessFunc->isAsyncHandler() && - witnessFunc->canBeAsyncHandler(); - } - auto diag = witness->diagnose( - canBeAsyncHandler - ? diag::actor_isolated_witness_could_be_async_handler - : diag::actor_isolated_witness, - witness->getDescriptiveKind(), witness->getName()); - - if (canBeAsyncHandler) { - diag.fixItInsert( - witness->getAttributeInsertionLoc(false), "@asyncHandler "); - } + if (checkActorIsolation(requirement, witness)) return; - } - - case ActorIsolationRestriction::GlobalActor: { - // FIXME: Check against the requirement. This needs serious refactoring. - break; - } - - case ActorIsolationRestriction::Unrestricted: - case ActorIsolationRestriction::Unsafe: - case ActorIsolationRestriction::LocalCapture: - break; - } // Objective-C checking for @objc requirements. if (requirement->isObjC() && diff --git a/lib/Sema/TypeCheckProtocol.h b/lib/Sema/TypeCheckProtocol.h index 3a8ecf1e34693..cabab3eeb13ae 100644 --- a/lib/Sema/TypeCheckProtocol.h +++ b/lib/Sema/TypeCheckProtocol.h @@ -739,6 +739,11 @@ class ConformanceChecker : public WitnessChecker { Type type, TypeDecl *typeDecl); + /// Check that the witness and requirement have compatible actor contexts. + /// + /// \returns true if an error occurred, false otherwise. + bool checkActorIsolation(ValueDecl *requirement, ValueDecl *witness); + /// Record a type witness. /// /// \param assocType The associated type whose witness is being recorded. diff --git a/test/decl/class/actor/global_actor_conformance.swift b/test/decl/class/actor/global_actor_conformance.swift new file mode 100644 index 0000000000000..8144892e12769 --- /dev/null +++ b/test/decl/class/actor/global_actor_conformance.swift @@ -0,0 +1,59 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// REQUIRES: concurrency + +import _Concurrency + +actor class SomeActor { } + +@globalActor +struct GlobalActor { + static var shared: SomeActor { SomeActor() } +} + +@globalActor +struct GenericGlobalActor { + static var shared: SomeActor { SomeActor() } +} + +protocol P1 { + associatedtype Assoc + + @GlobalActor func method1() // expected-note{{declared here}} + @GenericGlobalActor func method2() // expected-note{{declared here}} + @GenericGlobalActor func method3() + func method4() // expected-note{{declared here}} +} + +protocol P2 { + @GlobalActor func asyncMethod1() async + @GenericGlobalActor func asyncMethod2() async + func asyncMethod3() async +} + +class C1 : P1, P2 { + typealias Assoc = String + + // FIXME: This will be inferred + func method1() { } // expected-error{{instance method 'method1()' must be isolated to the global actor 'GlobalActor' to satisfy corresponding requirement from protocol 'P1'}}{{3-3=@GlobalActor}} + + @GenericGlobalActor func method2() { } // expected-error{{instance method 'method2()' isolated to global actor 'GenericGlobalActor' can not satisfy corresponding requirement from protocol 'P1' isolated to global actor 'GenericGlobalActor'}} + @GenericGlobalActorfunc method3() { } + @GlobalActor func method4() { } // expected-error{{instance method 'method4()' isolated to global actor 'GlobalActor' can not satisfy corresponding requirement from protocol 'P1'}} + + // Okay: we can ignore the mismatch in global actor types for 'async' methods. + func asyncMethod1() async { } + @GenericGlobalActor func asyncMethod2() async { } + @GlobalActor func asyncMethod3() async { } +} + + +class C2: P1 { + typealias Assoc = Int + + // Okay: we can ignore the mismatch in global actor types for 'asyncHandler' + // methods. + @asyncHandler func method1() { } + @asyncHandler func method2() { } + @asyncHandler func method3() { } + @asyncHandler func method4() { } +} From 90418612f400b3e2c58c4f42c635b55840ffeed8 Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Mon, 12 Oct 2020 17:43:53 -0700 Subject: [PATCH 422/745] [CodeCompletion] Reuse CompletionContextFinder for fallback completion when no typeCheckExpression call is made ...and adjust the fallback context it choses now that ErrorExprs no longer cause constraint generation to fail. Also fix some issues with the fallback logic in typeCheckForCodeCompletion: 1) For completion expressions in multi-statement closures, we were assuming a separate typeCheckExpression call would be made when the outer expression produced a single solution that had a resolved type for the closure. If the solution contained other fixes unrelated to the closure however, it wasn't applied and a separate call for the body was never made. 2) typeCheckForCodeComplation sometimes falls back to regular expression type checking but didn't update the passed-in target's expression after santizing and prechecking it, which may have modified it and its sub-expressions. This triggered assertion failures in certain cases due to the mix of the stale top-level expression pointer being used with updated subexpressions. --- lib/Sema/TypeCheckCodeCompletion.cpp | 365 +++++++++++++++------------ test/IDE/complete_ambiguous.swift | 23 ++ 2 files changed, 222 insertions(+), 166 deletions(-) diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index 08f2165d49c9e..5df6785449ef6 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -593,170 +593,201 @@ TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, } } -bool TypeChecker::typeCheckForCodeCompletion( - SolutionApplicationTarget &target, - llvm::function_ref callback) { - auto *DC = target.getDeclContext(); - auto &Context = DC->getASTContext(); - - auto *expr = target.getAsExpr(); - if (!expr) - return false; - - // First of all, let's check whether given target expression - // does indeed have the code completion location in it. - { - auto range = expr->getSourceRange(); - if (range.isInvalid() || - !Context.SourceMgr.rangeContainsCodeCompletionLoc(range)) - return false; - } - - FrontendStatsTracer StatsTracer(Context.Stats, - "typecheck-for-code-completion", expr); - PrettyStackTraceExpr stackTrace(Context, "code-completion", expr); - - expr = expr->walk(SanitizeExpr(Context, - /*shouldReusePrecheckedType=*/false)); +namespace { +class CompletionContextFinder : public ASTWalker { enum class ContextKind { - Expression, - Application, + FallbackExpression, StringInterpolation, SingleStmtClosure, MultiStmtClosure, ErrorExpression }; - class ContextFinder : public ASTWalker { - using Context = std::pair; + struct Context { + ContextKind Kind; + Expr * E; + }; - // Stack of all "interesting" contexts up to code completion expression. - llvm::SmallVector Contexts; + /// Stack of all "interesting" contexts up to code completion expression. + llvm::SmallVector Contexts; + CodeCompletionExpr *CompletionExpr = nullptr; + Expr *InitialExpr = nullptr; + DeclContext *InitialDC; - Expr *CompletionExpr = nullptr; +public: + /// Finder for completion contexts within the provided initial expression. + CompletionContextFinder(Expr *initialExpr, DeclContext *DC) + : InitialExpr(initialExpr), InitialDC(DC) { + assert(DC); + initialExpr->walk(*this); + }; - public: - ContextFinder(Expr *E) { - Contexts.push_back(std::make_pair(ContextKind::Expression, E)); + /// Finder for completion contexts within the outermost non-closure context of + /// the code completion expression's direct context. + CompletionContextFinder(DeclContext *completionDC): InitialDC(completionDC) { + while (auto *ACE = dyn_cast(InitialDC)) + InitialDC = ACE->getParent(); + InitialDC->walkContext(*this); + } + + std::pair walkToExprPre(Expr *E) override { + if (auto *closure = dyn_cast(E)) { + Contexts.push_back({closure->hasSingleExpressionBody() + ? ContextKind::SingleStmtClosure + : ContextKind::MultiStmtClosure, + closure}); } - std::pair walkToExprPre(Expr *E) override { - if (auto *closure = dyn_cast(E)) { - Contexts.push_back(std::make_pair(closure->hasSingleExpressionBody() - ? ContextKind::SingleStmtClosure - : ContextKind::MultiStmtClosure, - closure)); - } + if (isa(E)) { + Contexts.push_back({ContextKind::StringInterpolation, E}); + } - if (isa(E)) { - Contexts.push_back(std::make_pair(ContextKind::StringInterpolation, E)); - } + if (isa(E) || isa(E)) { + Contexts.push_back({ContextKind::FallbackExpression, E}); + } - if (isa(E)) { - Contexts.push_back(std::make_pair(ContextKind::Application, E)); + if (auto *Error = dyn_cast(E)) { + Contexts.push_back({ContextKind::ErrorExpression, E}); + if (auto *OrigExpr = Error->getOriginalExpr()) { + OrigExpr->walk(*this); + if (hasCompletionExpr()) + return std::make_pair(false, nullptr); } + } - if (isa(E)) { - CompletionExpr = E; - return std::make_pair(false, nullptr); - } + if (auto *CCE = dyn_cast(E)) { + CompletionExpr = CCE; + return std::make_pair(false, nullptr); + } - if (auto *Error = dyn_cast(E)) { - Contexts.push_back(std::make_pair(ContextKind::ErrorExpression, E)); - if (auto *OrigExpr = Error->getOriginalExpr()) { - OrigExpr->walk(*this); - return std::make_pair(false, hasCompletionExpr() ? nullptr : E); - } - } + return std::make_pair(true, E); + } - return std::make_pair(true, E); + Expr *walkToExprPost(Expr *E) override { + if (isa(E) || isa(E) || + isa(E) || isa(E) || isa(E)) { + assert(Contexts.back().E == E); + Contexts.pop_back(); } + return E; + } - Expr *walkToExprPost(Expr *E) override { - if (isa(E) || isa(E) || - isa(E) || isa(E)) - Contexts.pop_back(); - return E; - } + /// Check whether code completion expression is located inside of a + /// multi-statement closure. + bool locatedInMultiStmtClosure() const { + return hasContext(ContextKind::MultiStmtClosure); + } - /// Check whether code completion expression is located inside of a - /// multi-statement closure. - bool locatedInMultiStmtClosure() const { - return hasContext(ContextKind::MultiStmtClosure); - } + bool locatedInStringIterpolation() const { + return hasContext(ContextKind::StringInterpolation); + } - bool locatedInStringIterpolation() const { - return hasContext(ContextKind::StringInterpolation); - } + bool hasCompletionExpr() const { + return CompletionExpr; + } - bool hasCompletionExpr() const { - return CompletionExpr; - } + CodeCompletionExpr *getCompletionExpr() const { + assert(CompletionExpr); + return CompletionExpr; + } - Expr *getCompletionExpr() const { - assert(CompletionExpr); - return CompletionExpr; - } + struct Fallback { + Expr *E; ///< The fallback expression. + DeclContext *DC; ///< The fallback expression's decl context. + bool SeparatePrecheck; ///< True if the fallback may require prechecking. + }; - ErrorExpr *getInnermostErrorExpr() const { - for (const Context &curr : llvm::reverse(Contexts)) { - if (curr.first == ContextKind::ErrorExpression) - return cast(curr.second); - } - return nullptr; - } + /// As a fallback sometimes its useful to not only type-check + /// code completion expression directly but instead add some + /// of the enclosing context e.g. when completion is an argument + /// to a call. + Optional getFallbackCompletionExpr() const { + assert(CompletionExpr); + + Optional fallback; + bool separatePrecheck = false; + DeclContext *fallbackDC = InitialDC; + + // Find the outermost fallback expression within the innermost error + // expression or multi-statement closure, keeping track of its decl context. + for (auto context: Contexts) { + switch (context.Kind) { + case ContextKind::StringInterpolation: + LLVM_FALLTHROUGH; + case ContextKind::FallbackExpression: + if (!fallback && context.E != InitialExpr) + fallback = Fallback{context.E, fallbackDC, separatePrecheck}; + continue; + + case ContextKind::SingleStmtClosure: + if (!fallback && context.E != InitialExpr) + fallback = Fallback{context.E, fallbackDC, separatePrecheck}; + fallbackDC = cast(context.E); + continue; - ClosureExpr *getOutermostMultiStmtClosure() const { - for (const Context &curr : Contexts) { - if (curr.first == ContextKind::MultiStmtClosure) - return cast(curr.second); + case ContextKind::MultiStmtClosure: + fallbackDC = cast(context.E); + LLVM_FALLTHROUGH; + case ContextKind::ErrorExpression:; + fallback = None; + separatePrecheck = true; + continue; } - return nullptr; } - /// As a fallback sometimes its useful to not only type-check - /// code completion expression directly but instead add some - /// of the enclosing context e.g. when completion is an argument - /// to a call. - Expr *getCompletionExprInContext() const { - assert(CompletionExpr); - - auto &innerContext = Contexts.back(); - return innerContext.first == ContextKind::Application - ? innerContext.second - : CompletionExpr; - } + if (fallback) + return fallback; - private: - bool hasContext(ContextKind kind) const { - return llvm::find_if(Contexts, [&kind](const Context &currContext) { - return currContext.first == kind; - }) != Contexts.end(); - } - }; + if (CompletionExpr->getBase() && CompletionExpr != InitialExpr) + return Fallback{CompletionExpr, fallbackDC, separatePrecheck}; + return None; + } + +private: + bool hasContext(ContextKind kind) const { + return llvm::find_if(Contexts, [&kind](const Context &currContext) { + return currContext.Kind == kind; + }) != Contexts.end(); + } +}; + +} // end namespace + +bool TypeChecker::typeCheckForCodeCompletion( + SolutionApplicationTarget &target, + llvm::function_ref callback) { + auto *DC = target.getDeclContext(); + auto &Context = DC->getASTContext(); + + auto *expr = target.getAsExpr(); + if (!expr) + return false; + + // First of all, let's check whether given target expression + // does indeed have the code completion location in it. + { + auto range = expr->getSourceRange(); + if (range.isInvalid() || + !Context.SourceMgr.rangeContainsCodeCompletionLoc(range)) + return false; + } + + FrontendStatsTracer StatsTracer(Context.Stats, + "typecheck-for-code-completion", expr); + PrettyStackTraceExpr stackTrace(Context, "code-completion", expr); + + expr = expr->walk(SanitizeExpr(Context, + /*shouldReusePrecheckedType=*/false)); + target.setExpr(expr); - ContextFinder contextAnalyzer(expr); - expr->walk(contextAnalyzer); + CompletionContextFinder contextAnalyzer(expr, DC); // If there was no completion expr (e.g. if the code completion location was // among tokens that were skipped over during parser error recovery) bail. if (!contextAnalyzer.hasCompletionExpr()) return false; - // If the completion expression is in a valid subexpression of an ErrorExpr, - // fallback to trying the valid subexpression without any context. This can - // happen for cases like `expectsBoolArg(foo.).` which becomes an - // ErrorExpr due to the missing member name after the final dot. - if (auto *errorExpr = contextAnalyzer.getInnermostErrorExpr()) { - if (auto *origExpr = errorExpr->getOriginalExpr()) { - SolutionApplicationTarget completionTarget(origExpr, DC, CTP_Unused, - /*contextualType=*/Type(), - /*isDiscarded=*/true); - return typeCheckForCodeCompletion(completionTarget, callback); - } - } - // Interpolation components are type-checked separately. if (contextAnalyzer.locatedInStringIterpolation()) return false; @@ -793,41 +824,36 @@ bool TypeChecker::typeCheckForCodeCompletion( llvm::SmallVector solutions; // If solve failed to generate constraints or with some other - // issue, we need to fallback to type-checking code completion - // expression directly. + // issue, we need to fallback to type-checking a sub-expression. if (!cs.solveForCodeCompletion(target, solutions)) return CompletionResult::Fallback; - // If case type-check didn't produce any solutions, let's - // attempt to type-check code completion expression without - // enclosing context. + // Similarly, if the type-check didn't produce any solutions, fall back + // to type-checking a sub-expression in isolation. if (solutions.empty()) return CompletionResult::Fallback; // If code completion expression resides inside of multi-statement - // closure body it code either be type-checker together with context - // or not, it's impossible to say without trying. If solution - // doesn't have a type for a code completion expression it means that - // we have to wait until body of the closure is type-checked. + // closure body it could either be type-checked together with the context + // or not, it's impossible to say without checking. if (contextAnalyzer.locatedInMultiStmtClosure()) { auto &solution = solutions.front(); - // Let's check whether closure participated in the type-check. if (solution.hasType(contextAnalyzer.getCompletionExpr())) { llvm::for_each(solutions, callback); return CompletionResult::Ok; } - if (solutions.size() > 1) - return CompletionResult::Fallback; + // At this point we know the code completion expression wasn't checked + // with the closure's surrounding context. If a single valid solution + // was formed we can wait until body of the closure is type-checked and + // gather completions then. + if (solutions.size() == 1 && solution.Fixes.empty()) + return CompletionResult::NotApplicable; - auto *closure = contextAnalyzer.getOutermostMultiStmtClosure(); - auto closureType = solution.getResolvedType(closure); - - if (closureType->hasUnresolvedType()) - return CompletionResult::Fallback; - - return CompletionResult::NotApplicable; + // Otherwise, it's unlikely the body will ever be type-checked, so fall + // back to manually checking a sub-expression within the closure body. + return CompletionResult::Fallback; } llvm::for_each(solutions, callback); @@ -845,33 +871,25 @@ bool TypeChecker::typeCheckForCodeCompletion( break; } - { - auto *completionExpr = contextAnalyzer.getCompletionExpr(); - - if (contextAnalyzer.locatedInMultiStmtClosure()) { - auto completionInContext = contextAnalyzer.getCompletionExprInContext(); - // If pre-check fails, let's switch to code completion - // expression without any enclosing context. - if (ConstraintSystem::preCheckExpression( - completionInContext, DC, /*replaceInvalidRefsWithErrors=*/true)) { - completionExpr = contextAnalyzer.getCompletionExpr(); - } else { - completionExpr = completionInContext; - } - } - - // If initial solve failed, let's fallback to checking only code completion - // expresion without any context. - SolutionApplicationTarget completionTarget(completionExpr, DC, CTP_Unused, + // Determine the best subexpression to use based on the collected context + // of the code completion expression. + if (auto fallback = contextAnalyzer.getFallbackCompletionExpr()) { + assert(fallback->E != expr); + SolutionApplicationTarget completionTarget(fallback->E, + fallback->DC, CTP_Unused, /*contextualType=*/Type(), /*isDiscarded=*/true); + if (fallback->SeparatePrecheck) { + typeCheckForCodeCompletion(completionTarget, callback); + return true; + } switch (solveForCodeCompletion(completionTarget)) { case CompletionResult::Ok: case CompletionResult::Fallback: break; case CompletionResult::NotApplicable: - llvm_unreachable("solve on CodeCompletionExpr produced not applicable?"); + llvm_unreachable("fallback expr not applicable?"); } } return true; @@ -974,8 +992,23 @@ swift::lookupSemanticMember(DeclContext *DC, Type ty, DeclName name) { void DotExprTypeCheckCompletionCallback::fallbackTypeCheck() { assert(!gotCallback()); - SolutionApplicationTarget completionTarget(CompletionExpr, DC, CTP_Unused, - Type(), /*isDiscared=*/true); + + // Default to checking the completion expression in isolation. + Expr *fallbackExpr = CompletionExpr; + DeclContext *fallbackDC = DC; + + CompletionContextFinder finder(DC); + if (finder.hasCompletionExpr()) { + if (auto fallback = finder.getFallbackCompletionExpr()) { + fallbackExpr = fallback->E; + fallbackDC = fallback->DC; + } + } + + SolutionApplicationTarget completionTarget(fallbackExpr, fallbackDC, + CTP_Unused, Type(), + /*isDiscared=*/true); + TypeChecker::typeCheckForCodeCompletion( completionTarget, [&](const Solution &S) { sawSolution(S); }); } diff --git a/test/IDE/complete_ambiguous.swift b/test/IDE/complete_ambiguous.swift index 3f6d9a8990e50..2a2028c44f38d 100644 --- a/test/IDE/complete_ambiguous.swift +++ b/test/IDE/complete_ambiguous.swift @@ -4,6 +4,8 @@ // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=RELATED | %FileCheck %s --check-prefix=RELATED // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=RELATED_EXTRAARG | %FileCheck %s --check-prefix=RELATED // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=RELATED_INERROREXPR | %FileCheck %s --check-prefix=RELATED +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=NOCALLBACK_FALLBACK | %FileCheck %s --check-prefix=RELATED +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=MULTICLOSURE_FALLBACK | %FileCheck %s --check-prefix=MULTICLOSURE_FALLBACK // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=ERROR_IN_BASE | %FileCheck %s --check-prefix=SIMPLE // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC | %FileCheck %s --check-prefix=GENERIC // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_MISSINGARG | %FileCheck %s --check-prefix=NORESULTS @@ -62,6 +64,27 @@ func takesB(_ item: B) {} takesB((takesA { return overloadedReturn().#^RELATED_INERROREXPR^# }).) +switch undefined { + case takesA { return overloadedReturn().#^NOCALLBACK_FALLBACK^# }: + break +} + + +func takesClosureA(_ arg: (A) -> ()) {} +func takesClosureB(_ arg: (B) -> ()) {} + +takesClosureA { arg in + takesClosureB { arg in + arg.#^MULTICLOSURE_FALLBACK^# + } + print() + 10 +} + 10 + +// MULTICLOSURE_FALLBACK: Begin completions, 2 items +// MULTICLOSURE_FALLBACK-DAG: Keyword[self]/CurrNominal: self[#B#]{{; name=.+$}} +// MULTICLOSURE_FALLBACK-DAG: Decl[InstanceMethod]/CurrNominal: doBThings()[#Void#]{{; name=.+$}} +// MULTICLOSURE_FALLBACK: End completions + protocol C { associatedtype Element func getCElem() -> Element From 9239692d00eb7a5362fe18f42dfcd9ddc687f45c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 9 Oct 2020 22:49:11 -0700 Subject: [PATCH 423/745] [unittests] Add a fixture for Sema unit tests --- unittests/CMakeLists.txt | 1 + unittests/Sema/CMakeLists.txt | 7 +++++ unittests/Sema/SemaFixture.cpp | 36 ++++++++++++++++++++++++ unittests/Sema/SemaFixture.h | 51 ++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 unittests/Sema/CMakeLists.txt create mode 100644 unittests/Sema/SemaFixture.cpp create mode 100644 unittests/Sema/SemaFixture.h diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 5c6daa804cff8..ca552a07b4d15 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -12,6 +12,7 @@ if(SWIFT_INCLUDE_TOOLS) add_subdirectory(Localization) add_subdirectory(IDE) add_subdirectory(Parse) + add_subdirectory(Sema) add_subdirectory(SwiftDemangle) add_subdirectory(Syntax) if(SWIFT_BUILD_SYNTAXPARSERLIB) diff --git a/unittests/Sema/CMakeLists.txt b/unittests/Sema/CMakeLists.txt new file mode 100644 index 0000000000000..7252e9b6129d1 --- /dev/null +++ b/unittests/Sema/CMakeLists.txt @@ -0,0 +1,7 @@ + +add_swift_unittest(swiftSemaTests + SemaFixture.cpp) + +target_link_libraries(swiftSemaTests + PRIVATE + swiftSema) diff --git a/unittests/Sema/SemaFixture.cpp b/unittests/Sema/SemaFixture.cpp new file mode 100644 index 0000000000000..e28f899c0157c --- /dev/null +++ b/unittests/Sema/SemaFixture.cpp @@ -0,0 +1,36 @@ +//===--- SemaFixture.cpp - Helper for setting up Sema context --------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "SemaFixture.h" +#include "swift/AST/Module.h" +#include "swift/AST/ParseRequests.h" +#include "swift/Strings.h" +#include "swift/Subsystems.h" + +using namespace swift; +using namespace swift::unittest; + +SemaTest::SemaTest() + : Context(*ASTContext::get(LangOpts, TypeCheckerOpts, SearchPathOpts, + ClangImporterOpts, SourceMgr, Diags)) { + registerParseRequestFunctions(Context.evaluator); + registerTypeCheckerRequestFunctions(Context.evaluator); + auto stdlibID = Context.getIdentifier(STDLIB_NAME); + auto *module = ModuleDecl::create(stdlibID, Context); + Context.addLoadedModule(module); + + FileForLookups = new (Context) SourceFile(*module, SourceFileKind::Library, + /*buffer*/ None); + module->addFile(*FileForLookups); + + DC = module; +} diff --git a/unittests/Sema/SemaFixture.h b/unittests/Sema/SemaFixture.h new file mode 100644 index 0000000000000..da6d0ae0fbe2b --- /dev/null +++ b/unittests/Sema/SemaFixture.h @@ -0,0 +1,51 @@ +//===--- SemaFixture.h - Helper for setting up Sema context -----*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/ASTContext.h" +#include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/Module.h" +#include "swift/AST/SourceFile.h" +#include "swift/Basic/LangOptions.h" +#include "swift/Basic/SourceManager.h" +#include "llvm/Support/Host.h" +#include "gtest/gtest.h" + +namespace swift { +namespace unittest { + +class SemaTestBase : public ::testing::Test { +public: + LangOptions LangOpts; + TypeCheckerOptions TypeCheckerOpts; + SearchPathOptions SearchPathOpts; + ClangImporterOptions ClangImporterOpts; + SourceManager SourceMgr; + DiagnosticEngine Diags; + + SemaTestBase() : Diags(SourceMgr) { + LangOpts.Target = llvm::Triple(llvm::sys::getProcessTriple()); + } +}; + +/// Owns an ASTContext and the associated types. +class SemaTest : public SemaTestBase { + SourceFile *FileForLookups; + +public: + ASTContext &Context; + DeclContext *DC; + + SemaTest(); +}; + +} // end namespace unittest +} // end namespace swift From 0b22d91c9416e57a37829a6c471770e53f96fee2 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 12 Oct 2020 15:38:45 -0700 Subject: [PATCH 424/745] [unittests] Extend Sema testing fixture to load stdlib (+ shims) Setup module importers, load stdlib, establish separate testing module and load a single main file there which imports standard library. --- unittests/Sema/CMakeLists.txt | 7 ++++++- unittests/Sema/SemaFixture.cpp | 32 +++++++++++++++++++++++++------- unittests/Sema/SemaFixture.h | 15 ++++++++++++++- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/unittests/Sema/CMakeLists.txt b/unittests/Sema/CMakeLists.txt index 7252e9b6129d1..270b08eff4b34 100644 --- a/unittests/Sema/CMakeLists.txt +++ b/unittests/Sema/CMakeLists.txt @@ -4,4 +4,9 @@ add_swift_unittest(swiftSemaTests target_link_libraries(swiftSemaTests PRIVATE - swiftSema) + swiftAST + swiftSema + swiftSerialization) + +target_compile_definitions(swiftSemaTests PRIVATE + SWIFTLIB_DIR=\"${SWIFTLIB_DIR}\") diff --git a/unittests/Sema/SemaFixture.cpp b/unittests/Sema/SemaFixture.cpp index e28f899c0157c..6ca2e8c487cb3 100644 --- a/unittests/Sema/SemaFixture.cpp +++ b/unittests/Sema/SemaFixture.cpp @@ -13,24 +13,42 @@ #include "SemaFixture.h" #include "swift/AST/Module.h" #include "swift/AST/ParseRequests.h" -#include "swift/Strings.h" +#include "swift/AST/SourceFile.h" +#include "swift/Basic/LLVMInitialize.h" +#include "swift/ClangImporter/ClangImporter.h" +#include "swift/Serialization/SerializedModuleLoader.h" #include "swift/Subsystems.h" using namespace swift; using namespace swift::unittest; +using ModuleDecl = SourceFile::ImportedModuleDesc; + SemaTest::SemaTest() : Context(*ASTContext::get(LangOpts, TypeCheckerOpts, SearchPathOpts, ClangImporterOpts, SourceMgr, Diags)) { + INITIALIZE_LLVM(); + registerParseRequestFunctions(Context.evaluator); registerTypeCheckerRequestFunctions(Context.evaluator); - auto stdlibID = Context.getIdentifier(STDLIB_NAME); - auto *module = ModuleDecl::create(stdlibID, Context); - Context.addLoadedModule(module); - FileForLookups = new (Context) SourceFile(*module, SourceFileKind::Library, - /*buffer*/ None); - module->addFile(*FileForLookups); + Context.addModuleLoader(ImplicitSerializedModuleLoader::create(Context)); + Context.addModuleLoader(ClangImporter::create(Context), /*isClang=*/true); + + auto *stdlib = Context.getStdlibModule(/*loadIfAbsent=*/true); + assert(stdlib && "Failed to load standard library"); + + auto *module = + ModuleDecl::create(Context.getIdentifier("SemaTests"), Context); + + MainFile = new (Context) SourceFile(*module, SourceFileKind::Main, + /*buffer=*/None); + + auto stdlibImport = + ModuleDesc({ImportPath::Access(), stdlib}, /*options=*/{}); + + MainFile->setImports(stdlibImport); + module->addFile(*MainFile); DC = module; } diff --git a/unittests/Sema/SemaFixture.h b/unittests/Sema/SemaFixture.h index da6d0ae0fbe2b..61d303f4220d8 100644 --- a/unittests/Sema/SemaFixture.h +++ b/unittests/Sema/SemaFixture.h @@ -15,9 +15,14 @@ #include "swift/AST/Module.h" #include "swift/AST/SourceFile.h" #include "swift/Basic/LangOptions.h" +#include "swift/Basic/Platform.h" #include "swift/Basic/SourceManager.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Support/Host.h" +#include "llvm/Support/Path.h" #include "gtest/gtest.h" +#include namespace swift { namespace unittest { @@ -33,12 +38,20 @@ class SemaTestBase : public ::testing::Test { SemaTestBase() : Diags(SourceMgr) { LangOpts.Target = llvm::Triple(llvm::sys::getProcessTriple()); + + llvm::SmallString<128> libDir(SWIFTLIB_DIR); + llvm::sys::path::append(libDir, getPlatformNameForTriple(LangOpts.Target)); + + SearchPathOpts.RuntimeResourcePath = SWIFTLIB_DIR; + SearchPathOpts.RuntimeLibraryPaths.push_back(std::string(libDir.str())); + SearchPathOpts.RuntimeLibraryImportPaths.push_back( + std::string(libDir.str())); } }; /// Owns an ASTContext and the associated types. class SemaTest : public SemaTestBase { - SourceFile *FileForLookups; + SourceFile *MainFile; public: ASTContext &Context; From b2c31c394b02c0b2ae97f61e57daeeef8c770087 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 12 Oct 2020 17:21:09 -0700 Subject: [PATCH 425/745] [unittests/Sema] Add an ability to retrieve stdlib types by name --- unittests/Sema/SemaFixture.cpp | 23 +++++++++++++++++++++++ unittests/Sema/SemaFixture.h | 4 ++++ 2 files changed, 27 insertions(+) diff --git a/unittests/Sema/SemaFixture.cpp b/unittests/Sema/SemaFixture.cpp index 6ca2e8c487cb3..4c404b4195708 100644 --- a/unittests/Sema/SemaFixture.cpp +++ b/unittests/Sema/SemaFixture.cpp @@ -11,9 +11,12 @@ //===----------------------------------------------------------------------===// #include "SemaFixture.h" +#include "swift/AST/Decl.h" #include "swift/AST/Module.h" #include "swift/AST/ParseRequests.h" #include "swift/AST/SourceFile.h" +#include "swift/AST/Type.h" +#include "swift/AST/Types.h" #include "swift/Basic/LLVMInitialize.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/Serialization/SerializedModuleLoader.h" @@ -52,3 +55,23 @@ SemaTest::SemaTest() DC = module; } + +Type SemaTest::getStdlibType(StringRef name) const { + auto typeName = Context.getIdentifier(name); + + auto *stdlib = Context.getStdlibModule(); + + llvm::SmallVector results; + stdlib->lookupValue(typeName, NLKind::UnqualifiedLookup, results); + + if (results.size() != 1) + return Type(); + + if (auto *decl = dyn_cast(results.front())) { + if (auto *NTD = dyn_cast(decl)) + return NTD->getDeclaredType(); + return decl->getDeclaredInterfaceType(); + } + + return Type(); +} diff --git a/unittests/Sema/SemaFixture.h b/unittests/Sema/SemaFixture.h index 61d303f4220d8..eca5039a10c45 100644 --- a/unittests/Sema/SemaFixture.h +++ b/unittests/Sema/SemaFixture.h @@ -14,6 +14,7 @@ #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/Module.h" #include "swift/AST/SourceFile.h" +#include "swift/AST/Type.h" #include "swift/Basic/LangOptions.h" #include "swift/Basic/Platform.h" #include "swift/Basic/SourceManager.h" @@ -58,6 +59,9 @@ class SemaTest : public SemaTestBase { DeclContext *DC; SemaTest(); + +protected: + Type getStdlibType(StringRef name) const; }; } // end namespace unittest From dc7c9c2bfa0167180ec19f7a26029e16ef03dd60 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 12 Oct 2020 17:22:50 -0700 Subject: [PATCH 426/745] [unittests/Sema] Add a simple integer literal type inference test --- include/swift/Sema/ConstraintSystem.h | 1 + unittests/Sema/BindingInferenceTests.cpp | 46 ++++++++++++++++++++++++ unittests/Sema/CMakeLists.txt | 3 +- 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 unittests/Sema/BindingInferenceTests.cpp diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 17f804123a6c3..e3e2735b60ea6 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -4942,6 +4942,7 @@ class ConstraintSystem { Optional checkTypeOfBinding(TypeVariableType *typeVar, Type type) const; Optional determineBestBindings(); +public: /// Infer bindings for the given type variable based on current /// state of the constraint system. PotentialBindings inferBindingsFor(TypeVariableType *typeVar, diff --git a/unittests/Sema/BindingInferenceTests.cpp b/unittests/Sema/BindingInferenceTests.cpp new file mode 100644 index 0000000000000..f4b71d921a28c --- /dev/null +++ b/unittests/Sema/BindingInferenceTests.cpp @@ -0,0 +1,46 @@ +//===--- BindingInferenceTests.cpp ----------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "SemaFixture.h" +#include "swift/AST/Expr.h" +#include "swift/Sema/ConstraintSystem.h" + +using namespace swift; +using namespace swift::unittest; +using namespace swift::constraints; + +TEST_F(SemaTest, TestIntLiteralBindingInference) { + ConstraintSystemOptions options; + options |= ConstraintSystemFlags::AllowUnresolvedTypeVariables; + + ConstraintSystem cs(DC, options); + + auto *intLiteral = IntegerLiteralExpr::createFromUnsigned(Context, 42); + + auto *literalTy = cs.createTypeVariable(cs.getConstraintLocator(intLiteral), + /*options=*/0); + + cs.addConstraint( + ConstraintKind::LiteralConformsTo, literalTy, + Context.getProtocol(KnownProtocolKind::ExpressibleByIntegerLiteral) + ->getDeclaredInterfaceType(), + cs.getConstraintLocator(intLiteral)); + + auto bindings = cs.inferBindingsFor(literalTy); + + ASSERT_EQ(bindings.Bindings.size(), (unsigned)1); + + const auto &binding = bindings.Bindings.front(); + + ASSERT_TRUE(binding.BindingType->isEqual(getStdlibType("Int"))); + ASSERT_TRUE(binding.hasDefaultedLiteralProtocol()); +} diff --git a/unittests/Sema/CMakeLists.txt b/unittests/Sema/CMakeLists.txt index 270b08eff4b34..1257490d9183e 100644 --- a/unittests/Sema/CMakeLists.txt +++ b/unittests/Sema/CMakeLists.txt @@ -1,6 +1,7 @@ add_swift_unittest(swiftSemaTests - SemaFixture.cpp) + SemaFixture.cpp + BindingInferenceTests.cpp) target_link_libraries(swiftSemaTests PRIVATE From fb76ff1aea6aee7282d1e8652107f3f0e78b5d46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Mon, 12 Oct 2020 18:45:34 -0700 Subject: [PATCH 427/745] [Sema] Report non-constructor unavailable overrides as warnings Report unavailable overrides as a warning to avoid breaking sources. --- include/swift/AST/DiagnosticsSema.def | 6 ++++++ lib/Sema/TypeCheckDeclOverride.cpp | 31 ++++++++++++++++++--------- test/Sema/availability_versions.swift | 8 +++---- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 3b222cc32cf25..056d923a88d44 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2455,10 +2455,16 @@ NOTE(suggest_removing_override, none, ERROR(override_less_available,none, "overriding %0 must be as available as declaration it overrides", (DeclBaseName)) +WARNING(override_less_available_warn,none, + "overriding %0 must be as available as declaration it overrides", + (DeclBaseName)) ERROR(override_accessor_less_available,none, "overriding %0 for %1 must be as available as declaration it overrides", (DescriptiveDeclKind, DeclBaseName)) +WARNING(override_accessor_less_available_warn,none, + "overriding %0 for %1 must be as available as declaration it overrides", + (DescriptiveDeclKind, DeclBaseName)) ERROR(override_let_property,none, "cannot override immutable 'let' property %0 with the getter of a 'var'", diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index d7b79aa88a07c..33085e514644b 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1690,23 +1690,34 @@ static bool diagnoseOverrideForAvailability(ValueDecl *override, auto &ctx = override->getASTContext(); auto &diags = ctx.Diags; if (auto *accessor = dyn_cast(override)) { - diags.diagnose(override, diag::override_accessor_less_available, + bool downgradeToWarning = override->getAttrs().isUnavailable(ctx); + diags.diagnose(override, + downgradeToWarning? + diag::override_accessor_less_available_warn : + diag::override_accessor_less_available, accessor->getDescriptiveKind(), accessor->getStorage()->getBaseName()); diags.diagnose(base, diag::overridden_here); return true; } - // Don't report constructors that are marked unavailable as being less - // available than their introduction. This was previously allowed and - // can be used to forbid the direct use of a constructor in a subclass. - // Note that even when marked unavailable the constructor could be called - // by other inherited constructors. - if (isa(override) && - override->getAttrs().isUnavailable(ctx)) - return false; + bool downgradeToWarning = false; + if (override->getAttrs().isUnavailable(ctx)) { + // Don't report constructors that are marked unavailable as being less + // available than their introduction. This was previously allowed and + // can be used to forbid the direct use of a constructor in a subclass. + // Note that even when marked unavailable the constructor could be called + // by other inherited constructors. + if (isa(override)) + return false; + + // Report as a warning other unavailable overrides. + downgradeToWarning = true; + } - diags.diagnose(override, diag::override_less_available, + diags.diagnose(override, + downgradeToWarning? diag::override_less_available_warn : + diag::override_less_available, override->getBaseName()); diags.diagnose(base, diag::overridden_here); diff --git a/test/Sema/availability_versions.swift b/test/Sema/availability_versions.swift index 6ca4dada0de69..97ec079f50d06 100644 --- a/test/Sema/availability_versions.swift +++ b/test/Sema/availability_versions.swift @@ -845,11 +845,11 @@ class SubWithUnavailableMembers : SuperWithAlwaysAvailableMembers { required init() {} @available(OSX, unavailable) - override func shouldAlwaysBeAvailableMethod() { // expected-error {{overriding 'shouldAlwaysBeAvailableMethod' must be as available as declaration it overrides}} + override func shouldAlwaysBeAvailableMethod() { // expected-warning {{overriding 'shouldAlwaysBeAvailableMethod' must be as available as declaration it overrides}} } @available(OSX, unavailable) - override var shouldAlwaysBeAvailableProperty: Int { // expected-error {{overriding 'shouldAlwaysBeAvailableProperty' must be as available as declaration it overrides}} + override var shouldAlwaysBeAvailableProperty: Int { // expected-warning {{overriding 'shouldAlwaysBeAvailableProperty' must be as available as declaration it overrides}} get { return 10 } set(newVal) {} } @@ -857,13 +857,13 @@ class SubWithUnavailableMembers : SuperWithAlwaysAvailableMembers { override var setterShouldAlwaysBeAvailableProperty: Int { get { return 9 } @available(OSX, unavailable) - set(newVal) {} // expected-error {{overriding setter for 'setterShouldAlwaysBeAvailableProperty' must be as available as declaration it overrides}} + set(newVal) {} // expected-warning {{overriding setter for 'setterShouldAlwaysBeAvailableProperty' must be as available as declaration it overrides}} // This is a terrible diagnostic. rdar://problem/20427938 } override var getterShouldAlwaysBeAvailableProperty: Int { @available(OSX, unavailable) - get { return 9 } // expected-error {{overriding getter for 'getterShouldAlwaysBeAvailableProperty' must be as available as declaration it overrides}} + get { return 9 } // expected-warning {{overriding getter for 'getterShouldAlwaysBeAvailableProperty' must be as available as declaration it overrides}} set(newVal) {} } } From dfc5c708dc7c314a6137ed437e61200e5e675cc9 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Thu, 23 Apr 2020 17:29:08 -0700 Subject: [PATCH 428/745] [SIL] [Parser] Move SILParserTUState into SILParserState.h Moves the declaration of SILParserTUState into a new private header file: SILParserState.h --- lib/SIL/Parser/ParseSIL.cpp | 35 +------------------ lib/SIL/Parser/SILParserState.h | 61 +++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 34 deletions(-) create mode 100644 lib/SIL/Parser/SILParserState.h diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 1d093aa9e7132..73e2d5acf9f5b 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "SILParserFunctionBuilder.h" +#include "SILParserState.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" @@ -45,40 +46,6 @@ using namespace swift::syntax; // SILParserState implementation //===----------------------------------------------------------------------===// -namespace { -class SILParserState : public SILParserStateBase { -public: - explicit SILParserState(SILModule &M) : M(M) {} - ~SILParserState(); - - SILModule &M; - - /// This is all of the forward referenced functions with - /// the location for where the reference is. - llvm::DenseMap> ForwardRefFns; - /// A list of all functions forward-declared by a sil_scope. - llvm::DenseSet PotentialZombieFns; - - /// A map from textual .sil scope number to SILDebugScopes. - llvm::DenseMap ScopeSlots; - - /// Did we parse a sil_stage for this module? - bool DidParseSILStage = false; - - bool parseDeclSIL(Parser &P) override; - bool parseDeclSILStage(Parser &P) override; - bool parseSILVTable(Parser &P) override; - bool parseSILGlobal(Parser &P) override; - bool parseSILWitnessTable(Parser &P) override; - bool parseSILDefaultWitnessTable(Parser &P) override; - bool parseSILDifferentiabilityWitness(Parser &P) override; - bool parseSILCoverageMap(Parser &P) override; - bool parseSILProperty(Parser &P) override; - bool parseSILScope(Parser &P) override; -}; -} // end anonymous namespace - SILParserState::~SILParserState() { if (!ForwardRefFns.empty()) { for (auto Entry : ForwardRefFns) { diff --git a/lib/SIL/Parser/SILParserState.h b/lib/SIL/Parser/SILParserState.h new file mode 100644 index 0000000000000..7d915657db170 --- /dev/null +++ b/lib/SIL/Parser/SILParserState.h @@ -0,0 +1,61 @@ +//===--- SILParserState.h - SILParserState declaration -------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SIL_SILPARSERSTATE_H +#define SWIFT_SIL_SILPARSERSTATE_H + +#include "swift/Basic/LLVM.h" +#include "swift/Parse/ParseSILSupport.h" + +//===----------------------------------------------------------------------===// +// SILParserState +//===----------------------------------------------------------------------===// + +namespace swift { + +class Parser; +class SILModule; + +class SILParserState : public SILParserStateBase { +public: + explicit SILParserState(SILModule &M) : M(M) {} + ~SILParserState(); + + SILModule &M; + + /// This is all of the forward referenced functions with + /// the location for where the reference is. + llvm::DenseMap> ForwardRefFns; + /// A list of all functions forward-declared by a sil_scope. + llvm::DenseSet PotentialZombieFns; + + /// A map from textual .sil scope number to SILDebugScopes. + llvm::DenseMap ScopeSlots; + + /// Did we parse a sil_stage for this module? + bool DidParseSILStage = false; + + bool parseDeclSIL(Parser &P) override; + bool parseDeclSILStage(Parser &P) override; + bool parseSILVTable(Parser &P) override; + bool parseSILGlobal(Parser &P) override; + bool parseSILWitnessTable(Parser &P) override; + bool parseSILDefaultWitnessTable(Parser &P) override; + bool parseSILDifferentiabilityWitness(Parser &P) override; + bool parseSILCoverageMap(Parser &P) override; + bool parseSILProperty(Parser &P) override; + bool parseSILScope(Parser &P) override; +}; + +} // end namespace swift + +#endif // SWIFT_SIL_SILPARSERSTATE_H From 6e470f5fcd55e94d5010e4621bec225314327f50 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 13 Oct 2020 00:12:09 -0700 Subject: [PATCH 429/745] [unittest/Sema] NFC: Switch to use `AttributedImport` instead of deprecated `ImportedModuleDesc` --- unittests/Sema/SemaFixture.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/unittests/Sema/SemaFixture.cpp b/unittests/Sema/SemaFixture.cpp index 4c404b4195708..d260b1227ca73 100644 --- a/unittests/Sema/SemaFixture.cpp +++ b/unittests/Sema/SemaFixture.cpp @@ -12,6 +12,7 @@ #include "SemaFixture.h" #include "swift/AST/Decl.h" +#include "swift/AST/Import.h" #include "swift/AST/Module.h" #include "swift/AST/ParseRequests.h" #include "swift/AST/SourceFile.h" @@ -25,8 +26,6 @@ using namespace swift; using namespace swift::unittest; -using ModuleDecl = SourceFile::ImportedModuleDesc; - SemaTest::SemaTest() : Context(*ASTContext::get(LangOpts, TypeCheckerOpts, SearchPathOpts, ClangImporterOpts, SourceMgr, Diags)) { @@ -47,8 +46,8 @@ SemaTest::SemaTest() MainFile = new (Context) SourceFile(*module, SourceFileKind::Main, /*buffer=*/None); - auto stdlibImport = - ModuleDesc({ImportPath::Access(), stdlib}, /*options=*/{}); + AttributedImport stdlibImport{{ImportPath::Access(), stdlib}, + /*options=*/{}}; MainFile->setImports(stdlibImport); module->addFile(*MainFile); From 2d6456c03e5d52372c628995f392bfc36f607a28 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 13 Oct 2020 01:22:07 -0700 Subject: [PATCH 430/745] [ConstraintSystem] NFC: Remove obsolete `getPotentialBindings` declaration It has been related with `inferBindingsFor` but declaration was left in the header. --- include/swift/Sema/ConstraintSystem.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 17f804123a6c3..bf0b921eac731 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -4951,7 +4951,6 @@ class ConstraintSystem { Optional getPotentialBindingForRelationalConstraint(PotentialBindings &result, Constraint *constraint) const; - PotentialBindings getPotentialBindings(TypeVariableType *typeVar) const; /// Add a constraint to the constraint system. SolutionKind addConstraintImpl(ConstraintKind kind, Type first, Type second, From b0c9e69b6fd7bde871cf55570798094d51898fca Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 13 Oct 2020 09:51:31 +0200 Subject: [PATCH 431/745] SideEffectAnalysis: don't assume that arguments with trivial type cannot be pointers. Even values of trivial type can contain a Builtin.RawPointer, which can be used to read/write from/to. To compensate for the removed check, enable the escape-analysis check in MemBehavior (as it was before). This fixes a recently introduced miscompile. rdar://problem/70220876 --- lib/SILOptimizer/Analysis/MemoryBehavior.cpp | 8 ++----- .../Analysis/SideEffectAnalysis.cpp | 5 ----- test/SILOptimizer/dead_store_elim.sil | 22 +++++++++++++++++++ test/SILOptimizer/mem-behavior.sil | 22 +++++++++++++++++++ 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp index 9d3d8548e0c44..b867c18b6300a 100644 --- a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp +++ b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp @@ -215,11 +215,7 @@ class MemoryBehaviorVisitor SIMPLE_MEMBEHAVIOR_INST(CondFailInst, None) #undef SIMPLE_MEMBEHAVIOR_INST - // If we are asked to treat ref count increments as being inert, return None - // for these. - // - // FIXME: Once we separate the notion of ref counts from reading/writing - // memory this will be unnecessary. + // Incrementing reference counts doesn't have an observable memory effect. #define REFCOUNTINC_MEMBEHAVIOR_INST(Name) \ MemBehavior visit##Name(Name *I) { \ return MemBehavior::None; \ @@ -455,7 +451,7 @@ MemBehavior MemoryBehaviorVisitor::getApplyBehavior(FullApplySite AS) { behavior = MemBehavior::MayRead; // Ask escape analysis. - if (!nonEscapingAddress && !EA->canEscapeTo(V, AS)) + if (!EA->canEscapeTo(V, AS)) behavior = MemBehavior::None; } LLVM_DEBUG(llvm::dbgs() << " Found apply, returning " << behavior << '\n'); diff --git a/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp b/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp index 3b0096be8e5cb..017edb97342a4 100644 --- a/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp @@ -222,11 +222,6 @@ FunctionSideEffects::getMemBehavior(RetainObserveKind ScanKind) const { MemoryBehavior FunctionSideEffects::getArgumentBehavior(FullApplySite applySite, unsigned argIdx) { - // Rule out trivial non-address argument types. - SILType argType = applySite.getArgument(argIdx)->getType(); - if (!argType.isAddress() && argType.isTrivial(*applySite.getFunction())) - return MemoryBehavior::None; - // The overall argument effect is the combination of the argument and the // global effects. MemoryBehavior behavior = diff --git a/test/SILOptimizer/dead_store_elim.sil b/test/SILOptimizer/dead_store_elim.sil index 0864c4a97c40a..d04204b7c87fd 100644 --- a/test/SILOptimizer/dead_store_elim.sil +++ b/test/SILOptimizer/dead_store_elim.sil @@ -469,6 +469,28 @@ bb3: return %9999 : $() } +sil @read_from_raw_pointer : $@convention(thin) (Builtin.RawPointer) -> UInt8 { +bb0(%0 : $Builtin.RawPointer): + %1 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*UInt8 + %2 = load %1 : $*UInt8 + return %2 : $UInt8 +} + +// CHECK-LABEL: sil @dont_remove_store_to_escaping_allocstack_to_known_function : $@convention(thin) (UInt8) -> UInt8 +// CHECK: alloc_stack +// CHECK-NEXT: store +// CHECK: } // end sil function 'dont_remove_store_to_escaping_allocstack_to_known_function' +sil @dont_remove_store_to_escaping_allocstack_to_known_function : $@convention(thin) (UInt8) -> UInt8 { +bb0(%0 : $UInt8): + %1 = alloc_stack $UInt8 + store %0 to %1 : $*UInt8 + %3 = address_to_pointer %1 : $*UInt8 to $Builtin.RawPointer + %4 = function_ref @read_from_raw_pointer : $@convention(thin) (Builtin.RawPointer) -> UInt8 + %5 = apply %4(%3) : $@convention(thin) (Builtin.RawPointer) -> UInt8 + dealloc_stack %1 : $*UInt8 + return %5 : $UInt8 +} + sil @unknown : $@convention(thin) () -> () // CHECK-LABEL: sil @inout_is_not_aliasing : $@convention(thin) (@inout Builtin.Int32) -> () { diff --git a/test/SILOptimizer/mem-behavior.sil b/test/SILOptimizer/mem-behavior.sil index 0da680975137c..c33643578c3c7 100644 --- a/test/SILOptimizer/mem-behavior.sil +++ b/test/SILOptimizer/mem-behavior.sil @@ -35,6 +35,13 @@ bb0(%0 : $X): return %r : $() } +sil @read_from_raw_pointer : $@convention(thin) (Builtin.RawPointer) -> UInt8 { +bb0(%0 : $Builtin.RawPointer): + %1 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*UInt8 + %2 = load %1 : $*UInt8 + return %2 : $UInt8 +} + // CHECK-LABEL: @call_unknown_func // CHECK: PAIR #1. // CHECK-NEXT: %4 = apply %3(%0, %1) : $@convention(thin) (Int32, @in Int32) -> () @@ -224,6 +231,21 @@ bb0(%0 : $*Int32): return %5 : $Int32 } +// CHECK-LABEL: @escaping_allocstack_to_known_function +// CHECK: PAIR #1. +// CHECK-NEXT: %5 = apply %4(%3) : $@convention(thin) (Builtin.RawPointer) -> UInt8 // user: %7 +// CHECK-NEXT: %1 = alloc_stack $UInt8 // users: %6, %3, %2 +// CHECK-NEXT: r=1,w=0 +sil @escaping_allocstack_to_known_function : $@convention(thin) (UInt8) -> UInt8 { +bb0(%0 : $UInt8): + %1 = alloc_stack $UInt8 + store %0 to %1 : $*UInt8 + %3 = address_to_pointer %1 : $*UInt8 to $Builtin.RawPointer + %4 = function_ref @read_from_raw_pointer : $@convention(thin) (Builtin.RawPointer) -> UInt8 + %5 = apply %4(%3) : $@convention(thin) (Builtin.RawPointer) -> UInt8 + dealloc_stack %1 : $*UInt8 + return %5 : $UInt8 +} // CHECK-LABEL: @tryapply_allocstack_and_copyaddr // CHECK: PAIR #0. From 8b452ac4b5b7de8e7f18d9f755f6b629398eb944 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 13 Oct 2020 17:16:15 +0100 Subject: [PATCH 432/745] docs: convert Serialization.rst to Markdown (#34279) Converts one more doc file from `.rst` to Markdown for consistency. --- docs/README.md | 2 +- docs/{Serialization.rst => Serialization.md} | 34 ++++++------------- .../swift/Basic/SupplementaryOutputPaths.h | 2 +- 3 files changed, 13 insertions(+), 25 deletions(-) rename docs/{Serialization.rst => Serialization.md} (90%) diff --git a/docs/README.md b/docs/README.md index ceeb6bb95f34b..7e8a019c642de 100644 --- a/docs/README.md +++ b/docs/README.md @@ -120,7 +120,7 @@ documentation, please create a thread on the Swift forums under the lazy type-checking and efficient caching. - [Literals.md](/docs/Literals.md): Describes type-checking and inference specifically for literals. -- [Serialization.rst](/docs/Serialization.rst): +- [Serialization.md](/docs/Serialization.md): Gives an overview of the LLVM bitcode format used for swiftmodules. - [StableBitcode.md](/docs/StableBitcode.md): Describes how to maintain compatibility when changing the serialization diff --git a/docs/Serialization.rst b/docs/Serialization.md similarity index 90% rename from docs/Serialization.rst rename to docs/Serialization.md index 57f55ab071a2f..226d4267a271c 100644 --- a/docs/Serialization.rst +++ b/docs/Serialization.md @@ -1,8 +1,4 @@ -:orphan: - -================================= -Swift Binary Serialization Format -================================= +# Swift Binary Serialization Format The fundamental unit of distribution for Swift code is a *module.* A module contains declarations as an interface for clients to write code against. It may @@ -33,10 +29,9 @@ tied to the compiler internals to be useful for this purpose, and it is likely we'll invent a new format instead. -Why LLVM bitcode? -================= +## Why LLVM bitcode? -The `LLVM bitstream `_ format was +The [LLVM bitstream](http://llvm.org/docs/BitCodeFormat.html) format was invented as a container format for LLVM IR. It is a binary format supporting two basic structures: *blocks,* which define regions of the file, and *records,* which contain data fields that can be up to 64 bits. It has a few @@ -60,10 +55,9 @@ have most of these properties as well. But we're already linking against LLVM...might as well use it! -Versioning -========== +## Versioning -.. warning:: +#### _Warning_ This section is relevant to any forward-compatible format used for a library's public interface. However, as mentioned above this may not be @@ -101,13 +95,11 @@ requires extra work on the compiler's part to detect which features are in use; a simpler implementation would just use the latest version number supported: 1.9. -*This versioning scheme was inspired by* `Semantic Versioning -`_. *However, it is not compatible with Semantic Versioning +*This versioning scheme was inspired by* [Semantic Versioning](http://semver.org). *However, it is not compatible with Semantic Versioning because it promises* forward-compatibility *rather than* backward-compatibility. -A High-Level Tour of the Current Module Format -============================================== +## A High-Level Tour of the Current Module Format Every serialized module is represented as a single block called the "module block". The module block is made up of several other block kinds, largely for @@ -132,7 +124,7 @@ organizational purposes. - The **SIL block** contains SIL-level implementations that can be imported into a client's SILModule context. In most cases this is just a performance concern, but sometimes it affects language semantics as well, as in the case - of ``@_transparent``. The SIL block precedes the AST block because it affects + of `@_transparent`. The SIL block precedes the AST block because it affects which AST nodes get serialized. - The **SIL index block** contains tables for accessing various SIL entities by @@ -145,9 +137,7 @@ organizational purposes. Nodes are accessed by a file-unique "DeclIDs" (also covering DeclContexts) and "TypeIDs"; the two sets of IDs use separate numbering schemes. -.. note:: - - The AST block is currently referred to as the "decls block" in the source. + _note_: The AST block is currently referred to as the "decls block" in the source. - The **identifier block** contains a single blob of strings. This is intended for Identifiers---strings uniqued by the ASTContext---but can in theory @@ -160,13 +150,11 @@ organizational purposes. top-level declarations. -SIL -=== +## SIL [to be written] -Cross-reference resilience -========================== +## Cross-reference resilience [to be written] diff --git a/include/swift/Basic/SupplementaryOutputPaths.h b/include/swift/Basic/SupplementaryOutputPaths.h index 3649d4dbe7ae3..df4701081dcad 100644 --- a/include/swift/Basic/SupplementaryOutputPaths.h +++ b/include/swift/Basic/SupplementaryOutputPaths.h @@ -35,7 +35,7 @@ struct SupplementaryOutputPaths { /// /// This binary format is used to describe the interface of a module when /// imported by client source code. The swiftmodule format is described in - /// docs/Serialization.rst. + /// docs/Serialization.md. /// /// \sa swift::serialize std::string ModuleOutputPath; From a09ae489994d1d3f9e7879fc1493d97e28e3de5d Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Mon, 12 Oct 2020 15:48:30 -0700 Subject: [PATCH 433/745] [Dependency Scanner] Always add NonPathCommandLine arguments from Clang scan-deps result When building a set of command-line options required to build a Clang module, also add `NonPathCommandLine` from Clang's `ModuleDeps`. These flags are mandatory in order to build modules discovered by the scanner. Resolves rdar://70212660 --- lib/ClangImporter/ClangModuleDependencyScanner.cpp | 9 +++++++++ test/ScanDependencies/Inputs/CHeaders/module.modulemap | 2 +- test/ScanDependencies/module_deps.swift | 4 ++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/ClangImporter/ClangModuleDependencyScanner.cpp b/lib/ClangImporter/ClangModuleDependencyScanner.cpp index 3dd1673a16768..b0919422f3f9d 100644 --- a/lib/ClangImporter/ClangModuleDependencyScanner.cpp +++ b/lib/ClangImporter/ClangModuleDependencyScanner.cpp @@ -245,6 +245,15 @@ void ClangImporter::recordModuleDependencies( } } + // Add all args the non-path arguments required to be passed in, according + // to the Clang scanner + for (const auto &clangArg : clangModuleDep.NonPathCommandLine) { + swiftArgs.push_back("-Xcc"); + swiftArgs.push_back("-Xclang"); + swiftArgs.push_back("-Xcc"); + swiftArgs.push_back(clangArg); + } + // Swift frontend action: -emit-pcm swiftArgs.push_back("-emit-pcm"); swiftArgs.push_back("-module-name"); diff --git a/test/ScanDependencies/Inputs/CHeaders/module.modulemap b/test/ScanDependencies/Inputs/CHeaders/module.modulemap index 95fc7fef133c2..105f5e2b7bafe 100644 --- a/test/ScanDependencies/Inputs/CHeaders/module.modulemap +++ b/test/ScanDependencies/Inputs/CHeaders/module.modulemap @@ -8,7 +8,7 @@ module B { export * } -module C { +module C [system] { header "C.h" export * } diff --git a/test/ScanDependencies/module_deps.swift b/test/ScanDependencies/module_deps.swift index dc48ba5e0998d..aed7dbb82bb15 100644 --- a/test/ScanDependencies/module_deps.swift +++ b/test/ScanDependencies/module_deps.swift @@ -105,6 +105,10 @@ import SubE // CHECK-NEXT: "-only-use-extra-clang-opts" // CHECK-NEXT: "-Xcc" // CHECK-NEXT: "clang" +// CHECK: "-fsystem-module", +// CHECK-NEXT: "-emit-pcm", +// CHECK-NEXT: "-module-name", +// CHECK-NEXT: "C" /// --------Swift module E // CHECK: "swift": "E" From 4ab3cb15323e1a01fe45496109c6fd506fd6d6b7 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 13 Oct 2020 12:57:41 -0700 Subject: [PATCH 434/745] Revert "Implements SR-11580" This reverts commit c311e451951b28afacebae69107cef28e4af9e44. --- utils/gyb_syntax_support/ExprNodes.py | 6 ++---- utils/gyb_syntax_support/Node.py | 5 +---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/utils/gyb_syntax_support/ExprNodes.py b/utils/gyb_syntax_support/ExprNodes.py index d47a04b5dfc37..59801e8e98244 100644 --- a/utils/gyb_syntax_support/ExprNodes.py +++ b/utils/gyb_syntax_support/ExprNodes.py @@ -208,8 +208,7 @@ Node('FloatLiteralExpr', kind='Expr', children=[ Child('FloatingDigits', kind='FloatingLiteralToken'), - ], - must_uphold_invariant=True), + ]), Node('TupleExpr', kind='Expr', traits=['Parenthesized'], @@ -283,8 +282,7 @@ Node('IntegerLiteralExpr', kind='Expr', children=[ Child('Digits', kind='IntegerLiteralToken'), - ], - must_uphold_invariant=True), + ]), # true or false Node('BooleanLiteralExpr', kind='Expr', diff --git a/utils/gyb_syntax_support/Node.py b/utils/gyb_syntax_support/Node.py index 545c1d0855130..eb3899a07e378 100644 --- a/utils/gyb_syntax_support/Node.py +++ b/utils/gyb_syntax_support/Node.py @@ -19,8 +19,7 @@ class Node(object): def __init__(self, name, description=None, kind=None, traits=None, children=None, element=None, element_name=None, - element_choices=None, omit_when_empty=False, - must_uphold_invariant=False): + element_choices=None, omit_when_empty=False): self.syntax_kind = name self.swift_syntax_kind = lowercase_first_word(name) self.name = kind_to_type(self.syntax_kind) @@ -40,8 +39,6 @@ def __init__(self, name, description=None, kind=None, traits=None, self.omit_when_empty = omit_when_empty self.collection_element = element or "" - self.must_uphold_invariant = must_uphold_invariant - # For SyntaxCollections make sure that the element_name is set. assert(not self.is_syntax_collection() or element_name or (element and element != 'Syntax')) From fde1af756d336cb4c22067c99fe2c83a3b026012 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 13 Oct 2020 12:50:21 -0700 Subject: [PATCH 435/745] [unittest/Sema] Use default target triple to fix Windows build --- unittests/Sema/SemaFixture.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/Sema/SemaFixture.h b/unittests/Sema/SemaFixture.h index eca5039a10c45..adb0ab40d5039 100644 --- a/unittests/Sema/SemaFixture.h +++ b/unittests/Sema/SemaFixture.h @@ -38,7 +38,7 @@ class SemaTestBase : public ::testing::Test { DiagnosticEngine Diags; SemaTestBase() : Diags(SourceMgr) { - LangOpts.Target = llvm::Triple(llvm::sys::getProcessTriple()); + LangOpts.Target = llvm::Triple(llvm::sys::getDefaultTargetTriple()); llvm::SmallString<128> libDir(SWIFTLIB_DIR); llvm::sys::path::append(libDir, getPlatformNameForTriple(LangOpts.Target)); From 4351bd374af84b997525078c723dd05971ae9cd5 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 13 Oct 2020 14:11:22 -0700 Subject: [PATCH 436/745] Use The Location of the Pattern Binding in Codable Fixit The code here used to use the location of the nearest place to insert attributes, which makes no sense. Use the pattern binding's location instead to ensure that we actually replace the 'let' part of the pattern every time. rdar://69971194 --- lib/Sema/DerivedConformanceCodable.cpp | 6 ++-- .../special/coding/immutable_property.swift | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 test/decl/protocol/special/coding/immutable_property.swift diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index e5406758665f4..75fbdaeb7555a 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -799,8 +799,10 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) { diag::decodable_property_init_or_codingkeys_explicit, varDecl->getName()); } - varDecl->diagnose(diag::decodable_make_property_mutable) - .fixItReplace(varDecl->getAttributeInsertionLoc(true), "var"); + if (auto *PBD = varDecl->getParentPatternBinding()) { + varDecl->diagnose(diag::decodable_make_property_mutable) + .fixItReplace(PBD->getLoc(), "var"); + } continue; } diff --git a/test/decl/protocol/special/coding/immutable_property.swift b/test/decl/protocol/special/coding/immutable_property.swift new file mode 100644 index 0000000000000..efa1e58b8bd3b --- /dev/null +++ b/test/decl/protocol/special/coding/immutable_property.swift @@ -0,0 +1,28 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +struct Foo : Codable { + let x1: String = "" + // expected-warning@-1 {{immutable property will not be decoded because it is declared with an initial value which cannot be overwritten}} + // expected-note@-2 {{set the initial value via the initializer or explicitly define a CodingKeys enum including a 'x1' case to silence this warning}} + // expected-note@-3 {{make the property mutable instead}}{{3-6=var}} + + public let x2: String = "" + // expected-warning@-1 {{immutable property will not be decoded because it is declared with an initial value which cannot be overwritten}} + // expected-note@-2 {{set the initial value via the initializer or explicitly define a CodingKeys enum including a 'x2' case to silence this warning}} + // expected-note@-3 {{make the property mutable instead}}{{10-13=var}} + + internal let x3: String = "" + // expected-warning@-1 {{immutable property will not be decoded because it is declared with an initial value which cannot be overwritten}} + // expected-note@-2 {{set the initial value via the initializer or explicitly define a CodingKeys enum including a 'x3' case to silence this warning}} + // expected-note@-3 {{make the property mutable instead}}{{12-15=var}} + + fileprivate let x4: String = "" + // expected-warning@-1 {{immutable property will not be decoded because it is declared with an initial value which cannot be overwritten}} + // expected-note@-2 {{set the initial value via the initializer or explicitly define a CodingKeys enum including a 'x4' case to silence this warning}} + // expected-note@-3 {{make the property mutable instead}}{{15-18=var}} + + private let x5: String = "" + // expected-warning@-1 {{immutable property will not be decoded because it is declared with an initial value which cannot be overwritten}} + // expected-note@-2 {{set the initial value via the initializer or explicitly define a CodingKeys enum including a 'x5' case to silence this warning}} + // expected-note@-3 {{make the property mutable instead}}{{11-14=var}} +} From 8307a8d58e7ec6c5396b8e27ee4170a2f9fb844e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Tue, 13 Oct 2020 14:47:31 -0700 Subject: [PATCH 437/745] [Sema] Don't require availability for implicit decls Suppress warnings in synthesized code. The parent decls should already show a warning. require_explicit_availability.StructWithImplicitMembers:2:16: warning: public declarations should have an availability attribute when building with -require-explicit-availability public var hashValue: Int { get } ^ --- lib/Sema/TypeCheckAvailability.cpp | 5 +++-- test/attr/require_explicit_availability.swift | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index e072bf594ba84..4acbe673bb823 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -2900,9 +2900,10 @@ static bool declNeedsExplicitAvailability(const Decl *decl) { return false; } - // Skip functions emitted into clients or SPI. + // Skip functions emitted into clients, SPI or implicit. if (decl->getAttrs().hasAttribute() || - decl->isSPI()) + decl->isSPI() || + decl->isImplicit()) return false; // Warn on decls without an introduction version. diff --git a/test/attr/require_explicit_availability.swift b/test/attr/require_explicit_availability.swift index 902186b3384c0..7fa4cbf49ea77 100644 --- a/test/attr/require_explicit_availability.swift +++ b/test/attr/require_explicit_availability.swift @@ -164,3 +164,11 @@ extension SomeClass { // expected-warning {{public declarations should have an a set(newValue) { } } } + +@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, macCatalyst 13.0, *) +public struct StructWithImplicitMembers { } + +extension StructWithImplicitMembers: Hashable { } +// expected-note @-1 {{add @available attribute to enclosing extension}} +// expected-warning @-2 {{public declarations should have an availability attribute when building with -require-explicit-availability}} +// expected-error @-3 {{'StructWithImplicitMembers' is only available in macOS 10.15 or newer}} From f089ba9464fc164f6dc4ce01dafb1ec2f545912c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 13 Oct 2020 15:17:13 -0700 Subject: [PATCH 438/745] [Concurrency] Propagation of actor constraints. Implement propagation rules for global actor constraints, which can come from: * Enclosing extension or type * Superclass of a class * Overridden declaration * Requirement witnessed by a declaration * Storage declaration for an accessor --- include/swift/AST/ActorIsolation.h | 2 + lib/Sema/TypeCheckConcurrency.cpp | 250 +++++++++++++++--- test/Concurrency/global_actor_inference.swift | 90 +++++++ .../actor/global_actor_conformance.swift | 5 +- 4 files changed, 305 insertions(+), 42 deletions(-) create mode 100644 test/Concurrency/global_actor_inference.swift diff --git a/include/swift/AST/ActorIsolation.h b/include/swift/AST/ActorIsolation.h index 2aec57cdf20d4..78961df373191 100644 --- a/include/swift/AST/ActorIsolation.h +++ b/include/swift/AST/ActorIsolation.h @@ -84,6 +84,8 @@ class ActorIsolation { operator Kind() const { return getKind(); } + bool isUnspecified() const { return kind == Unspecified; } + ClassDecl *getActor() const { assert(getKind() == ActorInstance); return actor; diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 87afeb4734f04..4a7ba10daea93 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -971,65 +971,237 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { const_cast(expr)->walk(walker); } -ActorIsolation ActorIsolationRequest::evaluate( - Evaluator &evaluator, ValueDecl *value) const { +/// Determine actor isolation solely from attributes. +/// +/// \returns the actor isolation determined from attributes alone (with no +/// inference rules). Returns \c None if there were no attributes on this +/// declaration. +static Optional getIsolationFromAttributes(Decl *decl) { // Look up attributes on the declaration that can affect its actor isolation. // If any of them are present, use that attribute. - auto independentAttr = value->getAttrs().getAttribute(); - auto globalActorAttr = value->getGlobalActorAttr(); + auto independentAttr = decl->getAttrs().getAttribute(); + auto globalActorAttr = decl->getGlobalActorAttr(); unsigned numIsolationAttrs = (independentAttr ? 1 : 0) + (globalActorAttr ? 1 : 0); - if (numIsolationAttrs > 0) { - // Only one such attribute is valid. - if (numIsolationAttrs > 1) { - value->diagnose( - diag::actor_isolation_multiple_attr, value->getDescriptiveKind(), - value->getName(), independentAttr->getAttrName(), - globalActorAttr->second->getName().str()) - .highlight(independentAttr->getRangeWithAt()) - .highlight(globalActorAttr->first->getRangeWithAt()); - } + if (numIsolationAttrs == 0) + return None; - // If the declaration is explicitly marked @actorIndependent, report it as - // independent. - if (independentAttr) { - return ActorIsolation::forIndependent(); + // Only one such attribute is valid. + if (numIsolationAttrs > 1) { + DeclName name; + if (auto value = dyn_cast(decl)) { + name = value->getName(); + } else if (auto ext = dyn_cast(decl)) { + if (auto selfTypeDecl = ext->getSelfNominalTypeDecl()) + name = selfTypeDecl->getName(); } - // If the declaration is marked with a global actor, report it as being - // part of that global actor. - if (globalActorAttr) { - TypeResolutionOptions options(TypeResolverContext::None); - TypeResolution resolver = TypeResolution::forInterface( - value->getInnermostDeclContext(), options, nullptr); - Type globalActorType = resolver.resolveType( - globalActorAttr->first->getTypeRepr(), nullptr); - if (!globalActorType || globalActorType->hasError()) - return ActorIsolation::forUnspecified(); + decl->diagnose( + diag::actor_isolation_multiple_attr, decl->getDescriptiveKind(), + name, independentAttr->getAttrName(), + globalActorAttr->second->getName().str()) + .highlight(independentAttr->getRangeWithAt()) + .highlight(globalActorAttr->first->getRangeWithAt()); + } + + // If the declaration is explicitly marked @actorIndependent, report it as + // independent. + if (independentAttr) { + return ActorIsolation::forIndependent(); + } + + // If the declaration is marked with a global actor, report it as being + // part of that global actor. + if (globalActorAttr) { + TypeResolutionOptions options(TypeResolverContext::None); + TypeResolution resolver = TypeResolution::forInterface( + decl->getInnermostDeclContext(), options, nullptr); + Type globalActorType = resolver.resolveType( + globalActorAttr->first->getTypeRepr(), nullptr); + if (!globalActorType || globalActorType->hasError()) + return ActorIsolation::forUnspecified(); + + return ActorIsolation::forGlobalActor(globalActorType); + } + + llvm_unreachable("Forgot about an attribute?"); +} + +/// Infer isolation from witnessed protocol requirements. +static Optional getIsolationFromWitnessedRequirements( + ValueDecl *value) { + auto dc = value->getDeclContext(); + auto idc = dyn_cast_or_null(dc->getAsDecl()); + if (!idc) + return None; + + // Walk through each of the conformances in this context, collecting any + // requirements that have actor isolation. + auto conformances = evaluateOrDefault( + dc->getASTContext().evaluator, + LookupAllConformancesInContextRequest{idc}, { }); + using IsolatedRequirement = + std::tuple; + SmallVector isolatedRequirements; + for (auto conformance : conformances) { + auto protocol = conformance->getProtocol(); + for (auto found : protocol->lookupDirect(value->getName())) { + if (!isa(found->getDeclContext())) + continue; + + auto requirement = dyn_cast(found); + if (!requirement || isa(requirement)) + continue; + + auto requirementIsolation = getActorIsolation(requirement); + if (requirementIsolation.isUnspecified()) + continue; + + auto witness = conformance->getWitnessDecl(requirement); + if (witness != value) + continue; - return ActorIsolation::forGlobalActor(globalActorType); + isolatedRequirements.push_back( + IsolatedRequirement{conformance, requirementIsolation, requirement}); } + } + + // Filter out duplicate actors. + SmallPtrSet globalActorTypes; + bool sawActorIndependent = false; + isolatedRequirements.erase( + std::remove_if(isolatedRequirements.begin(), isolatedRequirements.end(), + [&](IsolatedRequirement &isolated) { + auto isolation = std::get<1>(isolated); + switch (isolation) { + case ActorIsolation::ActorInstance: + llvm_unreachable("protocol requirements cannot be actor instances"); + + case ActorIsolation::Independent: + // We only need one @actorIndependent. + if (sawActorIndependent) + return true; + + sawActorIndependent = true; + return false; + + case ActorIsolation::Unspecified: + return true; + + case ActorIsolation::GlobalActor: { + // Substitute into the global actor type. + auto conformance = std::get<0>(isolated); + auto requirementSubs = SubstitutionMap::getProtocolSubstitutions( + conformance->getProtocol(), dc->getSelfTypeInContext(), + ProtocolConformanceRef(conformance)); + Type globalActor = isolation.getGlobalActor().subst(requirementSubs); + if (!globalActorTypes.insert(globalActor->getCanonicalType()).second) + return true; + + // Update the global actor type, now that we've done this substitution. + std::get<1>(isolated) = ActorIsolation::forGlobalActor(globalActor); + return false; + } + } + }), + isolatedRequirements.end()); + + if (isolatedRequirements.size() != 1) + return None; + + return std::get<1>(isolatedRequirements.front()); +} + +ActorIsolation ActorIsolationRequest::evaluate( + Evaluator &evaluator, ValueDecl *value) const { + // If this declaration has one of the actor isolation attributes, report + // that. + if (auto isolationFromAttr = getIsolationFromAttributes(value)) { + return *isolationFromAttr; + } + + // Determine the default isolation for this declaration, which may still be + // overridden by other inference rules. + ActorIsolation defaultIsolation = ActorIsolation::forUnspecified(); + + // Check for instance members of actor classes, which are part of + // actor-isolated state. + auto classDecl = value->getDeclContext()->getSelfClassDecl(); + if (classDecl && classDecl->isActor() && value->isInstanceMember()) { + defaultIsolation = ActorIsolation::forActorInstance(classDecl); + } - llvm_unreachable("Forgot about an attribute?"); + // Disable inference of actor attributes outside of normal Swift source files. + if (auto sourceFile = value->getDeclContext()->getParentSourceFile()) { + switch (sourceFile->Kind) { + case SourceFileKind::Interface: + case SourceFileKind::SIL: + return defaultIsolation; + + case SourceFileKind::Library: + case SourceFileKind::Main: + // Attempt inference below. + break; + } + } else { + return defaultIsolation; } + // Function used when returning an inferred isolation. + auto inferredIsolation = [&](ActorIsolation inferred) { + return inferred; + }; + // If the declaration overrides another declaration, it must have the same // actor isolation. if (auto overriddenValue = value->getOverriddenDecl()) { if (auto isolation = getActorIsolation(overriddenValue)) - return isolation; + return inferredIsolation(isolation); } - // Check for instance members of actor classes, which are part of - // actor-isolated state. - auto classDecl = value->getDeclContext()->getSelfClassDecl(); - if (classDecl && classDecl->isActor() && value->isInstanceMember()) { - // Part of the actor's isolated state. - return ActorIsolation::forActorInstance(classDecl); + // If the declaration witnesses a protocol requirement that is isolated, + // use that. + if (auto witnessedIsolation = getIsolationFromWitnessedRequirements(value)) { + return inferredIsolation(*witnessedIsolation); + } + + // If the declaration is a class with a superclass that has specified + // isolation, use that. + if (auto classDecl = dyn_cast(value)) { + if (auto superclassDecl = classDecl->getSuperclassDecl()) { + auto superclassIsolation = getActorIsolation(superclassDecl); + if (!superclassIsolation.isUnspecified()) + return inferredIsolation(superclassIsolation); + } + } + + // If this is an accessor, use the actor isolation of its storage + // declaration. + if (auto accessor = dyn_cast(value)) { + auto storageIsolation = getActorIsolation(accessor->getStorage()); + if (!storageIsolation.isUnspecified()) + return inferredIsolation(storageIsolation); + } + + // If the declaration is in an extension that has one of the isolation + // attributes, use that. + if (auto ext = dyn_cast(value->getDeclContext())) { + if (auto isolationFromAttr = getIsolationFromAttributes(ext)) { + return inferredIsolation(*isolationFromAttr); + } + } + + // If the declaration is in a nominal type (or extension thereof) that + // has isolation, use that. + if (auto selfTypeDecl = value->getDeclContext()->getSelfNominalTypeDecl()) { + auto selfTypeIsolation = getActorIsolation(selfTypeDecl); + if (!selfTypeIsolation.isUnspecified()) { + return inferredIsolation(selfTypeIsolation); + } } - // Everything else is unspecified. - return ActorIsolation::forUnspecified(); + // Default isolation for this member. + return defaultIsolation; } ActorIsolation swift::getActorIsolation(ValueDecl *value) { diff --git a/test/Concurrency/global_actor_inference.swift b/test/Concurrency/global_actor_inference.swift new file mode 100644 index 0000000000000..8fcbe36011835 --- /dev/null +++ b/test/Concurrency/global_actor_inference.swift @@ -0,0 +1,90 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// REQUIRES: concurrency + +import _Concurrency + +actor class SomeActor { } + +@globalActor +struct SomeGlobalActor { + static let shared = SomeActor() +} + +@globalActor +struct OtherGlobalActor { + static let shared = SomeActor() +} + +// ---------------------------------------------------------------------- +// Global actor inference for protocols +// ---------------------------------------------------------------------- + +@SomeGlobalActor +protocol P1 { + func method() +} + +protocol P2 { + @SomeGlobalActor func method1() + func method2() +} + + +class C1: P1 { + func method() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} + + @OtherGlobalActor func testMethod() { + method() // expected-error{{instance method 'method()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}} + } +} + +class C2: P2 { + func method1() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} + func method2() { } + + @OtherGlobalActor func testMethod() { + method1() // expected-error{{instance method 'method1()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}} + method2() // okay + } +} + +// ---------------------------------------------------------------------- +// Global actor inference for classes and extensions +// ---------------------------------------------------------------------- +@SomeGlobalActor class C3 { + func method1() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} +} + +extension C3 { + func method2() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} +} + +class C4: C3 { + func method3() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} +} + +extension C4 { + func method4() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} +} + +class C5 { + func method1() { } +} + +@SomeGlobalActor extension C5 { + func method2() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} +} + +@OtherGlobalActor func testGlobalActorInference(c3: C3, c4: C4, c5: C5) { + // Propagation via class annotation + c3.method1() // expected-error{{instance method 'method1()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}} + c3.method2() // expected-error{{instance method 'method2()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}} + + // Propagation via subclassing + c4.method3() // expected-error{{instance method 'method3()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}} + c4.method4() // expected-error{{instance method 'method4()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}} + + // Propagation in an extension. + c5.method1() // OK: no propagation + c5.method2() // expected-error{{instance method 'method2()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}} +} diff --git a/test/decl/class/actor/global_actor_conformance.swift b/test/decl/class/actor/global_actor_conformance.swift index 8144892e12769..9bbdf4196a074 100644 --- a/test/decl/class/actor/global_actor_conformance.swift +++ b/test/decl/class/actor/global_actor_conformance.swift @@ -18,7 +18,7 @@ struct GenericGlobalActor { protocol P1 { associatedtype Assoc - @GlobalActor func method1() // expected-note{{declared here}} + @GlobalActor func method1() @GenericGlobalActor func method2() // expected-note{{declared here}} @GenericGlobalActor func method3() func method4() // expected-note{{declared here}} @@ -33,8 +33,7 @@ protocol P2 { class C1 : P1, P2 { typealias Assoc = String - // FIXME: This will be inferred - func method1() { } // expected-error{{instance method 'method1()' must be isolated to the global actor 'GlobalActor' to satisfy corresponding requirement from protocol 'P1'}}{{3-3=@GlobalActor}} + func method1() { } @GenericGlobalActor func method2() { } // expected-error{{instance method 'method2()' isolated to global actor 'GenericGlobalActor' can not satisfy corresponding requirement from protocol 'P1' isolated to global actor 'GenericGlobalActor'}} @GenericGlobalActorfunc method3() { } From d793878923e04ffddf68de3ff8bbbeff5318f1f1 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 13 Oct 2020 15:28:10 -0700 Subject: [PATCH 439/745] Schedule merge-modules When modulewrap Job is in the Queue In order to unblock the SwiftWASM project, which relies on an incremental build of the Swift driver that relies on the merge-modules job always being run. The situation appears to be something like this: 1) An incremental build is run 2) Temporary swiftmodule outputs are laid down 3) merge-modules is skipped 4) modulewrap is run anyways and reads the empty temp file We should fix this by skipping modulewrap if we can skip merge-modules. But for now, be conservative and fall back to the status quo behavior of always running merge-modules whenever we encounter a modulewrap job. --- lib/Driver/Compilation.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index 31dca8bc17dbd..6bede8ee5dec6 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -908,10 +908,18 @@ namespace driver { return everyIncrementalJob; }; + bool sawModuleWrapJob = false; const Job *mergeModulesJob = nullptr; CommandSet jobsToSchedule; CommandSet initialCascadingCommands; for (const Job *cmd : Comp.getJobs()) { + // A modulewrap job consumes the output of merge-modules. If it is + // in the queue, we must run merge-modules or empty temporary files + // will be consumed by the job instead. + // FIXME: We should be able to ditch this if we compare the timestamps + // of the temporary file to the build record, if it exists. + sawModuleWrapJob |= isa(cmd->getSource()); + // Skip jobs that have no associated incremental info. if (!isa(cmd->getSource())) { continue; @@ -949,7 +957,7 @@ namespace driver { // structure of the resulting module. Additionally, the initial scheduling // predicate above is only aware of intra-module changes. External // dependencies changing *must* cause merge-modules to be scheduled. - if (!jobsToSchedule.empty() && mergeModulesJob) { + if ((!jobsToSchedule.empty() || sawModuleWrapJob) && mergeModulesJob) { jobsToSchedule.insert(mergeModulesJob); } return jobsToSchedule; From eff6b66906f094026afe1a61dae44d4eeba2709b Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Wed, 7 Oct 2020 11:42:31 -0700 Subject: [PATCH 440/745] Add hasOwnership assertion in SILBuilder while creating unchecked_value_cast --- include/swift/SIL/SILBuilder.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 83d93da9059c1..484f6df02e79a 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -1125,6 +1125,7 @@ class SILBuilder { UncheckedValueCastInst *createUncheckedValueCast(SILLocation Loc, SILValue Op, SILType Ty) { + assert(hasOwnership()); return insert(UncheckedValueCastInst::create( getSILDebugLocation(Loc), Op, Ty, getFunction(), C.OpenedArchetypes)); } From a79417f48daeaa985100cafc30667a35015374b1 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Tue, 13 Oct 2020 18:22:04 -0700 Subject: [PATCH 441/745] Tweak formatting of _Concurrency module CMakeLists.txt Helps fix rdar://66414410. --- stdlib/public/Concurrency/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index 92db5f25e1d0e..a62a63462e6b3 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -29,4 +29,5 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} -parse-stdlib LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - INSTALL_IN_COMPONENT stdlib) + INSTALL_IN_COMPONENT stdlib +) From b02dc2b25bc2c9c258370265c54d744c178a101c Mon Sep 17 00:00:00 2001 From: Dave Lee Date: Tue, 13 Oct 2020 21:12:58 -0700 Subject: [PATCH 442/745] LLDB_PATH_TO_SWIFT_SOURCE too --- utils/build-script-impl | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index d805f5eeb5285..924e18c6a5e18 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -2030,7 +2030,6 @@ for host in "${ALL_HOSTS[@]}"; do -DLLDB_ENABLE_PYTHON=ON -DLLDB_ENABLE_LZMA=OFF -DLLDB_ENABLE_LUA=OFF - -DLLDB_PATH_TO_SWIFT_SOURCE:PATH="${SWIFT_SOURCE_DIR}" -DLLDB_INCLUDE_TESTS:BOOL=$(false_true ${BUILD_TOOLCHAIN_ONLY}) -DLLDB_TEST_USER_ARGS="${DOTEST_ARGS}" ) From 52bbe11161f908d4c58cbdd2a423c68b9688b191 Mon Sep 17 00:00:00 2001 From: Dave Lee Date: Fri, 9 Oct 2020 17:19:32 -0700 Subject: [PATCH 443/745] [build] Remove dash-dash from build-presets.ini (NFC) --- utils/build-presets.ini | 128 ---------------------------------------- 1 file changed, 128 deletions(-) diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 7d9a5c264c863..dc3826f228a96 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -14,8 +14,6 @@ # Buildbots for Darwin OSes #===------------------------------------------------------------------------===# [preset: mixin_buildbot_install_components] -dash-dash - swift-install-components=compiler;clang-builtin-headers;stdlib;sdk-overlay;parser-lib;editor-integration;tools;toolchain-tools;testsuite-tools;sourcekit-xpc-service;swift-remote-mirror;swift-remote-mirror-headers; [preset: mixin_buildbot_install_components_with_clang] @@ -32,8 +30,6 @@ validation-test lit-args=-v compiler-vendor=apple -dash-dash - verbose-build build-ninja @@ -60,8 +56,6 @@ mixin-preset= release assertions -dash-dash - swift-stdlib-build-type=RelWithDebInfo swift-stdlib-enable-assertions=false @@ -91,8 +85,6 @@ assertions test-optimized test-optimize-for-size -dash-dash - swift-stdlib-build-type=RelWithDebInfo swift-stdlib-enable-assertions=true @@ -110,8 +102,6 @@ assertions # Also run tests in optimized modes. test-optimized -dash-dash - swift-stdlib-build-type=RelWithDebInfo swift-stdlib-enable-assertions=true @@ -130,8 +120,6 @@ assertions test-optimized test-optimize-for-size -dash-dash - swift-stdlib-build-type=RelWithDebInfo swift-stdlib-enable-assertions=false @@ -144,8 +132,6 @@ mixin-preset= release no-assertions -dash-dash - swift-stdlib-build-type=RelWithDebInfo swift-stdlib-enable-assertions=false @@ -158,8 +144,6 @@ mixin-preset= release assertions -dash-dash - swift-stdlib-build-type=Debug swift-stdlib-enable-assertions=true @@ -168,8 +152,6 @@ swift-stdlib-enable-assertions=true mixin-preset= mixin_buildbot_tools_RA_stdlib_DA -dash-dash - # Don't run host tests for iOS, tvOS and watchOS platforms to make the build # faster. skip-test-ios-host @@ -192,8 +174,6 @@ skip-test-watchos-simulator mixin-preset= mixin_buildbot_tools_RA_stdlib_RD -dash-dash - # Don't run host tests for iOS, tvOS and watchOS platforms to make the build # faster. skip-test-ios-host @@ -216,8 +196,6 @@ skip-test-watchos-simulator mixin-preset= mixin_buildbot_tools_R_stdlib_RD -dash-dash - # Don't run host tests for iOS, tvOS and watchOS platforms to make the build # faster. skip-test-ios-host @@ -241,8 +219,6 @@ skip-test-watchos-simulator mixin-preset= mixin_buildbot_tools_RA_stdlib_RDA -dash-dash - # Don't run host tests for iOS, tvOS and watchOS platforms to make the build # faster. skip-test-ios-host @@ -300,8 +276,6 @@ skip-test-watchos-host # Build LLDB lldb -dash-dash - # Use the system debugserver to run the lldb swift tests lldb-no-debugserver lldb-use-system-debugserver @@ -320,8 +294,6 @@ lit-args=-v --time-tests compiler-vendor=apple -dash-dash - # On buildbots, always force a reconfiguration to make sure we pick up changes # in the build-script and build-presets.ini. reconfigure @@ -369,8 +341,6 @@ swiftevolve # Build Playground support playgroundsupport -dash-dash - # Only run OS X tests to make the build cycle faster. # We still build the iOS standard library though -- it is free given the # parallelism. @@ -390,7 +360,6 @@ mixin-preset=buildbot_incremental,tools=RA,stdlib=RA build-subdir=buildbot_incremental_xcode xcode -dash-dash # We do not support building cross compiled stdlibs on OS X with Xcode. So only # build the OS X SDK. skip-build-ios @@ -421,8 +390,6 @@ xctest build-swift-stdlib-unittest-extra -dash-dash - # This preset is meant to test XCTest, not Swift or Foundation. We don't # want stochastic failures in those test suites to prevent XCTest tests from # being run. @@ -440,8 +407,6 @@ assertions xctest test -dash-dash - skip-test-cmark skip-test-swift skip-test-foundation @@ -458,8 +423,6 @@ assertions build-swift-stdlib-unittest-extra -dash-dash - # FIXME: Swift/ASan does not support iOS yet. skip-build-ios skip-test-ios @@ -486,8 +449,6 @@ enable-tsan build-swift-stdlib-unittest-extra -dash-dash - skip-build-ios skip-test-ios skip-build-tvos @@ -506,8 +467,6 @@ assertions build-swift-stdlib-unittest-extra -dash-dash - # FIXME: Swift/UBSan does not support mac os x yet. skip-build-osx skip-test-osx @@ -537,8 +496,6 @@ assertions build-swift-stdlib-unittest-extra -dash-dash - # FIXME: Swift/ASan does not support iOS yet. skip-build-ios skip-test-ios @@ -576,8 +533,6 @@ llvm-targets-to-build=X86;ARM;AArch64;PowerPC # Set the vendor to apple compiler-vendor=apple -dash-dash - # Always reconfigure cmake reconfigure @@ -627,8 +582,6 @@ build-subdir=buildbot_incremental lto -dash-dash - [preset: buildbot_incremental,tools=RA,llvm-only] build-subdir=buildbot_incremental_llvmonly @@ -640,8 +593,6 @@ lit-args=-v compiler-vendor=apple -dash-dash - llvm-include-tests reconfigure verbose-build @@ -669,8 +620,6 @@ release assertions compiler-vendor=apple -dash-dash - # On buildbots, always force a reconfiguration to make sure we pick up changes # in the build-script and build-presets.ini. reconfigure @@ -704,8 +653,6 @@ mixin-preset=buildbot_incremental_asan,tools=RDA,stdlib=RDA [preset: buildbot_incremental_leaks] compiler-vendor=apple -dash-dash - # Disable ios. These builders are x86 only. skip-ios skip-tvos @@ -729,8 +676,6 @@ build-subdir=buildbot_incremental_leaks_RA_R release assertions -dash-dash - # We want our stdlib to not have assertions. swift-stdlib-enable-assertions=false @@ -756,8 +701,6 @@ assertions # swift-assertions # ... but our tests are expecting assertions to be either on or off everywhere. -dash-dash - # AST verifier slows down the compiler significantly. swift-enable-ast-verifier=0 @@ -786,8 +729,6 @@ xctest libicu libcxx -dash-dash - build-ninja install-llvm install-swift @@ -834,8 +775,6 @@ indexstore-db sourcekit-lsp lit-args=-v --time-tests -dash-dash - # rdar://problem/31454823 lldb-test-swift-only @@ -844,8 +783,6 @@ install-libdispatch reconfigure [preset: mixin_buildbot_linux,no_test] -dash-dash - skip-test-cmark skip-test-lldb skip-test-swift @@ -884,8 +821,6 @@ build-ninja libicu libcxx -dash-dash - android android-ndk=%(ndk_path)s android-api-level=21 @@ -923,8 +858,6 @@ reconfigure [preset: buildbot_linux_crosscompile_android,tools=RA,stdlib=RD,build,aarch64] mixin-preset=buildbot_linux_crosscompile_android,tools=RA,stdlib=RD,build -dash-dash - android-arch=aarch64 # Ubuntu 18.04 preset for backwards compat and future customizations. @@ -984,8 +917,6 @@ indexstore-db sourcekit-lsp lit-args=-v -dash-dash - install-foundation install-libdispatch reconfigure @@ -1018,8 +949,6 @@ xctest build-subdir=buildbot_linux -dash-dash - install-llvm install-swift install-lldb @@ -1059,8 +988,6 @@ test validation-test lit-args=-v -dash-dash - build-ninja reconfigure @@ -1081,8 +1008,6 @@ libdispatch swiftsyntax indexstore-db sourcekit-lsp -dash-dash - install-llvm install-swift install-llbuild @@ -1113,8 +1038,6 @@ long-test=0 stress-test=0 test-optimized=0 -dash-dash - skip-build-swift skip-build-cmark llvm-include-tests @@ -1151,8 +1074,6 @@ release-debuginfo assertions enable-lsan -dash-dash - build-ninja reconfigure @@ -1165,8 +1086,6 @@ enable-lsan debug-swift-stdlib debug-libdispatch -dash-dash - build-ninja reconfigure @@ -1210,8 +1129,6 @@ swiftsyntax-verify-generated-files release-debuginfo compiler-vendor=apple -dash-dash - # Cross compile for Apple Silicon cross-compile-hosts=macosx-arm64 @@ -1293,8 +1210,6 @@ validation-test long-test stress-test -dash-dash - lldb-test-swift-only # Path to the .tar.gz package we would create. @@ -1322,8 +1237,6 @@ mixin-preset= mixin_osx_package_base mixin_osx_package_test -dash-dash - no-assertions [preset: buildbot_osx_package,no_assertions,use_os_runtime] @@ -1345,8 +1258,6 @@ mixin-preset= mixin-preset= buildbot_osx_package,no_assertions -dash-dash - skip-test-swift skip-test-swiftpm skip-test-swift-driver @@ -1418,7 +1329,6 @@ mixin-preset= #===------------------------------------------------------------------------===# [preset: LLDB_Nested] -dash-dash skip-build-benchmarks install-destdir=%(swift_install_destdir)s llvm-targets-to-build=X86;ARM;AArch64;PowerPC;SystemZ;Mips @@ -1498,8 +1408,6 @@ install-libcxx # Build Playground support playgroundsupport -dash-dash - # Run the SIL verifier after each transform when building swift files sil-verify-all @@ -1555,8 +1463,6 @@ swiftevolve # Build Playground support playgroundsupport -dash-dash - # Skip build and test for tvOS skip-build-tvos skip-test-tvos @@ -1590,8 +1496,6 @@ build-swift-stdlib-unittest-extra llbuild swiftpm -dash-dash - # Only run watchOS tests skip-test-ios skip-test-tvos @@ -1911,8 +1815,6 @@ swift-assertions clang-user-visible-version=9.1.0 compiler-vendor=apple -dash-dash - build-ninja release-debuginfo reconfigure @@ -1938,8 +1840,6 @@ llvm-include-tests=0 [preset: remote_mirror_ios_customization] -dash-dash - darwin-xcrun-toolchain=ios darwin-deployment-version-ios=11.0 skip-build-osx @@ -1960,8 +1860,6 @@ mixin-preset= [preset: remote_mirror_watchos_customization] -dash-dash - darwin-xcrun-toolchain=watchos darwin-deployment-version-watchos=4.0 skip-build-osx @@ -1982,8 +1880,6 @@ mixin-preset= [preset: remote_mirror_tvos_customization] -dash-dash - darwin-xcrun-toolchain=tvos darwin-deployment-version-tvos=11.0 skip-build-osx @@ -2016,8 +1912,6 @@ ios tvos watchos -dash-dash - # On buildbots, always force a reconfiguration to make sure we pick up changes # in the build-script and build-presets.ini. reconfigure @@ -2033,8 +1927,6 @@ test validation-test lit-args=-v -dash-dash - skip-test-ios skip-test-tvos skip-test-watchos @@ -2046,8 +1938,6 @@ test validation-test lit-args=-v -dash-dash - skip-test-osx skip-test-tvos skip-test-watchos @@ -2058,8 +1948,6 @@ test validation-test lit-args=-v -dash-dash - skip-test-osx skip-test-watchos skip-test-ios @@ -2071,8 +1959,6 @@ test validation-test lit-args=-v -dash-dash - skip-test-osx skip-test-tvos skip-test-ios @@ -2090,8 +1976,6 @@ mixin-preset=mixin_buildbot_incremental,build release assertions -dash-dash - swift-stdlib-build-type=RelWithDebInfo swift-stdlib-enable-assertions=false @@ -2127,8 +2011,6 @@ mixin-preset=mixin_buildbot_incremental,build release assertions -dash-dash - swift-stdlib-build-type=RelWithDebInfo swift-stdlib-enable-assertions=true @@ -2175,8 +2057,6 @@ mixin-preset=mixin_buildbot_incremental,build release assertions -dash-dash - swift-stdlib-build-type=RelWithDebInfo swift-stdlib-enable-assertions=false @@ -2220,8 +2100,6 @@ mixin-preset=mixin_buildbot_incremental,build release assertions -dash-dash - [preset: buildbot_incremental,tools=RA,stdlib=RA,test=macOS,type=device] mixin-preset= buildbot_incremental,tools=RA,stdlib=RA,build @@ -2264,8 +2142,6 @@ assertions debug-llvm debug-swift -dash-dash - #===------------------------------------------------------------------------===# # Swift Preset # Tools: DebInfo and Assertions @@ -2280,8 +2156,6 @@ assertions debug-llvm debug-swift -dash-dash - swift-stdlib-build-type=RelWithDebInfo swift-stdlib-enable-assertions=true @@ -2313,8 +2187,6 @@ build-ninja build-subdir=LLVMClangSwift_iphoneos release -dash-dash - cross-compile-hosts=iphoneos-arm64 compiler-vendor=apple swift-primary-variant-sdk=IOS From 2f7ff6aa398dfe65f09fb2e44bc5969fdebbc11b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 13 Oct 2020 21:41:03 -0700 Subject: [PATCH 444/745] [Concurrency] Allow ActorIsolation in diagnostic messages. ActorIsolation is rendered as a descriptive phrase before an entity, e.g, "actor-independent" or "global actor 'UIActor'-isolated" when used in diagnostics. --- include/swift/AST/ActorIsolation.h | 1 + include/swift/AST/DiagnosticEngine.h | 14 ++++++++++++++ lib/AST/DiagnosticEngine.cpp | 20 ++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/include/swift/AST/ActorIsolation.h b/include/swift/AST/ActorIsolation.h index 78961df373191..7856ed8133fc0 100644 --- a/include/swift/AST/ActorIsolation.h +++ b/include/swift/AST/ActorIsolation.h @@ -16,6 +16,7 @@ #ifndef SWIFT_AST_ACTORISOLATIONSTATE_H #define SWIFT_AST_ACTORISOLATIONSTATE_H +#include "swift/AST/Type.h" #include "llvm/ADT/Hashing.h" namespace llvm { diff --git a/include/swift/AST/DiagnosticEngine.h b/include/swift/AST/DiagnosticEngine.h index 338f146922b5e..3ec8f3648a08a 100644 --- a/include/swift/AST/DiagnosticEngine.h +++ b/include/swift/AST/DiagnosticEngine.h @@ -18,6 +18,7 @@ #ifndef SWIFT_BASIC_DIAGNOSTICENGINE_H #define SWIFT_BASIC_DIAGNOSTICENGINE_H +#include "swift/AST/ActorIsolation.h" #include "swift/AST/DeclNameLoc.h" #include "swift/AST/DiagnosticConsumer.h" #include "swift/AST/TypeLoc.h" @@ -93,6 +94,7 @@ namespace swift { DeclAttribute, VersionTuple, LayoutConstraint, + ActorIsolation, }; namespace diag { @@ -122,6 +124,7 @@ namespace swift { const DeclAttribute *DeclAttributeVal; llvm::VersionTuple VersionVal; LayoutConstraint LayoutConstraintVal; + ActorIsolation ActorIsolationVal; }; public: @@ -209,6 +212,12 @@ namespace swift { DiagnosticArgument(LayoutConstraint L) : Kind(DiagnosticArgumentKind::LayoutConstraint), LayoutConstraintVal(L) { } + + DiagnosticArgument(ActorIsolation AI) + : Kind(DiagnosticArgumentKind::ActorIsolation), + ActorIsolationVal(AI) { + } + /// Initializes a diagnostic argument using the underlying type of the /// given enum. template< @@ -299,6 +308,11 @@ namespace swift { assert(Kind == DiagnosticArgumentKind::LayoutConstraint); return LayoutConstraintVal; } + + ActorIsolation getAsActorIsolation() const { + assert(Kind == DiagnosticArgumentKind::ActorIsolation); + return ActorIsolationVal; + } }; struct DiagnosticFormatOptions { diff --git a/lib/AST/DiagnosticEngine.cpp b/lib/AST/DiagnosticEngine.cpp index 9ab4e08c42620..7116f3517ac3f 100644 --- a/lib/AST/DiagnosticEngine.cpp +++ b/lib/AST/DiagnosticEngine.cpp @@ -660,6 +660,26 @@ static void formatDiagnosticArgument(StringRef Modifier, Out << FormatOpts.OpeningQuotationMark << Arg.getAsLayoutConstraint() << FormatOpts.ClosingQuotationMark; break; + case DiagnosticArgumentKind::ActorIsolation: + switch (auto isolation = Arg.getAsActorIsolation()) { + case ActorIsolation::ActorInstance: + Out << "actor-isolated"; + break; + + case ActorIsolation::GlobalActor: + Out << "global actor " << FormatOpts.OpeningQuotationMark + << isolation.getGlobalActor().getString() + << FormatOpts.ClosingQuotationMark << "-isolated"; + break; + + case ActorIsolation::Independent: + Out << "actor-independent"; + break; + + case ActorIsolation::Unspecified: + Out << "non-actor-isolated"; + break; + } } } From 18fd4be17a6a4307e46eeeae5d5220e3b9179c23 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 13 Oct 2020 21:41:59 -0700 Subject: [PATCH 445/745] [Concurrency] Check actor isolation consistency for overrides & subclasses. Both overriding declarations and subclasses must have the actor isolation as their overridden declarations or superclasses, respectively. Enforce this, ensuring that we're also doing the appropriate substitutions. --- include/swift/AST/ActorIsolation.h | 8 ++ include/swift/AST/DiagnosticsSema.def | 7 ++ lib/AST/TypeCheckRequests.cpp | 23 ++++++ lib/Sema/TypeCheckConcurrency.cpp | 74 ++++++++++++++++++- lib/Sema/TypeCheckConcurrency.h | 7 ++ lib/Sema/TypeCheckDeclPrimary.cpp | 8 +- test/Concurrency/global_actor_inference.swift | 60 ++++++++++++++- 7 files changed, 182 insertions(+), 5 deletions(-) diff --git a/include/swift/AST/ActorIsolation.h b/include/swift/AST/ActorIsolation.h index 7856ed8133fc0..c5ba69ba3f156 100644 --- a/include/swift/AST/ActorIsolation.h +++ b/include/swift/AST/ActorIsolation.h @@ -25,6 +25,7 @@ class raw_ostream; namespace swift { class ClassDecl; +class SubstitutionMap; class Type; /// Determine whether the given types are (canonically) equal, declared here @@ -97,6 +98,13 @@ class ActorIsolation { return globalActor; } + /// Determine whether this isolation will require substitution to be + /// evaluated. + bool requiresSubstitution() const; + + /// Substitute into types within the actor isolation. + ActorIsolation subst(SubstitutionMap subs) const; + friend bool operator==(const ActorIsolation &lhs, const ActorIsolation &rhs) { if (lhs.kind != rhs.kind) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 6a3d213a2314f..5bced0c6ac09a 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4262,6 +4262,13 @@ ERROR(actor_isolation_multiple_attr,none, "%0 %1 has multiple actor-isolation attributes ('%2' and '%3')", (DescriptiveDeclKind, DeclName, StringRef, StringRef)) +ERROR(actor_isolation_override_mismatch,none, + "%0 %1 %2 has different actor isolation from %3 overridden declaration", + (ActorIsolation, DescriptiveDeclKind, DeclName, ActorIsolation)) +ERROR(actor_isolation_superclass_mismatch,none, + "%0 class %1 has different actor isolation from %2 superclass %3", + (ActorIsolation, Identifier, ActorIsolation, Identifier)) + //------------------------------------------------------------------------------ // MARK: Type Check Types //------------------------------------------------------------------------------ diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 8b774caaea0d2..b72542686e9c4 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1447,6 +1447,29 @@ void CustomAttrTypeRequest::cacheResult(Type value) const { attr->setType(value); } +bool ActorIsolation::requiresSubstitution() const { + switch (kind) { + case ActorInstance: + case Independent: + case Unspecified: + return false; + + case GlobalActor: + return getGlobalActor()->hasTypeParameter(); + } +} + +ActorIsolation ActorIsolation::subst(SubstitutionMap subs) const { + switch (kind) { + case ActorInstance: + case Independent: + case Unspecified: + return *this; + + case GlobalActor: + return forGlobalActor(getGlobalActor().subst(subs)); + } +} void swift::simple_display( llvm::raw_ostream &out, const ActorIsolation &state) { diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 4a7ba10daea93..815f548c694e2 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -1155,8 +1155,16 @@ ActorIsolation ActorIsolationRequest::evaluate( // If the declaration overrides another declaration, it must have the same // actor isolation. if (auto overriddenValue = value->getOverriddenDecl()) { - if (auto isolation = getActorIsolation(overriddenValue)) - return inferredIsolation(isolation); + if (auto isolation = getActorIsolation(overriddenValue)) { + SubstitutionMap subs; + if (auto env = value->getInnermostDeclContext() + ->getGenericEnvironmentOfContext()) { + subs = SubstitutionMap::getOverrideSubstitutions( + overriddenValue, value, subs); + } + + return inferredIsolation(isolation.subst(subs)); + } } // If the declaration witnesses a protocol requirement that is isolated, @@ -1210,3 +1218,65 @@ ActorIsolation swift::getActorIsolation(ValueDecl *value) { ctx.evaluator, ActorIsolationRequest{value}, ActorIsolation::forUnspecified()); } + +void swift::checkOverrideActorIsolation(ValueDecl *value) { + if (isa(value)) + return; + + auto overridden = value->getOverriddenDecl(); + if (!overridden) + return; + + // Determine the actor isolation of this declaration. + auto isolation = getActorIsolation(value); + + // Determine the actor isolation of the overridden function.= + auto overriddenIsolation = getActorIsolation(overridden); + + if (overriddenIsolation.requiresSubstitution()) { + SubstitutionMap subs; + if (auto env = value->getInnermostDeclContext() + ->getGenericEnvironmentOfContext()) { + subs = SubstitutionMap::getOverrideSubstitutions(overridden, value, subs); + overriddenIsolation = overriddenIsolation.subst(subs); + } + } + + // If the isolation matches, we're done. + if (isolation == overriddenIsolation) + return; + + // Isolation mismatch. Diagnose it. + value->diagnose( + diag::actor_isolation_override_mismatch, isolation, + value->getDescriptiveKind(), value->getName(), overriddenIsolation); + overridden->diagnose(diag::overridden_here); +} + +void swift::checkSubclassActorIsolation(ClassDecl *classDecl) { + auto superclassDecl = classDecl->getSuperclassDecl(); + if (!superclassDecl) + return; + + auto isolation = getActorIsolation(classDecl); + auto superclassIsolation = getActorIsolation(superclassDecl); + + if (superclassIsolation.requiresSubstitution()) { + Type superclassType = classDecl->getSuperclass(); + if (!superclassType) + return; + + SubstitutionMap subs = superclassType->getMemberSubstitutionMap( + classDecl->getModuleContext(), classDecl); + superclassIsolation = superclassIsolation.subst(subs); + } + + if (isolation == superclassIsolation) + return; + + // Diagnose mismatch. + classDecl->diagnose( + diag::actor_isolation_superclass_mismatch, isolation, + classDecl->getName(), superclassIsolation, superclassDecl->getName()); + superclassDecl->diagnose(diag::decl_declared_here, superclassDecl->getName()); +} diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index 5a4869dbae150..ffe4b10f31f77 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -144,6 +144,13 @@ class ActorIsolationRestriction { operator Kind() const { return kind; }; }; +/// Check that the actor isolation of an override matches that of its +/// overridden declaration. +void checkOverrideActorIsolation(ValueDecl *value); + +/// Check that the actor isolation of a class matches that of its superclass. +void checkSubclassActorIsolation(ClassDecl *classDecl); + } // end namespace swift #endif /* SWIFT_SEMA_TYPECHECKCONCURRENCY_H */ diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index adc9c837df6c3..da39bf794a641 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -21,6 +21,7 @@ #include "DerivedConformances.h" #include "TypeChecker.h" #include "TypeCheckAccess.h" +#include "TypeCheckConcurrency.h" #include "TypeCheckDecl.h" #include "TypeCheckAvailability.h" #include "TypeCheckObjC.h" @@ -1382,12 +1383,17 @@ class DeclChecker : public DeclVisitor { (void) VD->getFormalAccess(); // Compute overrides. - (void) VD->getOverriddenDecls(); + if (!VD->getOverriddenDecls().empty()) + checkOverrideActorIsolation(VD); // Check whether the member is @objc or dynamic. (void) VD->isObjC(); (void) VD->isDynamic(); + // For a class, check actor isolation. + if (auto classDecl = dyn_cast(VD)) + checkSubclassActorIsolation(classDecl); + // If this is a member of a nominal type, don't allow it to have a name of // "Type" or "Protocol" since we reserve the X.Type and X.Protocol // expressions to mean something builtin to the language. We *do* allow diff --git a/test/Concurrency/global_actor_inference.swift b/test/Concurrency/global_actor_inference.swift index 8fcbe36011835..94ddddee62c39 100644 --- a/test/Concurrency/global_actor_inference.swift +++ b/test/Concurrency/global_actor_inference.swift @@ -15,6 +15,11 @@ struct OtherGlobalActor { static let shared = SomeActor() } +@globalActor +struct GenericGlobalActor { + static var shared: SomeActor { SomeActor() } +} + // ---------------------------------------------------------------------- // Global actor inference for protocols // ---------------------------------------------------------------------- @@ -25,11 +30,10 @@ protocol P1 { } protocol P2 { - @SomeGlobalActor func method1() + @SomeGlobalActor func method1() // expected-note {{'method1()' declared here}} func method2() } - class C1: P1 { func method() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} @@ -88,3 +92,55 @@ class C5 { c5.method1() // OK: no propagation c5.method2() // expected-error{{instance method 'method2()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}} } + +protocol P3 { + @OtherGlobalActor func method1() // expected-note{{'method1()' declared here}} + func method2() +} + +class C6: P2, P3 { + func method1() { } + // expected-error@-1{{instance method 'method1()' must be isolated to the global actor 'SomeGlobalActor' to satisfy corresponding requirement from protocol 'P2'}} + // expected-error@-2{{instance method 'method1()' must be isolated to the global actor 'OtherGlobalActor' to satisfy corresponding requirement from protocol 'P3'}} + func method2() { } + + func testMethod() { + method1() // okay: no inference + method2() // okay: no inference + } +} + +// ---------------------------------------------------------------------- +// Global actor checking for overrides +// ---------------------------------------------------------------------- +actor class GenericSuper { + @GenericGlobalActor func method() { } + + @GenericGlobalActor func method2() { } // expected-note {{overridden declaration is here}} + @GenericGlobalActor func method3() { } // expected-note {{overridden declaration is here}} + @GenericGlobalActor func method4() { } + @GenericGlobalActor func method5() { } +} + +actor class GenericSub : GenericSuper<[T]> { + override func method() { } // expected-note{{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}} + + @GenericGlobalActor override func method2() { } // expected-error{{global actor 'GenericGlobalActor'-isolated instance method 'method2()' has different actor isolation from global actor 'GenericGlobalActor<[T]>'-isolated overridden declaration}} + @actorIndependent override func method3() { } // expected-error{{actor-independent instance method 'method3()' has different actor isolation from global actor 'GenericGlobalActor<[T]>'-isolated overridden declaration}} + + @OtherGlobalActor func testMethod() { + method() // expected-error{{instance method 'method()' isolated to global actor 'GenericGlobalActor<[T]>' can not be referenced from different global actor 'OtherGlobalActor'}} + } +} + +// ---------------------------------------------------------------------- +// Global actor checking for supeclasses +// ---------------------------------------------------------------------- +struct Container { + @GenericGlobalActor class Superclass { } // expected-note{{'Superclass' declared here}} +} + +struct OtherContainer { + @GenericGlobalActor<[U]> class Subclass1 : Container<[U]>.Superclass { } + @GenericGlobalActor class Subclass2 : Container<[U]>.Superclass { } // expected-error{{global actor 'GenericGlobalActor'-isolated class 'Subclass2' has different actor isolation from global actor 'GenericGlobalActor<[U]>'-isolated superclass 'Superclass'}} +} From 11cf3ceffe20ea7d32678c5d83347735cea94341 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 13 Oct 2020 22:37:24 -0700 Subject: [PATCH 446/745] [Concurrency] Eliminate actor isolation checking for subclasses. Subclasses inherit the global actor from their superclass by default, but it's okay to change it---it's just a default that can be overridden on a per-member basis anyway. --- include/swift/AST/DiagnosticsSema.def | 3 -- lib/Sema/TypeCheckConcurrency.cpp | 28 ------------------- lib/Sema/TypeCheckConcurrency.h | 3 -- lib/Sema/TypeCheckDeclPrimary.cpp | 4 --- test/Concurrency/global_actor_inference.swift | 7 +++-- 5 files changed, 4 insertions(+), 41 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 5bced0c6ac09a..fe4fc96f29b4c 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4265,9 +4265,6 @@ ERROR(actor_isolation_multiple_attr,none, ERROR(actor_isolation_override_mismatch,none, "%0 %1 %2 has different actor isolation from %3 overridden declaration", (ActorIsolation, DescriptiveDeclKind, DeclName, ActorIsolation)) -ERROR(actor_isolation_superclass_mismatch,none, - "%0 class %1 has different actor isolation from %2 superclass %3", - (ActorIsolation, Identifier, ActorIsolation, Identifier)) //------------------------------------------------------------------------------ // MARK: Type Check Types diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 815f548c694e2..7c479f05762f9 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -1252,31 +1252,3 @@ void swift::checkOverrideActorIsolation(ValueDecl *value) { value->getDescriptiveKind(), value->getName(), overriddenIsolation); overridden->diagnose(diag::overridden_here); } - -void swift::checkSubclassActorIsolation(ClassDecl *classDecl) { - auto superclassDecl = classDecl->getSuperclassDecl(); - if (!superclassDecl) - return; - - auto isolation = getActorIsolation(classDecl); - auto superclassIsolation = getActorIsolation(superclassDecl); - - if (superclassIsolation.requiresSubstitution()) { - Type superclassType = classDecl->getSuperclass(); - if (!superclassType) - return; - - SubstitutionMap subs = superclassType->getMemberSubstitutionMap( - classDecl->getModuleContext(), classDecl); - superclassIsolation = superclassIsolation.subst(subs); - } - - if (isolation == superclassIsolation) - return; - - // Diagnose mismatch. - classDecl->diagnose( - diag::actor_isolation_superclass_mismatch, isolation, - classDecl->getName(), superclassIsolation, superclassDecl->getName()); - superclassDecl->diagnose(diag::decl_declared_here, superclassDecl->getName()); -} diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index ffe4b10f31f77..9191f815650f1 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -148,9 +148,6 @@ class ActorIsolationRestriction { /// overridden declaration. void checkOverrideActorIsolation(ValueDecl *value); -/// Check that the actor isolation of a class matches that of its superclass. -void checkSubclassActorIsolation(ClassDecl *classDecl); - } // end namespace swift #endif /* SWIFT_SEMA_TYPECHECKCONCURRENCY_H */ diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index da39bf794a641..5fd674b6d9775 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1390,10 +1390,6 @@ class DeclChecker : public DeclVisitor { (void) VD->isObjC(); (void) VD->isDynamic(); - // For a class, check actor isolation. - if (auto classDecl = dyn_cast(VD)) - checkSubclassActorIsolation(classDecl); - // If this is a member of a nominal type, don't allow it to have a name of // "Type" or "Protocol" since we reserve the X.Type and X.Protocol // expressions to mean something builtin to the language. We *do* allow diff --git a/test/Concurrency/global_actor_inference.swift b/test/Concurrency/global_actor_inference.swift index 94ddddee62c39..178368324eecc 100644 --- a/test/Concurrency/global_actor_inference.swift +++ b/test/Concurrency/global_actor_inference.swift @@ -134,13 +134,14 @@ actor class GenericSub : GenericSuper<[T]> { } // ---------------------------------------------------------------------- -// Global actor checking for supeclasses +// Global actor inference for superclasses // ---------------------------------------------------------------------- struct Container { - @GenericGlobalActor class Superclass { } // expected-note{{'Superclass' declared here}} + @GenericGlobalActor class Superclass { } } struct OtherContainer { + // Okay to change the global actor in a subclass. @GenericGlobalActor<[U]> class Subclass1 : Container<[U]>.Superclass { } - @GenericGlobalActor class Subclass2 : Container<[U]>.Superclass { } // expected-error{{global actor 'GenericGlobalActor'-isolated class 'Subclass2' has different actor isolation from global actor 'GenericGlobalActor<[U]>'-isolated superclass 'Superclass'}} + @GenericGlobalActor class Subclass2 : Container<[U]>.Superclass { } } From a5b15ed6309ce9c893204bec9ba8e9a2d19d9b66 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 13 Oct 2020 22:40:51 -0700 Subject: [PATCH 447/745] [Concurrency] Substitute into superclass global actors when inheriting them. --- lib/Sema/TypeCheckConcurrency.cpp | 13 ++++++++++++- test/Concurrency/global_actor_inference.swift | 10 ++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 7c479f05762f9..11d1a5e26c975 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -1178,8 +1178,19 @@ ActorIsolation ActorIsolationRequest::evaluate( if (auto classDecl = dyn_cast(value)) { if (auto superclassDecl = classDecl->getSuperclassDecl()) { auto superclassIsolation = getActorIsolation(superclassDecl); - if (!superclassIsolation.isUnspecified()) + if (!superclassIsolation.isUnspecified()) { + if (superclassIsolation.requiresSubstitution()) { + Type superclassType = classDecl->getSuperclass(); + if (!superclassType) + return ActorIsolation::forUnspecified(); + + SubstitutionMap subs = superclassType->getMemberSubstitutionMap( + classDecl->getModuleContext(), classDecl); + superclassIsolation = superclassIsolation.subst(subs); + } + return inferredIsolation(superclassIsolation); + } } } diff --git a/test/Concurrency/global_actor_inference.swift b/test/Concurrency/global_actor_inference.swift index 178368324eecc..1db013712493a 100644 --- a/test/Concurrency/global_actor_inference.swift +++ b/test/Concurrency/global_actor_inference.swift @@ -138,10 +138,20 @@ actor class GenericSub : GenericSuper<[T]> { // ---------------------------------------------------------------------- struct Container { @GenericGlobalActor class Superclass { } + @GenericGlobalActor<[T]> class Superclass2 { } } struct OtherContainer { // Okay to change the global actor in a subclass. @GenericGlobalActor<[U]> class Subclass1 : Container<[U]>.Superclass { } @GenericGlobalActor class Subclass2 : Container<[U]>.Superclass { } + + // Ensure that substitutions work properly when inheriting. + class Subclass3 : Container<(U, V)>.Superclass2 { + func method() { } // expected-note{{only asynchronous methods can be used outside the actor instance}} + + @OtherGlobalActor func testMethod() { + method() // expected-error{{instance method 'method()' isolated to global actor 'GenericGlobalActor<[(U, V)]>' can not be referenced from different global actor 'OtherGlobalActor'}} + } + } } From 3a651a61b7868c6b13a0cb90eaf1a5be92762a7c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 13 Oct 2020 22:57:06 -0700 Subject: [PATCH 448/745] [Concurrency] Fix circular reference on isolation propagation. --- lib/Sema/TypeCheckConcurrency.cpp | 17 +++++++++-------- .../single-file-private/AnyObject.swift | 14 ++++++++++++++ test/decl/class/circular_inheritance.swift | 16 ++++++++-------- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 11d1a5e26c975..474512979861c 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -1035,6 +1035,9 @@ static Optional getIsolationFromWitnessedRequirements( if (!idc) return None; + if (dc->getSelfProtocolDecl()) + return None; + // Walk through each of the conformances in this context, collecting any // requirements that have actor isolation. auto conformances = evaluateOrDefault( @@ -1167,6 +1170,12 @@ ActorIsolation ActorIsolationRequest::evaluate( } } + // If this is an accessor, use the actor isolation of its storage + // declaration. + if (auto accessor = dyn_cast(value)) { + return getActorIsolation(accessor->getStorage()); + } + // If the declaration witnesses a protocol requirement that is isolated, // use that. if (auto witnessedIsolation = getIsolationFromWitnessedRequirements(value)) { @@ -1194,14 +1203,6 @@ ActorIsolation ActorIsolationRequest::evaluate( } } - // If this is an accessor, use the actor isolation of its storage - // declaration. - if (auto accessor = dyn_cast(value)) { - auto storageIsolation = getActorIsolation(accessor->getStorage()); - if (!storageIsolation.isUnspecified()) - return inferredIsolation(storageIsolation); - } - // If the declaration is in an extension that has one of the isolation // attributes, use that. if (auto ext = dyn_cast(value->getDeclContext())) { diff --git a/test/Incremental/Verifier/single-file-private/AnyObject.swift b/test/Incremental/Verifier/single-file-private/AnyObject.swift index 2323db8859d4d..896bd76c3b7a3 100644 --- a/test/Incremental/Verifier/single-file-private/AnyObject.swift +++ b/test/Incremental/Verifier/single-file-private/AnyObject.swift @@ -72,3 +72,17 @@ func lookupOnAnyObject(object: AnyObject) { // expected-provides {{lookupOnAnyOb // expected-member {{Swift.CVarArg.someMethod}} // expected-member {{Swift.CustomStringConvertible.someMethod}} // expected-member {{Swift.CustomDebugStringConvertible.someMethod}} +// expected-member {{Swift.Equatable.someMember}} +// expected-member{{Swift.CustomDebugStringConvertible.init}} +// expected-member{{Swift.CVarArg.someMember}} +// expected-member{{Foundation._KeyValueCodingAndObservingPublishing.someMember}} +// expected-member{{Swift.Equatable.init}} +// expected-member{{Swift.Hashable.init}} +// expected-member{{Swift.CVarArg.init}} +// expected-member{{Foundation._KeyValueCodingAndObserving.someMember}} +// expected-member{{Foundation._KeyValueCodingAndObservingPublishing.init}} +// expected-member{{Swift.CustomDebugStringConvertible.someMember}} +// expected-member{{Swift.CustomStringConvertible.someMember}} +// expected-member{{Swift.CustomStringConvertible.init}} +// expected-member{{Swift.Hashable.someMember}} +// expected-member{{Foundation._KeyValueCodingAndObserving.init}} diff --git a/test/decl/class/circular_inheritance.swift b/test/decl/class/circular_inheritance.swift index 1ce97f8f216b6..103ceb4679ba5 100644 --- a/test/decl/class/circular_inheritance.swift +++ b/test/decl/class/circular_inheritance.swift @@ -4,14 +4,14 @@ // RUN: not %target-swift-frontend -typecheck -debug-cycles %s -build-request-dependency-graph -output-request-graphviz %t.dot -stats-output-dir %t/stats-dir 2> %t.cycles // RUN: %FileCheck -check-prefix CHECK-DOT %s < %t.dot -class Left // expected-error {{'Left' inherits from itself}} expected-note {{through reference here}} +class Left // expected-error {{'Left' inherits from itself}} expected-note 2{{through reference here}} : Right.Hand { // expected-note {{through reference here}} - class Hand {} + class Hand {} // expected-note {{through reference here}} } -class Right // expected-note {{through reference here}} expected-note{{class 'Right' declared here}} +class Right // expected-note 2 {{through reference here}} expected-note{{class 'Right' declared here}} : Left.Hand { // expected-note {{through reference here}} - class Hand {} + class Hand {} // expected-error {{circular reference}} } class C : B { } // expected-error{{'C' inherits from itself}} @@ -30,15 +30,15 @@ class Outer { class Inner : Outer {} } -class Outer2 // expected-error {{'Outer2' inherits from itself}} expected-note {{through reference here}} +class Outer2 // expected-error {{'Outer2' inherits from itself}} expected-note 2 {{through reference here}} : Outer2.Inner { // expected-note {{through reference here}} - class Inner {} + class Inner {} // expected-error{{circular reference}} } -class Outer3 // expected-error {{'Outer3' inherits from itself}} expected-note {{through reference here}} +class Outer3 // expected-error {{'Outer3' inherits from itself}} expected-note 2 {{through reference here}} : Outer3.Inner { // expected-note {{through reference here}} - class Inner {} + class Inner {} // expected-error{{circular reference}} } // CHECK-DOT: digraph Dependencies From 96a0d0e5840045e25d2c05f02999972f804201df Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Wed, 14 Oct 2020 07:10:16 -0700 Subject: [PATCH 449/745] Rename various IncludeUsableFromInlineAndInlineable to IncludeUsableFromInline `@inlinable` implies `@usableFromInline`. NFC intended. --- include/swift/AST/Decl.h | 2 +- include/swift/AST/LookupKinds.h | 2 +- include/swift/AST/ModuleNameLookup.h | 2 +- include/swift/AST/NameLookup.h | 2 +- lib/AST/Decl.cpp | 4 ++-- lib/AST/ModuleNameLookup.cpp | 2 +- lib/AST/NameLookup.cpp | 2 +- lib/AST/UnqualifiedLookup.cpp | 6 +++--- lib/Sema/TypeCheckAttr.cpp | 2 +- lib/Sema/TypeCheckNameLookup.cpp | 8 ++++---- lib/Sema/TypeCheckType.cpp | 4 ++-- lib/Sema/TypeChecker.h | 2 +- 12 files changed, 19 insertions(+), 19 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 75776ff3610f8..73f5c23f9ccce 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -2188,7 +2188,7 @@ class ValueDecl : public Decl { /// the default implementations are not visible to name lookup. bool isAccessibleFrom(const DeclContext *DC, bool forConformance = false, - bool includeInlineable = false) const; + bool allowUsableFromInline = false) const; /// Returns whether this declaration should be treated as \c open from /// \p useDC. This is very similar to #getFormalAccess, but takes diff --git a/include/swift/AST/LookupKinds.h b/include/swift/AST/LookupKinds.h index ec0b1bbb96ccd..aa1245bbe28fc 100644 --- a/include/swift/AST/LookupKinds.h +++ b/include/swift/AST/LookupKinds.h @@ -51,7 +51,7 @@ enum NLOptions : unsigned { NL_IncludeAttributeImplements = 1 << 5, // Include @usableFromInline and @inlinable - NL_IncludeUsableFromInlineAndInlineable = 1 << 6, + NL_IncludeUsableFromInline = 1 << 6, /// The default set of options used for qualified name lookup. /// diff --git a/include/swift/AST/ModuleNameLookup.h b/include/swift/AST/ModuleNameLookup.h index 6ec53e6c17887..bb09a801f3d66 100644 --- a/include/swift/AST/ModuleNameLookup.h +++ b/include/swift/AST/ModuleNameLookup.h @@ -57,7 +57,7 @@ void simple_display(llvm::raw_ostream &out, ResolutionKind kind); /// being performed, for checking access. This must be either a /// FileUnit or a Module. /// \param options name lookup options. Currently only used to communicate the -/// NL_IncludeUsableFromInlineAndInlineable option. +/// NL_IncludeUsableFromInline option. void lookupInModule(const DeclContext *moduleOrFile, DeclName name, SmallVectorImpl &decls, NLKind lookupKind, ResolutionKind resolutionKind, diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index c4ad80ca196b5..ed5f8acb45b5f 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -227,7 +227,7 @@ enum class UnqualifiedLookupFlags { IncludeOuterResults = 1 << 4, // This lookup should include results that are @inlinable or // @usableFromInline. - IncludeInlineableAndUsableFromInline = 1 << 5, + IncludeUsableFromInline = 1 << 5, }; using UnqualifiedLookupOptions = OptionSet; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 103938b921b2d..29079e477b1ef 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3394,8 +3394,8 @@ static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD, bool ValueDecl::isAccessibleFrom(const DeclContext *useDC, bool forConformance, - bool includeInlineable) const { - return checkAccess(useDC, this, forConformance, includeInlineable, + bool allowUsableFromInline) const { + return checkAccess(useDC, this, forConformance, allowUsableFromInline, [&]() { return getFormalAccess(); }); } diff --git a/lib/AST/ModuleNameLookup.cpp b/lib/AST/ModuleNameLookup.cpp index 44bc1f54c761b..4fbe9313bdbff 100644 --- a/lib/AST/ModuleNameLookup.cpp +++ b/lib/AST/ModuleNameLookup.cpp @@ -145,7 +145,7 @@ void ModuleNameLookup::lookupInModule( const size_t initialCount = decls.size(); size_t currentCount = decls.size(); - bool includeInlineable = options & NL_IncludeUsableFromInlineAndInlineable; + bool includeInlineable = options & NL_IncludeUsableFromInline; auto updateNewDecls = [&](const DeclContext *moduleScopeContext) { if (decls.size() == currentCount) diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index daf6a4c30c206..e7fd2f65cb5b4 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -1502,7 +1502,7 @@ static bool isAcceptableLookupResult(const DeclContext *dc, // Check access. if (!(options & NL_IgnoreAccessControl) && !dc->getASTContext().isAccessControlDisabled()) { - bool allowInlinable = options & NL_IncludeUsableFromInlineAndInlineable; + bool allowInlinable = options & NL_IncludeUsableFromInline; return decl->isAccessibleFrom(dc, /*forConformance*/ false, allowInlinable); } diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 4738926b5793f..27ac8195ebc94 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -384,8 +384,8 @@ void UnqualifiedLookupFactory::addImportedResults(const DeclContext *const dc) { auto resolutionKind = isOriginallyTypeLookup ? ResolutionKind::TypesOnly : ResolutionKind::Overloadable; auto nlOptions = NL_UnqualifiedDefault; - if (options.contains(Flags::IncludeInlineableAndUsableFromInline)) - nlOptions |= NL_IncludeUsableFromInlineAndInlineable; + if (options.contains(Flags::IncludeUsableFromInline)) + nlOptions |= NL_IncludeUsableFromInline; lookupInModule(dc, Name.getFullName(), CurModuleResults, NLKind::UnqualifiedLookup, resolutionKind, dc, nlOptions); @@ -841,4 +841,4 @@ ValueDecl *ASTScope::lookupSingleLocalDecl(SourceFile *sf, DeclName name, if (result.size() != 1) return nullptr; return result[0]; -} \ No newline at end of file +} diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index d0661b189ba3e..18671015905d4 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2465,7 +2465,7 @@ static void lookupReplacedDecl(DeclNameRef replacedDeclName, auto options = NL_QualifiedDefault; if (declCtxt->isInSpecializeExtensionContext()) - options |= NL_IncludeUsableFromInlineAndInlineable; + options |= NL_IncludeUsableFromInline; if (typeCtx) moduleScopeCtxt->lookupQualified({typeCtx}, replacedDeclName, options, diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index b148adab7180c..2fc3f814704bd 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -211,8 +211,8 @@ convertToUnqualifiedLookupOptions(NameLookupOptions options) { newOptions |= UnqualifiedLookupFlags::IgnoreAccessControl; if (options.contains(NameLookupFlags::IncludeOuterResults)) newOptions |= UnqualifiedLookupFlags::IncludeOuterResults; - if (options.contains(NameLookupFlags::IncludeInlineableAndUsableFromInline)) - newOptions |= UnqualifiedLookupFlags::IncludeInlineableAndUsableFromInline; + if (options.contains(NameLookupFlags::IncludeUsableFromInline)) + newOptions |= UnqualifiedLookupFlags::IncludeUsableFromInline; return newOptions; } @@ -388,8 +388,8 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc, if (options.contains(NameLookupFlags::IgnoreAccessControl)) subOptions |= NL_IgnoreAccessControl; - if (options.contains(NameLookupFlags::IncludeInlineableAndUsableFromInline)) - subOptions |= NL_IncludeUsableFromInlineAndInlineable; + if (options.contains(NameLookupFlags::IncludeUsableFromInline)) + subOptions |= NL_IncludeUsableFromInline; // Make sure we've resolved implicit members, if we need them. if (auto *current = type->getAnyNominal()) { diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 476e02939136d..da26ab89a5b9b 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1323,7 +1323,7 @@ static Type resolveTopLevelIdentTypeComponent(TypeResolution resolution, NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; if (options.contains(TypeResolutionFlags::AllowInlinable)) - lookupOptions |= NameLookupFlags::IncludeInlineableAndUsableFromInline; + lookupOptions |= NameLookupFlags::IncludeUsableFromInline; auto globals = TypeChecker::lookupUnqualifiedType(DC, id, comp->getLoc(), lookupOptions); @@ -1527,7 +1527,7 @@ static Type resolveNestedIdentTypeComponent(TypeResolution resolution, // Look for member types with the given name. NameLookupOptions lookupOptions = defaultMemberLookupOptions; if (options.contains(TypeResolutionFlags::AllowInlinable)) - lookupOptions |= NameLookupFlags::IncludeInlineableAndUsableFromInline; + lookupOptions |= NameLookupFlags::IncludeUsableFromInline; LookupTypeResult memberTypes; if (parentTy->mayHaveMembers()) memberTypes = TypeChecker::lookupMemberType( diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 3caaeaed2d3c3..685a3c54a38bb 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -153,7 +153,7 @@ enum class NameLookupFlags { /// result. IncludeOuterResults = 1 << 1, // Whether to include results that are marked @inlinable or @usableFromInline. - IncludeInlineableAndUsableFromInline = 1 << 2, + IncludeUsableFromInline = 1 << 2, }; /// A set of options that control name lookup. From 722cc755f8ee5bd74eb8f6c4e7f0a6514838fd50 Mon Sep 17 00:00:00 2001 From: Zoe Carver Date: Wed, 14 Oct 2020 09:29:32 -0700 Subject: [PATCH 450/745] [cxx-interop] Cast data to the correct type. (#34266) The implicit conversions are OK in C but C++ will error. To make this header valid in both C and C++ we should just always cast. --- stdlib/public/SwiftShims/DispatchOverlayShims.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/public/SwiftShims/DispatchOverlayShims.h b/stdlib/public/SwiftShims/DispatchOverlayShims.h index e9eeebfeb75f6..d49e9ad38263c 100644 --- a/stdlib/public/SwiftShims/DispatchOverlayShims.h +++ b/stdlib/public/SwiftShims/DispatchOverlayShims.h @@ -215,8 +215,8 @@ static inline unsigned int _swift_dispatch_data_apply( __swift_shims_dispatch_data_t data, __swift_shims_dispatch_data_applier SWIFT_DISPATCH_NOESCAPE applier) { - return dispatch_data_apply(data, ^bool(dispatch_data_t data, size_t off, const void *loc, size_t size){ - return applier(data, off, loc, size); + return dispatch_data_apply((dispatch_data_t)data, ^bool(dispatch_data_t data, size_t off, const void *loc, size_t size){ + return applier((__swift_shims_dispatch_data_t)data, off, loc, size); }); } From 21a0696e165747248ac5f4532feeb942e13e326b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 14 Oct 2020 09:41:34 -0700 Subject: [PATCH 451/745] [Concurrency] Clean up inference logic for @asyncHandler. --- lib/Sema/TypeCheckConcurrency.cpp | 34 ++++++++++++++++++------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 474512979861c..b2de9d9addd95 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -25,6 +25,24 @@ using namespace swift; +/// Determine whether it makes sense to infer an attribute in the given +/// context. +static bool shouldInferAttributeInContext(const DeclContext *dc) { + auto sourceFile = dc->getParentSourceFile(); + if (!sourceFile) + return false; + + switch (sourceFile->Kind) { + case SourceFileKind::Interface: + case SourceFileKind::SIL: + return false; + + case SourceFileKind::Library: + case SourceFileKind::Main: + return true; + } +} + /// Check whether the @asyncHandler attribute can be applied to the given /// function declaration. /// @@ -108,7 +126,7 @@ bool IsAsyncHandlerRequest::evaluate( return true; } - if (!func->getASTContext().LangOpts.EnableExperimentalConcurrency) + if (!shouldInferAttributeInContext(func->getDeclContext())) return false; // Are we in a context where inference is possible? @@ -1135,20 +1153,8 @@ ActorIsolation ActorIsolationRequest::evaluate( } // Disable inference of actor attributes outside of normal Swift source files. - if (auto sourceFile = value->getDeclContext()->getParentSourceFile()) { - switch (sourceFile->Kind) { - case SourceFileKind::Interface: - case SourceFileKind::SIL: - return defaultIsolation; - - case SourceFileKind::Library: - case SourceFileKind::Main: - // Attempt inference below. - break; - } - } else { + if (!shouldInferAttributeInContext(value->getDeclContext())) return defaultIsolation; - } // Function used when returning an inferred isolation. auto inferredIsolation = [&](ActorIsolation inferred) { From dee5356021f49ea31954e3ecc2170d285d47804c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 14 Oct 2020 10:20:39 -0700 Subject: [PATCH 452/745] [Concurrency] Add inferred actor-isolation attributes. When we infer an actor-isolation attribute on a declaration, add an implicit attribute that will show up in the printed interface and get serialized. ... and clean up the type resolution logic for global actor attributes to make it use CustomAttrTypeRequest, so the result gets appropriately cached in CustomAttr for serialization. --- include/swift/AST/TypeCheckRequests.h | 4 ++ lib/AST/TypeCheckRequests.cpp | 4 ++ lib/Sema/TypeCheckConcurrency.cpp | 36 +++++++++++++++--- test/ModuleInterface/actor_isolation.swift | 44 ++++++++++++++++++++++ 4 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 test/ModuleInterface/actor_isolation.swift diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 88434b3103a18..a2fc16fd8848c 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -2696,6 +2696,10 @@ enum class CustomAttrTypeKind { /// Property delegates have some funky rules, like allowing /// unbound generic types. PropertyDelegate, + + /// Global actors are represented as custom type attributes. They don't + /// have any particularly interesting semantics. + GlobalActor, }; void simple_display(llvm::raw_ostream &out, CustomAttrTypeKind value); diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index b72542686e9c4..aec44052dbebd 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1430,6 +1430,10 @@ void swift::simple_display(llvm::raw_ostream &out, CustomAttrTypeKind value) { case CustomAttrTypeKind::PropertyDelegate: out << "property-delegate"; return; + + case CustomAttrTypeKind::GlobalActor: + out << "global-actor"; + return; } llvm_unreachable("bad kind"); } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index b2de9d9addd95..28ba235fb2542 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -1031,15 +1031,18 @@ static Optional getIsolationFromAttributes(Decl *decl) { // If the declaration is marked with a global actor, report it as being // part of that global actor. if (globalActorAttr) { - TypeResolutionOptions options(TypeResolverContext::None); - TypeResolution resolver = TypeResolution::forInterface( - decl->getInnermostDeclContext(), options, nullptr); - Type globalActorType = resolver.resolveType( - globalActorAttr->first->getTypeRepr(), nullptr); + ASTContext &ctx = decl->getASTContext(); + auto dc = decl->getInnermostDeclContext(); + Type globalActorType = evaluateOrDefault( + ctx.evaluator, + CustomAttrTypeRequest{ + globalActorAttr->first, dc, CustomAttrTypeKind::GlobalActor}, + Type()); if (!globalActorType || globalActorType->hasError()) return ActorIsolation::forUnspecified(); - return ActorIsolation::forGlobalActor(globalActorType); + return ActorIsolation::forGlobalActor( + globalActorType->mapTypeOutOfContext()); } llvm_unreachable("Forgot about an attribute?"); @@ -1158,6 +1161,27 @@ ActorIsolation ActorIsolationRequest::evaluate( // Function used when returning an inferred isolation. auto inferredIsolation = [&](ActorIsolation inferred) { + // Add an implicit attribute to capture the actor isolation that was + // inferred, so that (e.g.) it will be printed and serialized. + ASTContext &ctx = value->getASTContext(); + switch (inferred) { + case ActorIsolation::Independent: + value->getAttrs().add(new (ctx) ActorIndependentAttr(true)); + break; + + case ActorIsolation::GlobalActor: { + auto typeExpr = TypeExpr::createImplicit(inferred.getGlobalActor(), ctx); + auto attr = CustomAttr::create( + ctx, SourceLoc(), typeExpr, /*implicit=*/true); + value->getAttrs().add(attr); + break; + } + + case ActorIsolation::ActorInstance: + case ActorIsolation::Unspecified: + // Nothing to do. + break; + } return inferred; }; diff --git a/test/ModuleInterface/actor_isolation.swift b/test/ModuleInterface/actor_isolation.swift new file mode 100644 index 0000000000000..dd565f4fc4bfe --- /dev/null +++ b/test/ModuleInterface/actor_isolation.swift @@ -0,0 +1,44 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -o %t/Test.swiftmodule -emit-module-interface-path %t/Test.swiftinterface -module-name Test -enable-experimental-concurrency %s +// RUN: %FileCheck %s --check-prefix FROMSOURCE --check-prefix CHECK < %t/Test.swiftinterface +// RUN: %target-swift-frontend -emit-module -o /dev/null -merge-modules %t/Test.swiftmodule -disable-objc-attr-requires-foundation-module -emit-module-interface-path %t/TestFromModule.swiftinterface -module-name Test -enable-experimental-concurrency +// RUN: %FileCheck %s --check-prefix FROMMODULE --check-prefix CHECK < %t/TestFromModule.swiftinterface + +// REQUIRES: concurrency +import _Concurrency + +// CHECK: actor public class SomeActor +public actor class SomeActor { + @actorIndependent func maine() { } +} + +// CHECK: @globalActor public struct SomeGlobalActor +@globalActor +public struct SomeGlobalActor { + public static let shared = SomeActor() +} + +// CHECK: @{{(Test.)?}}SomeGlobalActor public protocol P1 +// CHECK-NEXT: @{{(Test.)?}}SomeGlobalActor func method() +@SomeGlobalActor +public protocol P1 { + func method() +} + +// CHECK: class C1 +// CHECK-NEXT: @{{(Test.)?}}SomeGlobalActor public func method() +public class C1: P1 { + public func method() { } +} + +@SomeGlobalActor +public class C2 { } + +// CHECK: @{{(Test.)?}}SomeGlobalActor public class C2 +public class C3: C2 { } + +// CHECK: actor public class SomeSubActor +// CHECK-NEXT: @actorIndependent public func maine() +public actor class SomeSubActor: SomeActor { + override public func maine() { } +} From 4032cd8e7d4857dad9e07782212e30848580ca56 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 14 Oct 2020 10:26:57 -0700 Subject: [PATCH 453/745] Rename CustomAttrTypeKind::PropertyDelegate to PropertyWrapper. --- include/swift/AST/TypeCheckRequests.h | 4 ++-- lib/AST/TypeCheckRequests.cpp | 4 ++-- lib/Sema/TypeCheckPropertyWrapper.cpp | 2 +- lib/Sema/TypeCheckType.cpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index a2fc16fd8848c..7349a21aa9dbd 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -2693,9 +2693,9 @@ enum class CustomAttrTypeKind { /// any contextual type parameters. NonGeneric, - /// Property delegates have some funky rules, like allowing + /// Property wrappers have some funky rules, like allowing /// unbound generic types. - PropertyDelegate, + PropertyWrapper, /// Global actors are represented as custom type attributes. They don't /// have any particularly interesting semantics. diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index aec44052dbebd..214a1473f87e5 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1427,8 +1427,8 @@ void swift::simple_display(llvm::raw_ostream &out, CustomAttrTypeKind value) { out << "non-generic"; return; - case CustomAttrTypeKind::PropertyDelegate: - out << "property-delegate"; + case CustomAttrTypeKind::PropertyWrapper: + out << "property-wrapper"; return; case CustomAttrTypeKind::GlobalActor: diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 14f18c8a4f412..9529b61d06270 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -513,7 +513,7 @@ Type AttachedPropertyWrapperTypeRequest::evaluate(Evaluator &evaluator, auto ty = evaluateOrDefault( evaluator, CustomAttrTypeRequest{customAttr, var->getDeclContext(), - CustomAttrTypeKind::PropertyDelegate}, + CustomAttrTypeKind::PropertyWrapper}, Type()); if (!ty || ty->hasError()) { return ErrorType::get(var->getASTContext()); diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 2a2040fc26505..4fd9b91dbda8c 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -3974,7 +3974,7 @@ Type CustomAttrTypeRequest::evaluate(Evaluator &eval, CustomAttr *attr, OpenUnboundGenericTypeFn unboundTyOpener = nullptr; // Property delegates allow their type to be an unbound generic. - if (typeKind == CustomAttrTypeKind::PropertyDelegate) { + if (typeKind == CustomAttrTypeKind::PropertyWrapper) { unboundTyOpener = [](auto unboundTy) { // FIXME: Don't let unbound generic types // escape type resolution. For now, just From e509d6883a068334df96f34ade141d8c38dfd1f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Wed, 14 Oct 2020 10:52:11 -0700 Subject: [PATCH 454/745] Revert "[Sema] Fix availability checking in inlinable code" --- include/swift/AST/DiagnosticsSema.def | 6 - include/swift/AST/TypeRefinementContext.h | 24 +--- lib/AST/Availability.cpp | 16 +-- lib/AST/TypeRefinementContext.cpp | 27 ++-- lib/ClangImporter/ImportDecl.cpp | 13 +- lib/SIL/IR/SILPrinter.cpp | 3 +- lib/Sema/TypeCheckAttr.cpp | 13 +- lib/Sema/TypeCheckAvailability.cpp | 53 ++----- lib/Sema/TypeCheckDeclOverride.cpp | 27 +--- lib/Sema/TypeCheckProtocol.cpp | 10 +- lib/Serialization/SerializeSIL.cpp | 3 +- test/SILGen/vtables.swift | 5 +- test/Sema/availability_inlinable.swift | 48 ------- .../availability_refinement_contexts.swift | 134 ++++++------------ test/Sema/availability_versions.swift | 44 +----- test/attr/attr_availability_maccatalyst.swift | 21 +-- 16 files changed, 97 insertions(+), 350 deletions(-) delete mode 100644 test/Sema/availability_inlinable.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index ae21d7a9ad319..6feed045acaf1 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2455,16 +2455,10 @@ NOTE(suggest_removing_override, none, ERROR(override_less_available,none, "overriding %0 must be as available as declaration it overrides", (DeclBaseName)) -WARNING(override_less_available_warn,none, - "overriding %0 must be as available as declaration it overrides", - (DeclBaseName)) ERROR(override_accessor_less_available,none, "overriding %0 for %1 must be as available as declaration it overrides", (DescriptiveDeclKind, DeclBaseName)) -WARNING(override_accessor_less_available_warn,none, - "overriding %0 for %1 must be as available as declaration it overrides", - (DescriptiveDeclKind, DeclBaseName)) ERROR(override_let_property,none, "cannot override immutable 'let' property %0 with the getter of a 'var'", diff --git a/include/swift/AST/TypeRefinementContext.h b/include/swift/AST/TypeRefinementContext.h index c0851969fd712..2dca10739444e 100644 --- a/include/swift/AST/TypeRefinementContext.h +++ b/include/swift/AST/TypeRefinementContext.h @@ -154,23 +154,16 @@ class TypeRefinementContext { SourceRange SrcRange; - /// Runtime availability information for the code in this context. AvailabilityContext AvailabilityInfo; - /// Runtime availability information as explicitly declared by attributes - /// for the inlinable code in this context. Compared to AvailabilityInfo, - /// this is not bounded to the minimum deployment OS version. - AvailabilityContext AvailabilityInfoExplicit; - std::vector Children; TypeRefinementContext(ASTContext &Ctx, IntroNode Node, TypeRefinementContext *Parent, SourceRange SrcRange, - const AvailabilityContext &Info, - const AvailabilityContext &InfoExplicit); + const AvailabilityContext &Info); public: - + /// Create the root refinement context for the given SourceFile. static TypeRefinementContext *createRoot(SourceFile *SF, const AvailabilityContext &Info); @@ -179,9 +172,8 @@ class TypeRefinementContext { static TypeRefinementContext *createForDecl(ASTContext &Ctx, Decl *D, TypeRefinementContext *Parent, const AvailabilityContext &Info, - const AvailabilityContext &InfoExplicit, SourceRange SrcRange); - + /// Create a refinement context for the Then branch of the given IfStmt. static TypeRefinementContext * createForIfStmtThen(ASTContext &Ctx, IfStmt *S, TypeRefinementContext *Parent, @@ -248,19 +240,11 @@ class TypeRefinementContext { SourceRange getSourceRange() const { return SrcRange; } /// Returns the information on what can be assumed present at run time when - /// running code contained in this context, taking into account the minimum - /// deployment target. + /// running code contained in this context. const AvailabilityContext &getAvailabilityInfo() const { return AvailabilityInfo; } - /// Returns the information on what can be assumed present at run time when - /// running code contained in this context if it were to be inlined, - /// without considering the minimum deployment target. - const AvailabilityContext &getAvailabilityInfoExplicit() const { - return AvailabilityInfoExplicit; - } - /// Adds a child refinement context. void addChild(TypeRefinementContext *Child) { assert(Child->getSourceRange().isValid()); diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index 20e9fb6132ec1..e042a3dda668f 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -144,13 +144,8 @@ static bool isBetterThan(const AvailableAttr *newAttr, return true; // If they belong to the same platform, the one that introduces later wins. - if (prevAttr->Platform == newAttr->Platform) { - if (newAttr->isUnconditionallyUnavailable()) - return true; - if (prevAttr->isUnconditionallyUnavailable()) - return false; + if (prevAttr->Platform == newAttr->Platform) return prevAttr->Introduced.getValue() < newAttr->Introduced.getValue(); - } // If the new attribute's platform inherits from the old one, it wins. return inheritsAvailabilityFromPlatform(newAttr->Platform, @@ -163,12 +158,10 @@ AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) { for (auto Attr : D->getAttrs()) { auto *AvailAttr = dyn_cast(Attr); - if (AvailAttr == nullptr || + if (AvailAttr == nullptr || !AvailAttr->Introduced.hasValue() || !AvailAttr->isActivePlatform(Ctx) || AvailAttr->isLanguageVersionSpecific() || - AvailAttr->isPackageDescriptionVersionSpecific() || - (!AvailAttr->Introduced.hasValue() && - !AvailAttr->isUnconditionallyUnavailable())) { + AvailAttr->isPackageDescriptionVersionSpecific()) { continue; } @@ -179,9 +172,6 @@ AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) { if (!bestAvailAttr) return None; - if (bestAvailAttr->isUnconditionallyUnavailable()) - return AvailabilityContext(VersionRange::empty()); - return AvailabilityContext{ VersionRange::allGTE(bestAvailAttr->Introduced.getValue())}; } diff --git a/lib/AST/TypeRefinementContext.cpp b/lib/AST/TypeRefinementContext.cpp index 3fe283bebd896..c5bcaeb4e0d54 100644 --- a/lib/AST/TypeRefinementContext.cpp +++ b/lib/AST/TypeRefinementContext.cpp @@ -28,14 +28,12 @@ using namespace swift; TypeRefinementContext::TypeRefinementContext(ASTContext &Ctx, IntroNode Node, TypeRefinementContext *Parent, SourceRange SrcRange, - const AvailabilityContext &Info, - const AvailabilityContext &InfoExplicit) - : Node(Node), SrcRange(SrcRange), AvailabilityInfo(Info), - AvailabilityInfoExplicit(InfoExplicit) { + const AvailabilityContext &Info) + : Node(Node), SrcRange(SrcRange), AvailabilityInfo(Info) { if (Parent) { assert(SrcRange.isValid()); Parent->addChild(this); - assert(InfoExplicit.isContainedIn(Parent->getAvailabilityInfoExplicit())); + assert(Info.isContainedIn(Parent->getAvailabilityInfo())); } Ctx.addDestructorCleanup(Children); } @@ -48,20 +46,18 @@ TypeRefinementContext::createRoot(SourceFile *SF, ASTContext &Ctx = SF->getASTContext(); return new (Ctx) TypeRefinementContext(Ctx, SF, - /*Parent=*/nullptr, SourceRange(), Info, - AvailabilityContext::alwaysAvailable()); + /*Parent=*/nullptr, SourceRange(), Info); } TypeRefinementContext * TypeRefinementContext::createForDecl(ASTContext &Ctx, Decl *D, TypeRefinementContext *Parent, const AvailabilityContext &Info, - const AvailabilityContext &InfoExplicit, SourceRange SrcRange) { assert(D); assert(Parent); return new (Ctx) - TypeRefinementContext(Ctx, D, Parent, SrcRange, Info, InfoExplicit); + TypeRefinementContext(Ctx, D, Parent, SrcRange, Info); } TypeRefinementContext * @@ -72,7 +68,7 @@ TypeRefinementContext::createForIfStmtThen(ASTContext &Ctx, IfStmt *S, assert(Parent); return new (Ctx) TypeRefinementContext(Ctx, IntroNode(S, /*IsThen=*/true), Parent, - S->getThenStmt()->getSourceRange(), Info, Info); + S->getThenStmt()->getSourceRange(), Info); } TypeRefinementContext * @@ -83,7 +79,7 @@ TypeRefinementContext::createForIfStmtElse(ASTContext &Ctx, IfStmt *S, assert(Parent); return new (Ctx) TypeRefinementContext(Ctx, IntroNode(S, /*IsThen=*/false), Parent, - S->getElseStmt()->getSourceRange(), Info, Info); + S->getElseStmt()->getSourceRange(), Info); } TypeRefinementContext * @@ -96,7 +92,7 @@ TypeRefinementContext::createForConditionFollowingQuery(ASTContext &Ctx, assert(Parent); SourceRange Range(PAI->getEndLoc(), LastElement.getEndLoc()); return new (Ctx) TypeRefinementContext(Ctx, PAI, Parent, Range, - Info, Info); + Info); } TypeRefinementContext * @@ -111,7 +107,7 @@ TypeRefinementContext::createForGuardStmtFallthrough(ASTContext &Ctx, SourceRange Range(RS->getEndLoc(), ContainingBraceStmt->getEndLoc()); return new (Ctx) TypeRefinementContext(Ctx, IntroNode(RS, /*IsFallthrough=*/true), - Parent, Range, Info, Info); + Parent, Range, Info); } TypeRefinementContext * @@ -122,7 +118,7 @@ TypeRefinementContext::createForGuardStmtElse(ASTContext &Ctx, GuardStmt *RS, assert(Parent); return new (Ctx) TypeRefinementContext(Ctx, IntroNode(RS, /*IsFallthrough=*/false), Parent, - RS->getBody()->getSourceRange(), Info, Info); + RS->getBody()->getSourceRange(), Info); } TypeRefinementContext * @@ -132,7 +128,7 @@ TypeRefinementContext::createForWhileStmtBody(ASTContext &Ctx, WhileStmt *S, assert(S); assert(Parent); return new (Ctx) TypeRefinementContext( - Ctx, S, Parent, S->getBody()->getSourceRange(), Info, Info); + Ctx, S, Parent, S->getBody()->getSourceRange(), Info); } // Only allow allocation of TypeRefinementContext using the allocator in @@ -300,7 +296,6 @@ void TypeRefinementContext::print(raw_ostream &OS, SourceManager &SrcMgr, OS << "(" << getReasonName(getReason()); OS << " versions=" << AvailabilityInfo.getOSVersion().getAsString(); - OS << " explicit=" << AvailabilityInfoExplicit.getOSVersion().getAsString(); if (getReason() == Reason::Decl) { Decl *D = Node.getAsDecl(); diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 81cb3ee453050..079ded2e02094 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -1817,27 +1817,18 @@ static void applyAvailableAttribute(Decl *decl, AvailabilityContext &info, if (info.isAlwaysAvailable()) return; - PlatformAgnosticAvailabilityKind platformAgnosticAvailability; - llvm::VersionTuple introducedVersion; - if (info.isKnownUnreachable()) { - platformAgnosticAvailability = PlatformAgnosticAvailabilityKind::Unavailable; - } else { - platformAgnosticAvailability = PlatformAgnosticAvailabilityKind::None; - introducedVersion = info.getOSVersion().getLowerEndpoint(); - } - llvm::VersionTuple noVersion; auto AvAttr = new (C) AvailableAttr(SourceLoc(), SourceRange(), targetPlatform(C.LangOpts), /*Message=*/StringRef(), /*Rename=*/StringRef(), - introducedVersion, + info.getOSVersion().getLowerEndpoint(), /*IntroducedRange*/SourceRange(), /*Deprecated=*/noVersion, /*DeprecatedRange*/SourceRange(), /*Obsoleted=*/noVersion, /*ObsoletedRange*/SourceRange(), - platformAgnosticAvailability, + PlatformAgnosticAvailabilityKind::None, /*Implicit=*/false); decl->getAttrs().add(AvAttr); diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 65a32b8de3b4a..3b057395d2156 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -2638,8 +2638,7 @@ void SILFunction::print(SILPrintContext &PrintCtx) const { if (isAlwaysWeakImported()) OS << "[weak_imported] "; auto availability = getAvailabilityForLinkage(); - if (!availability.isAlwaysAvailable() && - !availability.isKnownUnreachable()) { + if (!availability.isAlwaysAvailable()) { auto version = availability.getOSVersion().getLowerEndpoint(); OS << "[available " << version.getAsString() << "] "; } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index d0661b189ba3e..29597227c705b 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -1543,16 +1543,9 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *attr) { VersionRange::allGTE(attr->Introduced.getValue())}; if (!AttrRange.isContainedIn(EnclosingAnnotatedRange.getValue())) { - // Don't show this error in swiftinterfaces if it is about a context that - // is unavailable, this was in the stdlib at Swift 5.3. - SourceFile *Parent = D->getDeclContext()->getParentSourceFile(); - if (!Parent || Parent->Kind != SourceFileKind::Interface || - !EnclosingAnnotatedRange.getValue().isKnownUnreachable()) { - diagnose(attr->getLocation(), - diag::availability_decl_more_than_enclosing); - diagnose(EnclosingDecl->getLoc(), - diag::availability_decl_more_than_enclosing_enclosing_here); - } + diagnose(attr->getLocation(), diag::availability_decl_more_than_enclosing); + diagnose(EnclosingDecl->getLoc(), + diag::availability_decl_more_than_enclosing_enclosing_here); } } diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 4acbe673bb823..3cb6978441c93 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -170,30 +170,13 @@ class TypeRefinementContextBuilder : private ASTWalker { // The potential versions in the declaration are constrained by both // the declared availability of the declaration and the potential versions // of its lexical context. - AvailabilityContext ExplicitDeclInfo = + AvailabilityContext DeclInfo = swift::AvailabilityInference::availableRange(D, Context); - ExplicitDeclInfo.intersectWith( - getCurrentTRC()->getAvailabilityInfoExplicit()); - AvailabilityContext DeclInfo = ExplicitDeclInfo; - - // When the body is inlinable consider only the explicitly declared range - // for checking availability. Otherwise, use the parent range which may - // begin at the minimum deployment target. - // - // Also use the parent range when reading swiftinterfaces for - // retrocompatibility. - bool isInlinable = D->getAttrs().hasAttribute() || - D->getAttrs().hasAttribute(); - SourceFile *SF = D->getDeclContext()->getParentSourceFile(); - if (!isInlinable || (SF && SF->Kind == SourceFileKind::Interface)) { - DeclInfo.intersectWith( - getCurrentTRC()->getAvailabilityInfo()); - } + DeclInfo.intersectWith(getCurrentTRC()->getAvailabilityInfo()); TypeRefinementContext *NewTRC = TypeRefinementContext::createForDecl(Context, D, getCurrentTRC(), DeclInfo, - ExplicitDeclInfo, refinementSourceRangeForDecl(D)); // Record the TRC for this storage declaration so that @@ -207,27 +190,19 @@ class TypeRefinementContextBuilder : private ASTWalker { return NewTRC; } - + /// Returns true if the declaration should introduce a new refinement context. bool declarationIntroducesNewContext(Decl *D) { if (!isa(D) && !isa(D)) { return false; } - - // Explicit inlinability may to the decl being used on an earlier OS - // version when inlined on the client side. This check assumes that - // implicit decls are handled elsewhere. - bool isExplicitlyInlinable = !D->isImplicit() && - (D->getAttrs().hasAttribute() || - D->getAttrs().hasAttribute()); - + // No need to introduce a context if the declaration does not have an - // availability or non-implicit inlinable attribute. - if (!hasActiveAvailableAttribute(D, Context) && - !isExplicitlyInlinable) { + // availability attribute. + if (!hasActiveAvailableAttribute(D, Context)) { return false; } - + // Only introduce for an AbstractStorageDecl if it is not local. // We introduce for the non-local case because these may // have getters and setters (and these may be synthesized, so they might @@ -238,7 +213,7 @@ class TypeRefinementContextBuilder : private ASTWalker { return false; } } - + return true; } @@ -699,7 +674,9 @@ TypeChecker::overApproximateAvailabilityAtLocation(SourceLoc loc, // refined. For now, this is fine -- but if we ever synthesize #available(), // this will be a real problem. - auto OverApproximateContext = AvailabilityContext::alwaysAvailable(); + // We can assume we are running on at least the minimum deployment target. + auto OverApproximateContext = + AvailabilityContext::forDeploymentTarget(Context); auto isInvalidLoc = [SF](SourceLoc loc) { return SF ? loc.isInvalid() : true; }; @@ -730,14 +707,6 @@ TypeChecker::overApproximateAvailabilityAtLocation(SourceLoc loc, } } - // If we still don't have an introduction version, use the current deployment - // target. This covers cases where an inlinable function and its parent - // contexts don't have explicit availability attributes. - if (!OverApproximateContext.getOSVersion().hasLowerEndpoint()) { - auto currentOS = AvailabilityContext::forDeploymentTarget(Context); - OverApproximateContext.constrainWith(currentOS); - } - return OverApproximateContext; } diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index f0bc0550a8085..11c7985877a54 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1689,37 +1689,16 @@ static bool diagnoseOverrideForAvailability(ValueDecl *override, if (isRedundantAccessorOverrideAvailabilityDiagnostic(override, base)) return false; - auto &ctx = override->getASTContext(); - auto &diags = ctx.Diags; + auto &diags = override->getASTContext().Diags; if (auto *accessor = dyn_cast(override)) { - bool downgradeToWarning = override->getAttrs().isUnavailable(ctx); - diags.diagnose(override, - downgradeToWarning? - diag::override_accessor_less_available_warn : - diag::override_accessor_less_available, + diags.diagnose(override, diag::override_accessor_less_available, accessor->getDescriptiveKind(), accessor->getStorage()->getBaseName()); diags.diagnose(base, diag::overridden_here); return true; } - bool downgradeToWarning = false; - if (override->getAttrs().isUnavailable(ctx)) { - // Don't report constructors that are marked unavailable as being less - // available than their introduction. This was previously allowed and - // can be used to forbid the direct use of a constructor in a subclass. - // Note that even when marked unavailable the constructor could be called - // by other inherited constructors. - if (isa(override)) - return false; - - // Report as a warning other unavailable overrides. - downgradeToWarning = true; - } - - diags.diagnose(override, - downgradeToWarning? diag::override_less_available_warn : - diag::override_less_available, + diags.diagnose(override, diag::override_less_available, override->getBaseName()); diags.diagnose(base, diag::overridden_here); diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 6016d82df68dc..01cf489a25a6b 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -1455,11 +1455,6 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement, return CheckKind::UsableFromInline; } - if (match.Witness->getAttrs().isUnavailable(getASTContext()) && - !requirement->getAttrs().isUnavailable(getASTContext())) { - return CheckKind::WitnessUnavailable; - } - auto requiredAvailability = AvailabilityContext::alwaysAvailable(); if (checkWitnessAvailability(requirement, match.Witness, &requiredAvailability)) { @@ -1489,6 +1484,11 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement, } } + if (match.Witness->getAttrs().isUnavailable(getASTContext()) && + !requirement->getAttrs().isUnavailable(getASTContext())) { + return CheckKind::WitnessUnavailable; + } + return CheckKind::Success; } diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 767455fa5f004..70fa8597c5470 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -430,8 +430,7 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) { Optional available; auto availability = F.getAvailabilityForLinkage(); - if (!availability.isAlwaysAvailable() && - !availability.isKnownUnreachable()) { + if (!availability.isAlwaysAvailable()) { available = availability.getOSVersion().getLowerEndpoint(); } ENCODE_VER_TUPLE(available, available) diff --git a/test/SILGen/vtables.swift b/test/SILGen/vtables.swift index 9c0f2fafc7369..387fd0df1e69d 100644 --- a/test/SILGen/vtables.swift +++ b/test/SILGen/vtables.swift @@ -21,6 +21,7 @@ class C : B { // CHECK: #A.bar: {{.*}} : @$s7vtables1CC3bar{{[_0-9a-zA-Z]*}}F // CHECK: #A.bas: {{.*}} : @$s7vtables1AC3bas{{[_0-9a-zA-Z]*}}F // CHECK: #A.qux: {{.*}} : @$s7vtables1CC3qux{{[_0-9a-zA-Z]*}}F +// CHECK: #A.flux: {{.*}} : @$s7vtables1BC4flux{{[_0-9a-zA-Z]*}}F // CHECK: #B.init!allocator: {{.*}} : @$s7vtables1CC{{[_0-9a-zA-Z]*}}fC // CHECK: #B.zim: {{.*}} : @$s7vtables1BC3zim{{[_0-9a-zA-Z]*}}F // CHECK: #B.zang: {{.*}} : @$s7vtables1CC4zang{{[_0-9a-zA-Z]*}}F @@ -34,7 +35,7 @@ class A { func bar() {} func bas() {} func qux() {} - @available(*, unavailable) func flux() {} + func flux() {} } // CHECK: sil_vtable A { @@ -53,6 +54,7 @@ class B : A { // bar inherited from A // bas inherited from A override func qux() {} + @available(*, unavailable) override func flux() {} func zim() {} func zang() {} @@ -63,6 +65,7 @@ class B : A { // CHECK: #A.bar: {{.*}} : @$s7vtables1AC3bar{{[_0-9a-zA-Z]*}}F // CHECK: #A.bas: {{.*}} : @$s7vtables1AC3bas{{[_0-9a-zA-Z]*}}F // CHECK: #A.qux: {{.*}} : @$s7vtables1BC3qux{{[_0-9a-zA-Z]*}}F +// CHECK: #A.flux: {{.*}} : @$s7vtables1BC4flux{{[_0-9a-zA-Z]*}}F // CHECK: #B.init!allocator: {{.*}} : @$s7vtables1BC{{[_0-9a-zA-Z]*}}fC // CHECK: #B.zim: {{.*}} : @$s7vtables1BC3zim{{[_0-9a-zA-Z]*}}F // CHECK: #B.zang: {{.*}} : @$s7vtables1BC4zang{{[_0-9a-zA-Z]*}}F diff --git a/test/Sema/availability_inlinable.swift b/test/Sema/availability_inlinable.swift deleted file mode 100644 index 34750bce1134e..0000000000000 --- a/test/Sema/availability_inlinable.swift +++ /dev/null @@ -1,48 +0,0 @@ -/// Inlinable functions should check availability by ignoring the current -/// deployment target as clients could inline the function in a lower target. - -// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx11.0 -// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx10.10 - -// REQUIRES: OS=macosx - -@available(macOS 10.10, *) -@inlinable -public func availMacOS10() { - availMacOS11() // expected-error {{'availMacOS11()' is only available in macOS 11.0 or newer}} - // expected-note @-1 {{add 'if #available' version check}} - - if #available(macOS 11.0, *) { - availMacOS11() - } else { - availMacOS11() // expected-error {{'availMacOS11()' is only available in macOS 11.0 or newer}} - // expected-note @-1 {{add 'if #available' version check}} - } - - if #available(macOS 10.15, *) { - availMacOS11() // expected-error {{'availMacOS11()' is only available in macOS 11.0 or newer}} - // expected-note @-1 {{add 'if #available' version check}} - } - - func nestedFunc() { - availMacOS11() // expected-error {{'availMacOS11()' is only available in macOS 11.0 or newer}} - // expected-note @-1 {{add 'if #available' version check}} - } -} - -@available(macOS 11.0, *) -public func availMacOS11() { } - -@available(macOS 10.10, *) -public struct StructAvailMacOS10 { - @inlinable - public func availabilityFromTheContextInlinable() { - // expected-note @-1 {{add @available attribute to enclosing instance method}} - availMacOS11() // expected-error {{'availMacOS11()' is only available in macOS 11.0 or newer}} - // expected-note @-1 {{add 'if #available' version check}} - - availabilityFromContext() - } - - public func availabilityFromContext() {} -} diff --git a/test/Sema/availability_refinement_contexts.swift b/test/Sema/availability_refinement_contexts.swift index 44f6b8fd05f6e..088a644384ff6 100644 --- a/test/Sema/availability_refinement_contexts.swift +++ b/test/Sema/availability_refinement_contexts.swift @@ -1,18 +1,18 @@ -// RUN: %target-swift-frontend -typecheck -dump-type-refinement-contexts -target %target-cpu-apple-macosx10.50 %s > %t.dump 2>&1 +// RUN: %target-swift-frontend -typecheck -dump-type-refinement-contexts %s > %t.dump 2>&1 // RUN: %FileCheck --strict-whitespace %s < %t.dump // REQUIRES: OS=macosx -// CHECK: {{^}}(root versions=[10.50.0,+Inf) explicit=all +// CHECK: {{^}}(root versions=[10.{{[0-9]+}}.0,+Inf) -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=SomeClass -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=someMethod() -// CHECK-NEXT: {{^}} (decl versions=[10.53,+Inf) explicit=[10.53,+Inf) decl=someInnerFunc() -// CHECK-NEXT: {{^}} (decl versions=[10.53,+Inf) explicit=[10.53,+Inf) decl=InnerClass -// CHECK-NEXT: {{^}} (decl versions=[10.54,+Inf) explicit=[10.54,+Inf) decl=innerClassMethod -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=someStaticProperty -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=someComputedProperty -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=someOtherMethod() +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=SomeClass +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=someMethod() +// CHECK-NEXT: {{^}} (decl versions=[10.53,+Inf) decl=someInnerFunc() +// CHECK-NEXT: {{^}} (decl versions=[10.53,+Inf) decl=InnerClass +// CHECK-NEXT: {{^}} (decl versions=[10.54,+Inf) decl=innerClassMethod +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=someStaticProperty +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=someComputedProperty +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=someOtherMethod() @available(OSX 10.51, *) class SomeClass { @available(OSX 10.52, *) @@ -43,13 +43,13 @@ class SomeClass { func someOtherMethod() { } } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=someFunction() +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=someFunction() @available(OSX 10.51, *) func someFunction() { } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=SomeProtocol -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=protoMethod() -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=protoProperty +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=SomeProtocol +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=protoMethod() +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=protoProperty @available(OSX 10.51, *) protocol SomeProtocol { @available(OSX 10.52, *) @@ -59,26 +59,26 @@ protocol SomeProtocol { var protoProperty: Int { get } } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=extension.SomeClass -// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) explicit=[10.52,+Inf) decl=someExtensionFunction() +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=extension.SomeClass +// CHECK-NEXT: {{^}} (decl versions=[10.52,+Inf) decl=someExtensionFunction() @available(OSX 10.51, *) extension SomeClass { @available(OSX 10.52, *) func someExtensionFunction() { } } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=functionWithStmtCondition -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.52,+Inf) explicit=[10.52,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.53,+Inf) explicit=[10.53,+Inf) -// CHECK-NEXT: {{^}} (if_then versions=[10.53,+Inf) explicit=[10.53,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.54,+Inf) explicit=[10.54,+Inf) -// CHECK-NEXT: {{^}} (if_then versions=[10.54,+Inf) explicit=[10.54,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.55,+Inf) explicit=[10.55,+Inf) -// CHECK-NEXT: {{^}} (decl versions=[10.55,+Inf) explicit=[10.55,+Inf) decl=funcInGuardElse() -// CHECK-NEXT: {{^}} (guard_fallthrough versions=[10.55,+Inf) explicit=[10.55,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.56,+Inf) explicit=[10.56,+Inf) -// CHECK-NEXT: {{^}} (guard_fallthrough versions=[10.56,+Inf) explicit=[10.56,+Inf) -// CHECK-NEXT: {{^}} (decl versions=[10.57,+Inf) explicit=[10.57,+Inf) decl=funcInInnerIfElse() +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=functionWithStmtCondition +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.52,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.53,+Inf) +// CHECK-NEXT: {{^}} (if_then versions=[10.53,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.54,+Inf) +// CHECK-NEXT: {{^}} (if_then versions=[10.54,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.55,+Inf) +// CHECK-NEXT: {{^}} (decl versions=[10.55,+Inf) decl=funcInGuardElse() +// CHECK-NEXT: {{^}} (guard_fallthrough versions=[10.55,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.56,+Inf) +// CHECK-NEXT: {{^}} (guard_fallthrough versions=[10.56,+Inf) +// CHECK-NEXT: {{^}} (decl versions=[10.57,+Inf) decl=funcInInnerIfElse() @available(OSX 10.51, *) func functionWithStmtCondition() { if #available(OSX 10.52, *), @@ -97,11 +97,11 @@ func functionWithStmtCondition() { } } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=functionWithUnnecessaryStmtCondition -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.53,+Inf) explicit=[10.53,+Inf) -// CHECK-NEXT: {{^}} (if_then versions=[10.53,+Inf) explicit=[10.53,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.54,+Inf) explicit=[10.54,+Inf) -// CHECK-NEXT: {{^}} (if_then versions=[10.54,+Inf) explicit=[10.54,+Inf) +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=functionWithUnnecessaryStmtCondition +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.53,+Inf) +// CHECK-NEXT: {{^}} (if_then versions=[10.53,+Inf) +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.54,+Inf) +// CHECK-NEXT: {{^}} (if_then versions=[10.54,+Inf) @available(OSX 10.51, *) func functionWithUnnecessaryStmtCondition() { @@ -127,13 +127,13 @@ func functionWithUnnecessaryStmtCondition() { } } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=functionWithUnnecessaryStmtConditionsHavingElseBranch -// CHECK-NEXT: {{^}} (if_else versions=empty explicit=empty -// CHECK-NEXT: {{^}} (decl versions=empty explicit=empty decl=funcInInnerIfElse() -// CHECK-NEXT: {{^}} (if_else versions=empty explicit=empty -// CHECK-NEXT: {{^}} (guard_else versions=empty explicit=empty -// CHECK-NEXT: {{^}} (guard_else versions=empty explicit=empty -// CHECK-NEXT: {{^}} (if_else versions=empty explicit=empty +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=functionWithUnnecessaryStmtConditionsHavingElseBranch +// CHECK-NEXT: {{^}} (if_else versions=empty +// CHECK-NEXT: {{^}} (decl versions=empty decl=funcInInnerIfElse() +// CHECK-NEXT: {{^}} (if_else versions=empty +// CHECK-NEXT: {{^}} (guard_else versions=empty +// CHECK-NEXT: {{^}} (guard_else versions=empty +// CHECK-NEXT: {{^}} (if_else versions=empty @available(OSX 10.51, *) func functionWithUnnecessaryStmtConditionsHavingElseBranch(p: Int?) { @@ -180,10 +180,10 @@ func functionWithUnnecessaryStmtConditionsHavingElseBranch(p: Int?) { } -// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) explicit=[10.51,+Inf) decl=functionWithWhile() -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.52,+Inf) explicit=[10.52,+Inf) -// CHECK-NEXT: {{^}} (while_body versions=[10.52,+Inf) explicit=[10.52,+Inf) -// CHECK-NEXT: {{^}} (decl versions=[10.54,+Inf) explicit=[10.54,+Inf) decl=funcInWhileBody() +// CHECK-NEXT: {{^}} (decl versions=[10.51,+Inf) decl=functionWithWhile() +// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.52,+Inf) +// CHECK-NEXT: {{^}} (while_body versions=[10.52,+Inf) +// CHECK-NEXT: {{^}} (decl versions=[10.54,+Inf) decl=funcInWhileBody() @available(OSX 10.51, *) func functionWithWhile() { while #available(OSX 10.52, *), @@ -192,49 +192,3 @@ func functionWithWhile() { func funcInWhileBody() { } } } - -// CHECK-NEXT: {{^}} (decl versions=[10.50.0,+Inf) explicit=[10.10,+Inf) decl=olderFunction() -@available(macOS 10.10, *) -public func olderFunction() { -} - -// CHECK-NEXT: {{^}} (decl versions=empty explicit=empty decl=unavailableFunction() -@available(macOS, unavailable) -public func unavailableFunction() { -} - -// CHECK-NEXT: {{^}} (decl versions=[10.10,+Inf) explicit=[10.10,+Inf) decl=inlinableFunction() -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.55,+Inf) explicit=[10.55,+Inf) -// CHECK-NEXT: {{^}} (if_then versions=[10.55,+Inf) explicit=[10.55,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.15,+Inf) -// CHECK-NEXT: {{^}} (if_then versions=[10.15,+Inf) explicit=[10.15,+Inf) -// CHECK-NEXT: {{^}} (condition_following_availability versions=[10.15,+Inf) explicit=[10.15,+Inf) -// CHECK-NEXT: {{^}} (guard_fallthrough versions=[10.15,+Inf) explicit=[10.15,+Inf) -@available(macOS 10.10, *) -@inlinable -public func inlinableFunction() { - if #available(macOS 10.55, *) { } else { } - - if #available(macOS 10.15, *) { } - - guard #available(macOS 10.15, *) else { } - - func nestedFunc() { } -} - -// CHECK-NEXT: {{^}} (decl versions=[10.50.0,+Inf) explicit=[10.10,+Inf) decl=SomeOlderClass -// CHECK-NEXT: {{^}} (decl versions=[10.10,+Inf) explicit=[10.10,+Inf) decl=someInlinableMethod() -// CHECK-NEXT: {{^}} (decl versions=[10.15,+Inf) explicit=[10.15,+Inf) decl=someOtherInlinableMethod() -// CHECK-NEXT: {{^}} (decl versions=[10.50.0,+Inf) explicit=[10.15,+Inf) decl=someMethod() -@available(OSX 10.10, *) -class SomeOlderClass { - @inlinable - func someInlinableMethod() { } - - @available(OSX 10.15, *) - @inlinable - func someOtherInlinableMethod() { } - - @available(OSX 10.15, *) - func someMethod() { } -} diff --git a/test/Sema/availability_versions.swift b/test/Sema/availability_versions.swift index 97ec079f50d06..ad4159215becf 100644 --- a/test/Sema/availability_versions.swift +++ b/test/Sema/availability_versions.swift @@ -788,33 +788,26 @@ class UnavailableClassExtendingUnavailableClass : ClassAvailableOn10_51 { // Method availability is contravariant class SuperWithAlwaysAvailableMembers { - - required init() {} // expected-note {{overridden declaration is here}} - - func shouldAlwaysBeAvailableMethod() { // expected-note 2 {{overridden declaration is here}} + func shouldAlwaysBeAvailableMethod() { // expected-note {{overridden declaration is here}} } - var shouldAlwaysBeAvailableProperty: Int { // expected-note 2 {{overridden declaration is here}} + var shouldAlwaysBeAvailableProperty: Int { // expected-note {{overridden declaration is here}} get { return 9 } set(newVal) {} } var setterShouldAlwaysBeAvailableProperty: Int { get { return 9 } - set(newVal) {} // expected-note 2 {{overridden declaration is here}} + set(newVal) {} // expected-note {{overridden declaration is here}} } var getterShouldAlwaysBeAvailableProperty: Int { - get { return 9 } // expected-note 2 {{overridden declaration is here}} + get { return 9 } // expected-note {{overridden declaration is here}} set(newVal) {} } } class SubWithLimitedMemberAvailability : SuperWithAlwaysAvailableMembers { - - @available(OSX, introduced: 10.51) - required init() {} // expected-error {{overriding 'init' must be as available as declaration it overrides}} - @available(OSX, introduced: 10.51) override func shouldAlwaysBeAvailableMethod() { // expected-error {{overriding 'shouldAlwaysBeAvailableMethod' must be as available as declaration it overrides}} } @@ -839,35 +832,6 @@ class SubWithLimitedMemberAvailability : SuperWithAlwaysAvailableMembers { } } -class SubWithUnavailableMembers : SuperWithAlwaysAvailableMembers { - - @available(OSX, unavailable) - required init() {} - - @available(OSX, unavailable) - override func shouldAlwaysBeAvailableMethod() { // expected-warning {{overriding 'shouldAlwaysBeAvailableMethod' must be as available as declaration it overrides}} - } - - @available(OSX, unavailable) - override var shouldAlwaysBeAvailableProperty: Int { // expected-warning {{overriding 'shouldAlwaysBeAvailableProperty' must be as available as declaration it overrides}} - get { return 10 } - set(newVal) {} - } - - override var setterShouldAlwaysBeAvailableProperty: Int { - get { return 9 } - @available(OSX, unavailable) - set(newVal) {} // expected-warning {{overriding setter for 'setterShouldAlwaysBeAvailableProperty' must be as available as declaration it overrides}} - // This is a terrible diagnostic. rdar://problem/20427938 - } - - override var getterShouldAlwaysBeAvailableProperty: Int { - @available(OSX, unavailable) - get { return 9 } // expected-warning {{overriding getter for 'getterShouldAlwaysBeAvailableProperty' must be as available as declaration it overrides}} - set(newVal) {} - } -} - class SuperWithLimitedMemberAvailability { @available(OSX, introduced: 10.51) func someMethod() { diff --git a/test/attr/attr_availability_maccatalyst.swift b/test/attr/attr_availability_maccatalyst.swift index 2346cb9ec0dfc..f69cc8aea1b3d 100644 --- a/test/attr/attr_availability_maccatalyst.swift +++ b/test/attr/attr_availability_maccatalyst.swift @@ -1,6 +1,6 @@ // RUN: %swift -typecheck -verify -parse-stdlib -target x86_64-apple-ios51.0-macabi %s -// REQUIRES: VENDOR=apple +// REQUIRES: OS=maccatalyst @available(macCatalyst, introduced: 1.0, deprecated: 2.0, obsoleted: 9.0, message: "you don't want to do that anyway") @@ -52,16 +52,6 @@ func introducedLaterOnMacCatalyst() { func introducedLaterOnIOS() { } -@available(iOS, introduced: 1.0) -@available(macCatalyst, unavailable) -func unavailableOnMacCatalyst() { // expected-note 3 {{'unavailableOnMacCatalyst()' has been explicitly marked unavailable here}} -} - -@available(iOS, unavailable) -@available(macCatalyst, introduced: 1.0) -func unavailableOnIOS() { -} - // expected-note@+1 *{{add @available attribute to enclosing global function}} func testPoundAvailable() { @@ -70,9 +60,6 @@ func testPoundAvailable() { // expected-note@-1 {{add 'if #available' version check}} introducedLaterOnIOS() // expected-error {{'introducedLaterOnIOS()' is only available in Mac Catalyst 56.0 or newer}} // expected-note@-1 {{add 'if #available' version check}} - - unavailableOnMacCatalyst() // expected-error {{'unavailableOnMacCatalyst()' is unavailable in Mac Catalyst}} - unavailableOnIOS() } // macCatalyst should win over iOS when present @@ -82,9 +69,6 @@ func testPoundAvailable() { // expected-note@-1 {{add 'if #available' version check}} introducedLaterOnIOS() // expected-error {{'introducedLaterOnIOS()' is only available in Mac Catalyst 56.0 or newer}} // expected-note@-1 {{add 'if #available' version check}} - - unavailableOnMacCatalyst() // expected-error {{'unavailableOnMacCatalyst()' is unavailable in Mac Catalyst}} - unavailableOnIOS() } if #available(iOS 55.0, macCatalyst 56.0, *) { @@ -104,9 +88,6 @@ func testPoundAvailable() { // expected-note@-1 {{add 'if #available' version check}} introducedLaterOnIOS() // expected-error {{'introducedLaterOnIOS()' is only available in Mac Catalyst 56.0 or newer}} // expected-note@-1 {{add 'if #available' version check}} - - unavailableOnMacCatalyst() // expected-error {{'unavailableOnMacCatalyst()' is unavailable in Mac Catalyst}} - unavailableOnIOS() } if #available(iOS 56.0, *) { From 29093b15df24e98dda65cd51e26a7bd618a2d709 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Wed, 14 Oct 2020 12:18:35 -0700 Subject: [PATCH 455/745] Driver: -L= belongs to the linker option group rdar://70300892 --- include/swift/Option/Options.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 7bf0b4a70a8fe..a7547774da71f 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -672,7 +672,7 @@ def framework : Separate<["-"], "framework">, Group, def L : JoinedOrSeparate<["-"], "L">, Group, Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath]>, HelpText<"Add directory to library link search path">; -def L_EQ : Joined<["-"], "L=">, +def L_EQ : Joined<["-"], "L=">, Group, Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath]>, Alias; From a7259558238eadd1c69a234baef1b67ab45f8492 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Wed, 14 Oct 2020 13:10:19 -0700 Subject: [PATCH 456/745] More renaming --- lib/AST/ModuleNameLookup.cpp | 5 +++-- lib/AST/NameLookup.cpp | 5 +++-- lib/Sema/TypeCheckDecl.cpp | 4 ++-- lib/Sema/TypeCheckGeneric.cpp | 2 +- lib/Sema/TypeCheckType.cpp | 4 ++-- lib/Sema/TypeCheckType.h | 2 +- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/AST/ModuleNameLookup.cpp b/lib/AST/ModuleNameLookup.cpp index 4fbe9313bdbff..22674d560f0ba 100644 --- a/lib/AST/ModuleNameLookup.cpp +++ b/lib/AST/ModuleNameLookup.cpp @@ -145,7 +145,7 @@ void ModuleNameLookup::lookupInModule( const size_t initialCount = decls.size(); size_t currentCount = decls.size(); - bool includeInlineable = options & NL_IncludeUsableFromInline; + bool includeUsableFromInline = options & NL_IncludeUsableFromInline; auto updateNewDecls = [&](const DeclContext *moduleScopeContext) { if (decls.size() == currentCount) @@ -157,7 +157,8 @@ void ModuleNameLookup::lookupInModule( if (resolutionKind == ResolutionKind::TypesOnly && !isa(VD)) return true; if (respectAccessControl && - !VD->isAccessibleFrom(moduleScopeContext, false, includeInlineable)) + !VD->isAccessibleFrom(moduleScopeContext, false, + includeUsableFromInline)) return true; return false; }); diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index e7fd2f65cb5b4..bc323187263f9 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -1502,8 +1502,9 @@ static bool isAcceptableLookupResult(const DeclContext *dc, // Check access. if (!(options & NL_IgnoreAccessControl) && !dc->getASTContext().isAccessControlDisabled()) { - bool allowInlinable = options & NL_IncludeUsableFromInline; - return decl->isAccessibleFrom(dc, /*forConformance*/ false, allowInlinable); + bool allowUsableFromInline = options & NL_IncludeUsableFromInline; + return decl->isAccessibleFrom(dc, /*forConformance*/ false, + allowUsableFromInline); } return true; diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index bf400f54c9e32..d0ac96b22ea22 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2190,7 +2190,7 @@ static Type validateParameterType(ParamDecl *decl) { options |= TypeResolutionFlags::Direct; if (dc->isInSpecializeExtensionContext()) - options |= TypeResolutionFlags::AllowInlinable; + options |= TypeResolutionFlags::AllowUsableFromInline; const auto resolution = TypeResolution::forInterface(dc, options, unboundTyOpener); @@ -2727,7 +2727,7 @@ ExtendedTypeRequest::evaluate(Evaluator &eval, ExtensionDecl *ext) const { // Compute the extended type. TypeResolutionOptions options(TypeResolverContext::ExtensionBinding); if (ext->isInSpecializeExtensionContext()) - options |= TypeResolutionFlags::AllowInlinable; + options |= TypeResolutionFlags::AllowUsableFromInline; const auto resolution = TypeResolution::forStructural( ext->getDeclContext(), options, [](auto unboundTy) { diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index da60f36c5b31a..fb07113c6c560 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -905,7 +905,7 @@ RequirementRequest::evaluate(Evaluator &evaluator, // Figure out the type resolution. auto options = TypeResolutionOptions(TypeResolverContext::GenericRequirement); if (owner.dc->isInSpecializeExtensionContext()) - options |= TypeResolutionFlags::AllowInlinable; + options |= TypeResolutionFlags::AllowUsableFromInline; Optional resolution; switch (stage) { case TypeResolutionStage::Structural: diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index da26ab89a5b9b..f6376064dee61 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1322,7 +1322,7 @@ static Type resolveTopLevelIdentTypeComponent(TypeResolution resolution, } NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; - if (options.contains(TypeResolutionFlags::AllowInlinable)) + if (options.contains(TypeResolutionFlags::AllowUsableFromInline)) lookupOptions |= NameLookupFlags::IncludeUsableFromInline; auto globals = TypeChecker::lookupUnqualifiedType(DC, id, comp->getLoc(), lookupOptions); @@ -1526,7 +1526,7 @@ static Type resolveNestedIdentTypeComponent(TypeResolution resolution, // Look for member types with the given name. NameLookupOptions lookupOptions = defaultMemberLookupOptions; - if (options.contains(TypeResolutionFlags::AllowInlinable)) + if (options.contains(TypeResolutionFlags::AllowUsableFromInline)) lookupOptions |= NameLookupFlags::IncludeUsableFromInline; LookupTypeResult memberTypes; if (parentTy->mayHaveMembers()) diff --git a/lib/Sema/TypeCheckType.h b/lib/Sema/TypeCheckType.h index 098e9fd1e31bd..dcc7d7f4ae37b 100644 --- a/lib/Sema/TypeCheckType.h +++ b/lib/Sema/TypeCheckType.h @@ -66,7 +66,7 @@ enum class TypeResolutionFlags : uint16_t { AllowModule = 1 << 9, /// Make internal @usableFromInline and @inlinable decls visible. - AllowInlinable = 1 << 10, + AllowUsableFromInline = 1 << 10, }; /// Type resolution contexts that require special handling. From d6d921d7fafcfa67e500e6013f93ac30f6248b79 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Wed, 14 Oct 2020 08:30:12 -0700 Subject: [PATCH 457/745] IRGen: Fix fixLifetime emission of enums rdar://70270724 --- lib/IRGen/GenEnum.cpp | 4 ++-- test/IRGen/fixlifetime.sil | 22 +++++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/IRGen/GenEnum.cpp b/lib/IRGen/GenEnum.cpp index a2666b7fad980..93d7b4dc7f512 100644 --- a/lib/IRGen/GenEnum.cpp +++ b/lib/IRGen/GenEnum.cpp @@ -2716,8 +2716,8 @@ namespace { // Unpack as an instance of the payload type and use its fixLifetime // operation. Explosion srcAsPayload; - unpackIntoPayloadExplosion(IGF, srcAsPayload, srcAsPayload); - payloadTI.fixLifetime(IGF, src); + unpackIntoPayloadExplosion(IGF, src, srcAsPayload); + payloadTI.fixLifetime(IGF, srcAsPayload); return; } } diff --git a/test/IRGen/fixlifetime.sil b/test/IRGen/fixlifetime.sil index bc05fde771f7a..8e974ec582f0a 100644 --- a/test/IRGen/fixlifetime.sil +++ b/test/IRGen/fixlifetime.sil @@ -8,7 +8,7 @@ // unnecessary. // ONONE-NOT: @__swift_fixLifetime -// CHECK-objc-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test(%T11fixlifetime1CC* %0, %objc_object* %1, i8** %2, i8* %3, %swift.refcounted* %4, %T11fixlifetime3AggV* noalias nocapture dereferenceable({{.*}}) %5) {{.*}} { +// CHECK-objc-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test(%T11fixlifetime1CC* %0, %objc_object* %1, i8** %2, i8* %3, %swift.refcounted* %4, %T11fixlifetime3AggV* noalias nocapture dereferenceable({{.*}}) %5, i64 %6, i64 %7) {{.*}} { // CHECK-objc: entry: // CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%T11fixlifetime1CC*)*)(%T11fixlifetime1CC* // CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%objc_object*)*)(%objc_object* @@ -16,9 +16,11 @@ // CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%T11fixlifetime1CC*)*)(%T11fixlifetime1CC* // CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%objc_object*)*)(%objc_object* // CHECK-objc: call void @__swift_fixLifetime(%swift.refcounted* +// CHECK-objc: [[TEMP:%.*]] = inttoptr i64 %6 to %objc_object* +// CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%objc_object*)*)(%objc_object* [[TEMP]]) // CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%T11fixlifetime1CC**)*)(%T11fixlifetime1CC** -// CHECK-native-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test(%T11fixlifetime1CC* %0, %swift.refcounted* %1, i8** %2, i8* %3, %swift.refcounted* %4, %T11fixlifetime3AggV* noalias nocapture dereferenceable({{.*}}) %5) {{.*}} { +// CHECK-native-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test(%T11fixlifetime1CC* %0, %swift.refcounted* %1, i8** %2, i8* %3, %swift.refcounted* %4, %T11fixlifetime3AggV* noalias nocapture dereferenceable({{.*}}) %5, i64 %6, i64 %7) {{.*}} { // CHECK-native: entry: // CHECK-native: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%T11fixlifetime1CC*)*)(%T11fixlifetime1CC* // CHECK-native: call void @__swift_fixLifetime(%swift.refcounted* @@ -44,17 +46,23 @@ struct Agg { var f : F } +enum MyOptional { + case none + case some(T) +} + sil [Onone] @test - : $@convention(thin) (C, P, @callee_owned () -> (), Agg) -> () { -bb0(%0 : $C, %1 : $P, %2 : $@callee_owned () -> (), %3 : $Agg): + : $@convention(thin) (C, P, @callee_owned () -> (), Agg, @guaranteed MyOptional

) -> () { +bb0(%0 : $C, %1 : $P, %2 : $@callee_owned () -> (), %3 : $Agg, %4 : $MyOptional

): fix_lifetime %0 : $C fix_lifetime %1 : $P fix_lifetime %2 : $@callee_owned () -> () fix_lifetime %3 : $Agg + fix_lifetime %4 : $MyOptional

- %4 = alloc_stack $C - fix_lifetime %4 : $*C - dealloc_stack %4 : $*C + %5 = alloc_stack $C + fix_lifetime %5 : $*C + dealloc_stack %5 : $*C %9999 = tuple() return %9999 : $() } From 6125d25cb4f5059f455b3c6f4bde70f6aa79f59e Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 14 Oct 2020 13:26:09 -0700 Subject: [PATCH 458/745] [NFC] Silence Non-Exhaustive Switch Warnings on Windows --- include/swift/AST/Decl.h | 1 + include/swift/AST/ForeignErrorConvention.h | 1 + include/swift/Demangling/TypeLookupError.h | 1 + include/swift/Sema/ConstraintSystem.h | 2 ++ lib/AST/AutoDiff.cpp | 1 + lib/AST/ClangTypeConverter.cpp | 1 + lib/AST/TypeCheckRequests.cpp | 2 ++ lib/Driver/DarwinToolChains.cpp | 1 + lib/IDE/APIDigesterData.cpp | 1 + lib/IRGen/MetadataLayout.cpp | 1 + lib/IRGen/MetadataRequest.cpp | 1 + lib/SILOptimizer/Differentiation/PullbackCloner.cpp | 1 + lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h | 2 ++ lib/SILOptimizer/Transforms/AllocBoxToStack.cpp | 1 + lib/Sema/TypeCheckConcurrency.cpp | 3 +++ lib/Sema/TypeCheckRequestFunctions.cpp | 1 + 16 files changed, 21 insertions(+) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 75776ff3610f8..e8b6870d21025 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3762,6 +3762,7 @@ struct SelfReferencePosition final { case Contravariant: return Covariant; } + llvm_unreachable("unhandled self reference position!"); } explicit operator bool() const { return kind > None; } diff --git a/include/swift/AST/ForeignErrorConvention.h b/include/swift/AST/ForeignErrorConvention.h index d09a7a80c0d86..3d9d95280c0e2 100644 --- a/include/swift/AST/ForeignErrorConvention.h +++ b/include/swift/AST/ForeignErrorConvention.h @@ -198,6 +198,7 @@ class ForeignErrorConvention { case NonNilError: return false; } + llvm_unreachable("unhandled foreign error kind!"); } bool operator==(ForeignErrorConvention other) const { diff --git a/include/swift/Demangling/TypeLookupError.h b/include/swift/Demangling/TypeLookupError.h index c67fc35598be8..cf2c75087e0fa 100644 --- a/include/swift/Demangling/TypeLookupError.h +++ b/include/swift/Demangling/TypeLookupError.h @@ -150,6 +150,7 @@ class TypeLookupError { delete castContext; return nullptr; } + llvm_unreachable("unhandled command!"); }; } diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 0642dedac73ba..5dd0778a492ee 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -1907,6 +1907,7 @@ class SolutionApplicationTarget { case Kind::patternBinding: return patternBinding; } + llvm_unreachable("invalid case label type"); } VarDecl *getAsUninitializedWrappedVar() const { @@ -1921,6 +1922,7 @@ class SolutionApplicationTarget { case Kind::uninitializedWrappedVar: return uninitializedWrappedVar; } + llvm_unreachable("invalid case label type"); } BraceStmt *getFunctionBody() const { diff --git a/lib/AST/AutoDiff.cpp b/lib/AST/AutoDiff.cpp index 492e38a2cb861..566de24dae297 100644 --- a/lib/AST/AutoDiff.cpp +++ b/lib/AST/AutoDiff.cpp @@ -462,6 +462,7 @@ bool swift::operator==(const TangentPropertyInfo::Error &lhs, case TangentPropertyInfo::Error::Kind::TangentPropertyWrongType: return lhs.getType()->isEqual(rhs.getType()); } + llvm_unreachable("unhandled tangent property!"); } void swift::simple_display(llvm::raw_ostream &os, TangentPropertyInfo info) { diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index 9fd13f797798d..365d9b72e3d03 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -207,6 +207,7 @@ const clang::Type *ClangTypeConverter::getFunctionType( case SILFunctionType::Representation::Closure: llvm_unreachable("Expected a C-compatible representation."); } + llvm_unreachable("unhandled representation!"); } clang::QualType ClangTypeConverter::convertMemberType(NominalTypeDecl *DC, diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 707b0cdecb62c..30a5d1c84a02d 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1497,6 +1497,7 @@ bool ActorIsolation::requiresSubstitution() const { case GlobalActor: return getGlobalActor()->hasTypeParameter(); } + llvm_unreachable("unhandled actor isolation kind!"); } ActorIsolation ActorIsolation::subst(SubstitutionMap subs) const { @@ -1509,6 +1510,7 @@ ActorIsolation ActorIsolation::subst(SubstitutionMap subs) const { case GlobalActor: return forGlobalActor(getGlobalActor().subst(subs)); } + llvm_unreachable("unhandled actor isolation kind!"); } void swift::simple_display( diff --git a/lib/Driver/DarwinToolChains.cpp b/lib/Driver/DarwinToolChains.cpp index 0f4ffda29b3fb..95accad29f2bd 100644 --- a/lib/Driver/DarwinToolChains.cpp +++ b/lib/Driver/DarwinToolChains.cpp @@ -364,6 +364,7 @@ bool jobMatchesFilter(LinkKind jobKind, BackDeployLibFilter filter) { case BackDeployLibFilter::all: return true; } + llvm_unreachable("unhandled back deploy lib filter!"); } } diff --git a/lib/IDE/APIDigesterData.cpp b/lib/IDE/APIDigesterData.cpp index 5bda079279477..d4d9a6655aba1 100644 --- a/lib/IDE/APIDigesterData.cpp +++ b/lib/IDE/APIDigesterData.cpp @@ -56,6 +56,7 @@ StringRef swift::ide::api::getDeclKindStr(const DeclKind Value, bool lower) { } #include "swift/AST/DeclNodes.def" } + llvm_unreachable("Unhandled DeclKind in switch."); } else { return getDeclKindStrRaw(Value); } diff --git a/lib/IRGen/MetadataLayout.cpp b/lib/IRGen/MetadataLayout.cpp index e93d370240ea4..5ea2eb6ee2be4 100644 --- a/lib/IRGen/MetadataLayout.cpp +++ b/lib/IRGen/MetadataLayout.cpp @@ -414,6 +414,7 @@ ClassMetadataLayout::getMethodInfo(IRGenFunction &IGF, SILDeclRef method) const{ case MethodInfo::Kind::DirectImpl: return MethodInfo(stored.TheImpl); } + llvm_unreachable("unhandled method info kind!"); } Offset ClassMetadataLayout::getFieldOffset(IRGenFunction &IGF, diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index d3f9fe58b6a16..34b1b05b042dc 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -616,6 +616,7 @@ static MetadataResponse emitNominalPrespecializedGenericMetadataRef( return MetadataResponse::handle(IGF, request, call); } } + llvm_unreachable("unhandled metadata canonicality"); } static llvm::Value * diff --git a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp index 29adb43cfca57..38f94d73f8fec 100644 --- a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp +++ b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp @@ -336,6 +336,7 @@ class PullbackCloner::Implementation final case AdjointValueKind::Concrete: return val.getConcreteValue(); } + llvm_unreachable("unhandled adjoint value kind!"); } /// Materializes an adjoint value indirectly to a SIL buffer. diff --git a/lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h b/lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h index 169eb9859bea8..88ba1007d2ea2 100644 --- a/lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h +++ b/lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h @@ -88,6 +88,7 @@ class LLVM_LIBRARY_VISIBILITY OwnershipPhiOperand { case Kind::Struct: return false; } + llvm_unreachable("unhandled operand kind!"); } bool operator<(const OwnershipPhiOperand &other) const { @@ -113,6 +114,7 @@ class LLVM_LIBRARY_VISIBILITY OwnershipPhiOperand { }); } } + llvm_unreachable("unhandled operand kind!"); } }; diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp index 6483b47a12a6f..fd7695b164640 100644 --- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp +++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp @@ -984,6 +984,7 @@ specializeApplySite(SILOptFunctionBuilder &FuncBuilder, ApplySite Apply, GenericSpecializationInformation::create(ApplyInst, Builder)); } } + llvm_unreachable("unhandled apply inst kind!"); } static void rewriteApplySites(AllocBoxToStackState &pass) { diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 474512979861c..0215c6687e89d 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -825,6 +825,7 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { // Okay. return false; } + llvm_unreachable("unhandled actor isolation kind!"); } /// Check a reference to a local or global. @@ -868,6 +869,7 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { case ActorIsolationRestriction::Unsafe: return diagnoseReferenceToUnsafe(value, loc); } + llvm_unreachable("unhandled actor isolation kind!"); } /// Check a reference with the given base expression to the given member. @@ -964,6 +966,7 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { case ActorIsolationRestriction::Unsafe: return diagnoseReferenceToUnsafe(member, memberLoc); } + llvm_unreachable("unhandled actor isolation kind!"); } }; diff --git a/lib/Sema/TypeCheckRequestFunctions.cpp b/lib/Sema/TypeCheckRequestFunctions.cpp index c3abd1a067820..78d4c0c231f2d 100644 --- a/lib/Sema/TypeCheckRequestFunctions.cpp +++ b/lib/Sema/TypeCheckRequestFunctions.cpp @@ -262,6 +262,7 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { case DynamicReplacement: return dynamicReplacement->getName(); } + llvm_unreachable("unhandled decl name kind!"); } }; From 9c879c008e642278b2bce557db19b78ca391caad Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 14 Oct 2020 13:27:21 -0700 Subject: [PATCH 459/745] Resolve Virtual Inheritance By Dominance By Being Explicit Since the alternative is name lookup selecting members from a pure virtual base, it's probably best to be explicit here. --- lib/IRGen/IRGenSIL.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 0997f60e2a8a6..9661d638d83e9 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1219,6 +1219,13 @@ class SyncNativeCCEntryPointArgumentEmission final llvm::Value *getCoroutineBuffer() override { return allParamValues.claimNext(); } + +public: + using SyncEntryPointArgumentEmission::requiresIndirectResult; + using SyncEntryPointArgumentEmission::getIndirectResultForFormallyDirectResult; + using SyncEntryPointArgumentEmission::getIndirectResult; + using SyncEntryPointArgumentEmission::getNextPolymorphicParameterAsMetadata; + using SyncEntryPointArgumentEmission::getNextPolymorphicParameter; }; class AsyncNativeCCEntryPointArgumentEmission final From ba737b563154e2e5261c57c482699d6ee8f4abf3 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 14 Oct 2020 13:30:47 -0700 Subject: [PATCH 460/745] [NFC] Use an Explicit 64-Bit Shift --- lib/IRGen/GenType.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index 1d99e48ca3f42..b1dff67ab22b3 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -1216,7 +1216,7 @@ TypeConverter::createPrimitiveForAlignedPointer(llvm::PointerType *type, Alignment align, Alignment pointerAlignment) { SpareBitVector spareBits = IGM.TargetInfo.PointerSpareBits; - for (unsigned bit = 0; Alignment(1 << bit) != pointerAlignment; ++bit) { + for (unsigned bit = 0; Alignment(1ull << bit) != pointerAlignment; ++bit) { spareBits.setBit(bit); } From 0d6a16258a1519b59644618be1ae2280476878ba Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 14 Oct 2020 13:31:14 -0700 Subject: [PATCH 461/745] Silence a Deprecation Warning with FixedVectorType --- lib/IRGen/GenCall.cpp | 2 +- lib/IRGen/GenType.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index fdfdd3d945b6f..813c9e5a013a0 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -1015,7 +1015,7 @@ namespace { llvm::Type *convertVectorType(clang::CanQual type) { auto eltTy = convertBuiltinType(type->getElementType().castAs()); - return llvm::VectorType::get(eltTy, type->getNumElements()); + return llvm::FixedVectorType::get(eltTy, type->getNumElements()); } llvm::Type *convertBuiltinType(clang::CanQual type) { diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index b1dff67ab22b3..a2d991b875589 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -1872,7 +1872,7 @@ convertPrimitiveBuiltin(IRGenModule &IGM, CanType canTy) { = convertPrimitiveBuiltin(IGM, vecTy->getElementType()->getCanonicalType()); - auto llvmVecTy = llvm::VectorType::get(elementTy, vecTy->getNumElements()); + auto llvmVecTy = llvm::FixedVectorType::get(elementTy, vecTy->getNumElements()); unsigned bitSize = size.getValue() * vecTy->getNumElements() * 8; if (!llvm::isPowerOf2_32(bitSize)) bitSize = llvm::NextPowerOf2(bitSize); From 78bc6aae363ff8e82d28aa7725c562ecf7e66ab2 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 14 Oct 2020 14:30:42 -0700 Subject: [PATCH 462/745] Platform: link against Pathcch.lib when using pathcch.h The MSDN documentation indicates that you should link against the `Pathcch.lib` import library when using functions from `pathcch.h` which can also be verified by use functions failing to link due to unresolved symbols. Add the missing linking to enable autolinking for `WinSDK`. --- stdlib/public/Platform/winsdk.modulemap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/public/Platform/winsdk.modulemap b/stdlib/public/Platform/winsdk.modulemap index a44f8de118874..24a054f58e5c0 100644 --- a/stdlib/public/Platform/winsdk.modulemap +++ b/stdlib/public/Platform/winsdk.modulemap @@ -102,6 +102,8 @@ module WinSDK [system] { module path { header "PathCch.h" export * + + link "pathcch.lib" } // api-ms-win-core-processthreads-l1-1-2.dll From 1c85d201921abcedbd3a693229f597a6d015f81d Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Wed, 14 Oct 2020 15:38:12 -0700 Subject: [PATCH 463/745] Try to appease 32bit tests --- test/IRGen/fixlifetime.sil | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/IRGen/fixlifetime.sil b/test/IRGen/fixlifetime.sil index 8e974ec582f0a..cf5dd443126c5 100644 --- a/test/IRGen/fixlifetime.sil +++ b/test/IRGen/fixlifetime.sil @@ -8,7 +8,7 @@ // unnecessary. // ONONE-NOT: @__swift_fixLifetime -// CHECK-objc-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test(%T11fixlifetime1CC* %0, %objc_object* %1, i8** %2, i8* %3, %swift.refcounted* %4, %T11fixlifetime3AggV* noalias nocapture dereferenceable({{.*}}) %5, i64 %6, i64 %7) {{.*}} { +// CHECK-objc-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test(%T11fixlifetime1CC* %0, %objc_object* %1, i8** %2, i8* %3, %swift.refcounted* %4, %T11fixlifetime3AggV* noalias nocapture dereferenceable({{.*}}) %5, {{(i64|i32)}} %6, {{(i64|i32)}} %7) {{.*}} { // CHECK-objc: entry: // CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%T11fixlifetime1CC*)*)(%T11fixlifetime1CC* // CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%objc_object*)*)(%objc_object* @@ -16,11 +16,11 @@ // CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%T11fixlifetime1CC*)*)(%T11fixlifetime1CC* // CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%objc_object*)*)(%objc_object* // CHECK-objc: call void @__swift_fixLifetime(%swift.refcounted* -// CHECK-objc: [[TEMP:%.*]] = inttoptr i64 %6 to %objc_object* +// CHECK-objc: [[TEMP:%.*]] = inttoptr {{(i64|i32)}} %6 to %objc_object* // CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%objc_object*)*)(%objc_object* [[TEMP]]) // CHECK-objc: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%T11fixlifetime1CC**)*)(%T11fixlifetime1CC** -// CHECK-native-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test(%T11fixlifetime1CC* %0, %swift.refcounted* %1, i8** %2, i8* %3, %swift.refcounted* %4, %T11fixlifetime3AggV* noalias nocapture dereferenceable({{.*}}) %5, i64 %6, i64 %7) {{.*}} { +// CHECK-native-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test(%T11fixlifetime1CC* %0, %swift.refcounted* %1, i8** %2, i8* %3, %swift.refcounted* %4, %T11fixlifetime3AggV* noalias nocapture dereferenceable({{.*}}) %5, {{(i64|i32)}} %6, {{(i64|i32)}} %7) {{.*}} { // CHECK-native: entry: // CHECK-native: call void bitcast (void (%swift.refcounted*)* @__swift_fixLifetime to void (%T11fixlifetime1CC*)*)(%T11fixlifetime1CC* // CHECK-native: call void @__swift_fixLifetime(%swift.refcounted* From 1e0038c3be0f6f2fbe1ba6ccb7fa394b7320aa22 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 14 Oct 2020 16:05:54 -0700 Subject: [PATCH 464/745] [ConstraintSystem] Remove implementation of operator designated types in the solver. --- include/swift/Basic/LangOptions.h | 4 - include/swift/Option/FrontendOptions.td | 4 - include/swift/Sema/ConstraintSystem.h | 13 -- lib/Frontend/CompilerInvocation.cpp | 2 - lib/Sema/CSSolver.cpp | 178 +----------------- lib/Sema/CSStep.cpp | 3 +- test/Constraints/add_with_nil.swift | 2 +- test/attr/attr_implements_fp.swift | 4 +- test/attr/attr_implements_serial.swift | 2 +- .../expression_too_complex_4.swift | 3 +- .../slow}/fast-operator-typechecking.swift | 4 +- .../{fast => slow}/rdar17170728.swift | 3 +- .../{fast => slow}/rdar19357292.swift | 3 +- .../{fast => slow}/rdar19836070.swift | 3 +- .../{fast => slow}/rdar22770433.swift | 3 +- .../{fast => slow}/rdar23429943.swift | 3 +- .../type_checker_perf/slow/rdar25866240.gyb | 26 +++ .../slow/rdar25866240.swift.gyb | 11 -- .../{fast => slow}/rdar31439825.swift | 3 +- .../{fast => slow}/rdar31742586.swift | 3 +- .../{fast => slow}/rdar32998180.swift | 3 +- .../{fast => slow}/rdar35213699.swift | 3 +- 22 files changed, 55 insertions(+), 228 deletions(-) rename validation-test/Sema/type_checker_perf/{fast => slow}/expression_too_complex_4.swift (73%) rename {test/Constraints => validation-test/Sema/type_checker_perf/slow}/fast-operator-typechecking.swift (74%) rename validation-test/Sema/type_checker_perf/{fast => slow}/rdar17170728.swift (69%) rename validation-test/Sema/type_checker_perf/{fast => slow}/rdar19357292.swift (69%) rename validation-test/Sema/type_checker_perf/{fast => slow}/rdar19836070.swift (62%) rename validation-test/Sema/type_checker_perf/{fast => slow}/rdar22770433.swift (69%) rename validation-test/Sema/type_checker_perf/{fast => slow}/rdar23429943.swift (63%) create mode 100644 validation-test/Sema/type_checker_perf/slow/rdar25866240.gyb delete mode 100644 validation-test/Sema/type_checker_perf/slow/rdar25866240.swift.gyb rename validation-test/Sema/type_checker_perf/{fast => slow}/rdar31439825.swift (53%) rename validation-test/Sema/type_checker_perf/{fast => slow}/rdar31742586.swift (61%) rename validation-test/Sema/type_checker_perf/{fast => slow}/rdar32998180.swift (73%) rename validation-test/Sema/type_checker_perf/{fast => slow}/rdar35213699.swift (61%) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 0d24bb497c510..8d64525ffa2bf 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -538,10 +538,6 @@ namespace swift { /// Disable constraint system performance hacks. bool DisableConstraintSolverPerformanceHacks = false; - /// Enable constraint solver support for experimental - /// operator protocol designator feature. - bool SolverEnableOperatorDesignatedTypes = false; - /// Enable experimental support for one-way constraints for the /// parameters of closures. bool EnableOneWayClosureParameters = false; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 8c2a727c4947b..a1fddc99263d2 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -480,10 +480,6 @@ def enable_operator_designated_types : Flag<["-"], "enable-operator-designated-types">, HelpText<"Enable operator designated types">; -def solver_enable_operator_designated_types : - Flag<["-"], "solver-enable-operator-designated-types">, - HelpText<"Enable operator designated types in constraint solver">; - def enable_invalid_ephemeralness_as_error : Flag<["-"], "enable-invalid-ephemeralness-as-error">, HelpText<"Diagnose invalid ephemeral to non-ephemeral conversions as errors">; diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 0642dedac73ba..dacb453045009 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -5293,19 +5293,6 @@ class ConstraintSystem { typedef std::function &options)> PartitionAppendCallback; - // Attempt to sort nominalTypes based on what we can discover about - // calls into the overloads in the disjunction that bindOverload is - // a part of. - void sortDesignatedTypes(SmallVectorImpl &nominalTypes, - Constraint *bindOverload); - - // Partition the choices in a disjunction based on those that match - // the designated types for the operator that the disjunction was - // formed for. - void partitionForDesignatedTypes(ArrayRef Choices, - ConstraintMatchLoop forEachChoice, - PartitionAppendCallback appendPartition); - // Partition the choices in the disjunction into groups that we will // iterate over in an order appropriate to attempt to stop before we // have to visit all of the options. diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 538403e12514a..568c7a6aa7e65 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -742,8 +742,6 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args, // Always enable operator designated types for the standard library. Opts.EnableOperatorDesignatedTypes |= FrontendOpts.ParseStdlib; - Opts.SolverEnableOperatorDesignatedTypes |= - Args.hasArg(OPT_solver_enable_operator_designated_types); Opts.EnableOneWayClosureParameters |= Args.hasArg(OPT_experimental_one_way_closure_params); diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 789e937ceb76a..db48cc1f57295 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1965,165 +1965,6 @@ static bool isOperatorBindOverload(Constraint *bindOverload) { return funcDecl && funcDecl->getOperatorDecl(); } -// Given a bind overload constraint for an operator, return the -// protocol designated as the first place to look for overloads of the -// operator. -static ArrayRef -getOperatorDesignatedNominalTypes(Constraint *bindOverload) { - auto choice = bindOverload->getOverloadChoice(); - auto *funcDecl = cast(choice.getDecl()); - auto *operatorDecl = funcDecl->getOperatorDecl(); - return operatorDecl->getDesignatedNominalTypes(); -} - -void ConstraintSystem::sortDesignatedTypes( - SmallVectorImpl &nominalTypes, - Constraint *bindOverload) { - auto *tyvar = bindOverload->getFirstType()->castTo(); - auto applicableFns = getConstraintGraph().gatherConstraints( - tyvar, ConstraintGraph::GatheringKind::EquivalenceClass, - [](Constraint *match) { - return match->getKind() == ConstraintKind::ApplicableFunction; - }); - - // FIXME: This is not true when we run the constraint optimizer. - // assert(applicableFns.size() <= 1); - - // We have a disjunction for an operator but no application of it, - // so it's being passed as an argument. - if (applicableFns.size() == 0) - return; - - // FIXME: We have more than one applicable per disjunction as a - // result of merging disjunction type variables. We may want - // to rip that out at some point. - Constraint *foundApplicable = nullptr; - SmallVector, 2> argumentTypes; - for (auto *applicableFn : applicableFns) { - argumentTypes.clear(); - auto *fnTy = applicableFn->getFirstType()->castTo(); - ArgumentInfoCollector argInfo(*this, fnTy); - // Stop if we hit anything with concrete types or conformances to - // literals. - if (!argInfo.getTypes().empty() || !argInfo.getLiteralProtocols().empty()) { - foundApplicable = applicableFn; - break; - } - } - - if (!foundApplicable) - return; - - // FIXME: It would be good to avoid this redundancy. - auto *fnTy = foundApplicable->getFirstType()->castTo(); - ArgumentInfoCollector argInfo(*this, fnTy); - - size_t nextType = 0; - for (auto argType : argInfo.getTypes()) { - auto *nominal = argType->getAnyNominal(); - for (size_t i = nextType; i < nominalTypes.size(); ++i) { - if (nominal == nominalTypes[i]) { - std::swap(nominalTypes[nextType], nominalTypes[i]); - ++nextType; - break; - } else if (auto *protoDecl = dyn_cast(nominalTypes[i])) { - if (TypeChecker::conformsToProtocol(argType, protoDecl, DC)) { - std::swap(nominalTypes[nextType], nominalTypes[i]); - ++nextType; - break; - } - } - } - } - - if (nextType + 1 >= nominalTypes.size()) - return; - - for (auto *protocol : argInfo.getLiteralProtocols()) { - auto defaultType = TypeChecker::getDefaultType(protocol, DC); - // ExpressibleByNilLiteral does not have a default type. - if (!defaultType) - continue; - auto *nominal = defaultType->getAnyNominal(); - for (size_t i = nextType + 1; i < nominalTypes.size(); ++i) { - if (nominal == nominalTypes[i]) { - std::swap(nominalTypes[nextType], nominalTypes[i]); - ++nextType; - break; - } - } - } -} - -void ConstraintSystem::partitionForDesignatedTypes( - ArrayRef Choices, ConstraintMatchLoop forEachChoice, - PartitionAppendCallback appendPartition) { - - auto types = getOperatorDesignatedNominalTypes(Choices[0]); - if (types.empty()) - return; - - SmallVector designatedNominalTypes(types.begin(), - types.end()); - - if (designatedNominalTypes.size() > 1) - sortDesignatedTypes(designatedNominalTypes, Choices[0]); - - SmallVector, 4> definedInDesignatedType; - SmallVector, 4> definedInExtensionOfDesignatedType; - - auto examineConstraint = - [&](unsigned constraintIndex, Constraint *constraint) -> bool { - auto *decl = constraint->getOverloadChoice().getDecl(); - auto *funcDecl = cast(decl); - - auto *parentDC = funcDecl->getParent(); - auto *parentDecl = parentDC->getSelfNominalTypeDecl(); - - // Skip anything not defined in a nominal type. - if (!parentDecl) - return false; - - for (auto designatedTypeIndex : indices(designatedNominalTypes)) { - auto *designatedNominal = - designatedNominalTypes[designatedTypeIndex]; - - if (parentDecl != designatedNominal) - continue; - - auto &constraints = - isa(parentDC) - ? definedInExtensionOfDesignatedType[designatedTypeIndex] - : definedInDesignatedType[designatedTypeIndex]; - - constraints.push_back(constraintIndex); - return true; - } - - return false; - }; - - definedInDesignatedType.resize(designatedNominalTypes.size()); - definedInExtensionOfDesignatedType.resize(designatedNominalTypes.size()); - - forEachChoice(Choices, examineConstraint); - - // Now collect the overload choices that are defined within the type - // that was designated in the operator declaration. - // Add partitions for each of the overloads we found in types that - // were designated as part of the operator declaration. - for (auto designatedTypeIndex : indices(designatedNominalTypes)) { - if (designatedTypeIndex < definedInDesignatedType.size()) { - auto &primary = definedInDesignatedType[designatedTypeIndex]; - appendPartition(primary); - } - if (designatedTypeIndex < definedInExtensionOfDesignatedType.size()) { - auto &secondary = definedInExtensionOfDesignatedType[designatedTypeIndex]; - appendPartition(secondary); - } - } -} - // Performance hack: if there are two generic overloads, and one is // more specialized than the other, prefer the more-specialized one. static Constraint *tryOptimizeGenericDisjunction( @@ -2266,8 +2107,7 @@ void ConstraintSystem::partitionDisjunction( } // Partition SIMD operators. - if (!getASTContext().TypeCheckerOpts.SolverEnableOperatorDesignatedTypes && - isOperatorBindOverload(Choices[0])) { + if (isOperatorBindOverload(Choices[0])) { forEachChoice(Choices, [&](unsigned index, Constraint *constraint) -> bool { if (!isOperatorBindOverload(constraint)) return false; @@ -2291,11 +2131,6 @@ void ConstraintSystem::partitionDisjunction( } }; - if (getASTContext().TypeCheckerOpts.SolverEnableOperatorDesignatedTypes && - isOperatorBindOverload(Choices[0])) { - partitionForDesignatedTypes(Choices, forEachChoice, appendPartition); - } - SmallVector everythingElse; // Gather the remaining options. forEachChoice(Choices, [&](unsigned index, Constraint *constraint) -> bool { @@ -2320,17 +2155,6 @@ Constraint *ConstraintSystem::selectDisjunction() { if (disjunctions.empty()) return nullptr; - // Attempt apply disjunctions first. When we have operators with - // designated types, this is important, because it allows us to - // select all the preferred operator overloads prior to other - // disjunctions that we may not be able to short-circuit, allowing - // us to eliminate behavior that is exponential in the number of - // operators in the expression. - if (getASTContext().TypeCheckerOpts.SolverEnableOperatorDesignatedTypes) { - if (auto *disjunction = selectApplyDisjunction()) - return disjunction; - } - if (auto *disjunction = selectBestBindingDisjunction(*this, disjunctions)) return disjunction; diff --git a/lib/Sema/CSStep.cpp b/lib/Sema/CSStep.cpp index 26fff0c7eebab..10636456a5a1c 100644 --- a/lib/Sema/CSStep.cpp +++ b/lib/Sema/CSStep.cpp @@ -660,8 +660,7 @@ bool DisjunctionStep::shortCircuitDisjunctionAt( if (currentChoice->getKind() == ConstraintKind::BindOverload && isSIMDOperator(currentChoice->getOverloadChoice().getDecl()) && lastSuccessfulChoice->getKind() == ConstraintKind::BindOverload && - !isSIMDOperator(lastSuccessfulChoice->getOverloadChoice().getDecl()) && - !ctx.TypeCheckerOpts.SolverEnableOperatorDesignatedTypes) { + !isSIMDOperator(lastSuccessfulChoice->getOverloadChoice().getDecl())) { return true; } diff --git a/test/Constraints/add_with_nil.swift b/test/Constraints/add_with_nil.swift index 30121abdfb20a..0a5e9b1eabc22 100644 --- a/test/Constraints/add_with_nil.swift +++ b/test/Constraints/add_with_nil.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -swift-version 5 -solver-enable-operator-designated-types -solver-disable-shrink -disable-constraint-solver-performance-hacks +// RUN: %target-typecheck-verify-swift -swift-version 5 func test(_ x: Int) -> Int { return x + nil diff --git a/test/attr/attr_implements_fp.swift b/test/attr/attr_implements_fp.swift index 21096fc433339..b464ee23d43e9 100644 --- a/test/attr/attr_implements_fp.swift +++ b/test/attr/attr_implements_fp.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: echo 'main()' >%t/main.swift -// RUN: %target-swiftc_driver -o %t/a.out %s %t/main.swift -Xfrontend -enable-operator-designated-types -Xfrontend -solver-enable-operator-designated-types +// RUN: %target-swiftc_driver -o %t/a.out %s %t/main.swift // RUN: %target-codesign %t/a.out // RUN: %target-run %t/a.out | %FileCheck %s // REQUIRES: executable_test @@ -13,7 +13,7 @@ public var comparedAsCauxmparablesCount : Int = 0 public var comparedAsFauxtsCount : Int = 0 -infix operator .< : ComparisonPrecedence, BinaryFauxtingPoint, Cauxmparable +infix operator .< : ComparisonPrecedence public protocol Cauxmparable { static func .< (lhs: Self, rhs: Self) -> Bool diff --git a/test/attr/attr_implements_serial.swift b/test/attr/attr_implements_serial.swift index e388590ed4a58..944e40d5bd032 100644 --- a/test/attr/attr_implements_serial.swift +++ b/test/attr/attr_implements_serial.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: echo 'client()' >%t/main.swift -// RUN: %target-build-swift-dylib(%t/%target-library-name(AttrImplFP)) -module-name AttrImplFP -emit-module -emit-module-path %t/AttrImplFP.swiftmodule %S/attr_implements_fp.swift -Xfrontend -enable-operator-designated-types -Xfrontend -solver-enable-operator-designated-types +// RUN: %target-build-swift-dylib(%t/%target-library-name(AttrImplFP)) -module-name AttrImplFP -emit-module -emit-module-path %t/AttrImplFP.swiftmodule %S/attr_implements_fp.swift // RUN: %target-build-swift -I %t -o %t/a.out %s %t/main.swift -L %t %target-rpath(%t) -lAttrImplFP // RUN: %target-codesign %t/a.out // RUN: %target-codesign %t/%target-library-name(AttrImplFP) diff --git a/validation-test/Sema/type_checker_perf/fast/expression_too_complex_4.swift b/validation-test/Sema/type_checker_perf/slow/expression_too_complex_4.swift similarity index 73% rename from validation-test/Sema/type_checker_perf/fast/expression_too_complex_4.swift rename to validation-test/Sema/type_checker_perf/slow/expression_too_complex_4.swift index b9dfd039243a9..f404bb6e35d1c 100644 --- a/validation-test/Sema/type_checker_perf/fast/expression_too_complex_4.swift +++ b/validation-test/Sema/type_checker_perf/slow/expression_too_complex_4.swift @@ -1,8 +1,9 @@ -// RUN: %target-typecheck-verify-swift -swift-version 4 -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types +// RUN: %target-typecheck-verify-swift -swift-version 4 -solver-expression-time-threshold=1 // REQUIRES: tools-release,no_asan func test(_ i: Int, _ j: Int) -> Int { return 1 + (((i >> 1) + (i >> 2) + (i >> 3) + (i >> 4) << 1) << 1) & 0x40 + 1 + (((i >> 1) + (i >> 2) + (i >> 3) + (i >> 4) << 1) << 1) & 0x40 + 1 + (((i >> 1) + (i >> 2) + (i >> 3) + (i >> 4) << 1) << 1) & 0x40 + // expected-error@-1 {{the compiler is unable to type-check this expression in reasonable time}} } diff --git a/test/Constraints/fast-operator-typechecking.swift b/validation-test/Sema/type_checker_perf/slow/fast-operator-typechecking.swift similarity index 74% rename from test/Constraints/fast-operator-typechecking.swift rename to validation-test/Sema/type_checker_perf/slow/fast-operator-typechecking.swift index c51bb0331d9c5..20b9e029a84d4 100644 --- a/test/Constraints/fast-operator-typechecking.swift +++ b/validation-test/Sema/type_checker_perf/slow/fast-operator-typechecking.swift @@ -1,8 +1,9 @@ -// RUN: %target-typecheck-verify-swift -swift-version 5 -solver-enable-operator-designated-types -solver-disable-shrink -disable-constraint-solver-performance-hacks +// RUN: %target-typecheck-verify-swift -swift-version 5 -solver-expression-time-threshold=1 // rdar://problem/32998180 func checksum(value: UInt16) -> UInt16 { var checksum = (((value >> 2) ^ (value >> 8) ^ (value >> 12) ^ (value >> 14)) & 0x01) << 1 + // expected-error@-1 {{the compiler is unable to type-check this expression in reasonable time}} checksum |= (((value) ^ (value >> UInt16(4)) ^ (value >> UInt16(6)) ^ (value >> UInt16(10))) & 0x01) checksum ^= 0x02 return checksum @@ -17,4 +18,5 @@ func f(tail: UInt64, byteCount: UInt64) { func size(count: Int) { // Size of the buffer we need to allocate let _ = count * MemoryLayout.size * (4 + 3 + 3 + 2 + 4) + // expected-error@-1 {{the compiler is unable to type-check this expression in reasonable time}} } diff --git a/validation-test/Sema/type_checker_perf/fast/rdar17170728.swift b/validation-test/Sema/type_checker_perf/slow/rdar17170728.swift similarity index 69% rename from validation-test/Sema/type_checker_perf/fast/rdar17170728.swift rename to validation-test/Sema/type_checker_perf/slow/rdar17170728.swift index aeb136a7992b0..62c2bb2ea367b 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar17170728.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar17170728.swift @@ -1,10 +1,11 @@ -// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 // REQUIRES: tools-release,no_asan let i: Int? = 1 let j: Int? let k: Int? = 2 +// expected-error@+1 {{the compiler is unable to type-check this expression in reasonable time}} let _ = [i, j, k].reduce(0 as Int?) { $0 != nil && $1 != nil ? $0! + $1! : ($0 != nil ? $0! : ($1 != nil ? $1! : nil)) } diff --git a/validation-test/Sema/type_checker_perf/fast/rdar19357292.swift b/validation-test/Sema/type_checker_perf/slow/rdar19357292.swift similarity index 69% rename from validation-test/Sema/type_checker_perf/fast/rdar19357292.swift rename to validation-test/Sema/type_checker_perf/slow/rdar19357292.swift index 41b9ae8518d13..308029540bdaf 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar19357292.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar19357292.swift @@ -1,8 +1,9 @@ -// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 // REQUIRES: tools-release,no_asan func test(strings: [String]) { for string in strings { let _ = string.split(omittingEmptySubsequences: false) { $0 == "C" || $0 == "D" || $0 == "H" || $0 == "S"} + // expected-error@-1 {{the compiler is unable to type-check this expression in reasonable time}} } } diff --git a/validation-test/Sema/type_checker_perf/fast/rdar19836070.swift b/validation-test/Sema/type_checker_perf/slow/rdar19836070.swift similarity index 62% rename from validation-test/Sema/type_checker_perf/fast/rdar19836070.swift rename to validation-test/Sema/type_checker_perf/slow/rdar19836070.swift index b6f7dd0106422..1ff690b029c63 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar19836070.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar19836070.swift @@ -1,6 +1,7 @@ -// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 // REQUIRES: tools-release,no_asan +// expected-error@+1 {{the compiler is unable to type-check this expression in reasonable time}} let _: (Character) -> Bool = { c in ("a" <= c && c <= "z") || ("A" <= c && c <= "Z") || c == "_" } diff --git a/validation-test/Sema/type_checker_perf/fast/rdar22770433.swift b/validation-test/Sema/type_checker_perf/slow/rdar22770433.swift similarity index 69% rename from validation-test/Sema/type_checker_perf/fast/rdar22770433.swift rename to validation-test/Sema/type_checker_perf/slow/rdar22770433.swift index 430b4f5caba2d..f063e685689de 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar22770433.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar22770433.swift @@ -1,7 +1,8 @@ -// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 // REQUIRES: tools-release,no_asan func test(n: Int) -> Int { + // expected-error@+1 {{the compiler is unable to type-check this expression in reasonable time}} return n == 0 ? 0 : (0.. 0 && $1 % 2 == 0) ? ((($0 + $1) - ($0 + $1)) / ($1 - $0)) + (($0 + $1) / ($1 - $0)) : $0 } diff --git a/validation-test/Sema/type_checker_perf/fast/rdar23429943.swift b/validation-test/Sema/type_checker_perf/slow/rdar23429943.swift similarity index 63% rename from validation-test/Sema/type_checker_perf/fast/rdar23429943.swift rename to validation-test/Sema/type_checker_perf/slow/rdar23429943.swift index cf634beccefbd..7f3efc941f47e 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar23429943.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar23429943.swift @@ -1,6 +1,7 @@ -// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 // REQUIRES: tools-release,no_asan +// expected-error@+1 {{the compiler is unable to type-check this expression in reasonable time}} let _ = [0].reduce([Int]()) { return $0.count == 0 && ($1 == 0 || $1 == 2 || $1 == 3) ? [] : $0 + [$1] } diff --git a/validation-test/Sema/type_checker_perf/slow/rdar25866240.gyb b/validation-test/Sema/type_checker_perf/slow/rdar25866240.gyb new file mode 100644 index 0000000000000..d42e1bb7a421c --- /dev/null +++ b/validation-test/Sema/type_checker_perf/slow/rdar25866240.gyb @@ -0,0 +1,26 @@ +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 +// REQUIRES: asserts,no_asan + +func f( + collection1: [String], + collection2: [String], + collection3: [String], + collection4: [String], + collection5: [String], + collection6: [String], + collection7: [String], + collection8: [String], + collection9: [String], + collection10: [String] +) { + _ = collection1 + + collection2 + + collection3 + + collection4 + + collection5 + + collection6 + + collection7 + + collection8 + + collection9 + + collection10 +} diff --git a/validation-test/Sema/type_checker_perf/slow/rdar25866240.swift.gyb b/validation-test/Sema/type_checker_perf/slow/rdar25866240.swift.gyb deleted file mode 100644 index 3b631b01c1616..0000000000000 --- a/validation-test/Sema/type_checker_perf/slow/rdar25866240.swift.gyb +++ /dev/null @@ -1,11 +0,0 @@ -// RUN: %scale-test --begin 2 --end 10 --step 1 --select NumConstraintScopes %s -Xfrontend=-solver-disable-shrink -Xfrontend=-disable-constraint-solver-performance-hacks -Xfrontend=-solver-enable-operator-designated-types -Xfrontend=-solver-expression-time-threshold=1 -// REQUIRES: asserts,no_asan - -func f( -%for i in range(N): - collection${i}: [String], -%end - collection${N+1}: [String] -) { - _ = ${' + '.join("collection%s" % i for i in range(N))} -} diff --git a/validation-test/Sema/type_checker_perf/fast/rdar31439825.swift b/validation-test/Sema/type_checker_perf/slow/rdar31439825.swift similarity index 53% rename from validation-test/Sema/type_checker_perf/fast/rdar31439825.swift rename to validation-test/Sema/type_checker_perf/slow/rdar31439825.swift index 9802d55b497f8..4a8d06390b6e6 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar31439825.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar31439825.swift @@ -1,6 +1,7 @@ -// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 // REQUIRES: tools-release,no_asan let a = 1 _ = -a + -a - -a + -a - -a +// expected-error@-1 {{the compiler is unable to type-check this expression in reasonable time}} diff --git a/validation-test/Sema/type_checker_perf/fast/rdar31742586.swift b/validation-test/Sema/type_checker_perf/slow/rdar31742586.swift similarity index 61% rename from validation-test/Sema/type_checker_perf/fast/rdar31742586.swift rename to validation-test/Sema/type_checker_perf/slow/rdar31742586.swift index 92eb51d065103..0cc33ce8253cf 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar31742586.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar31742586.swift @@ -1,6 +1,7 @@ -// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 // REQUIRES: tools-release,no_asan func rdar31742586() -> Double { return -(1 + 2) + -(3 + 4) + 5 - (-(1 + 2) + -(3 + 4) + 5) + // expected-error@-1 {{the compiler is unable to type-check this expression in reasonable time}} } diff --git a/validation-test/Sema/type_checker_perf/fast/rdar32998180.swift b/validation-test/Sema/type_checker_perf/slow/rdar32998180.swift similarity index 73% rename from validation-test/Sema/type_checker_perf/fast/rdar32998180.swift rename to validation-test/Sema/type_checker_perf/slow/rdar32998180.swift index 3b4fcc831cfc1..0c4830cb2c6cd 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar32998180.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar32998180.swift @@ -1,8 +1,9 @@ -// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 // REQUIRES: tools-release,no_asan func rdar32998180(value: UInt16) -> UInt16 { let result = ((((value >> 1) ^ (value >> 1) ^ (value >> 1) ^ (value >> 1)) & 1) << 1) | (((((value >> 1) ^ (value >> 1) ^ (value >> 1) ^ (value >> 1)) & 1) << 1) << 1) + // expected-error@-1 {{the compiler is unable to type-check this expression in reasonable time}} return result } diff --git a/validation-test/Sema/type_checker_perf/fast/rdar35213699.swift b/validation-test/Sema/type_checker_perf/slow/rdar35213699.swift similarity index 61% rename from validation-test/Sema/type_checker_perf/fast/rdar35213699.swift rename to validation-test/Sema/type_checker_perf/slow/rdar35213699.swift index 8c12b9ac6bd00..5d89ab1541a89 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar35213699.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar35213699.swift @@ -1,7 +1,8 @@ -// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 // REQUIRES: tools-release,no_asan func test() { let _: UInt = 1 * 2 + 3 * 4 + 5 * 6 + 7 * 8 + 9 * 10 + 11 * 12 + 13 * 14 + // expected-error@-1 {{the compiler is unable to type-check this expression in reasonable time}} } From 4c0f49f7a6a7a642cd57aeec70e8b392f3296cf1 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 14 Oct 2020 17:28:33 -0700 Subject: [PATCH 465/745] [ConstraintSystem] Remove selectApplyDisjunction, which was only used by the operator designated types implementation. --- include/swift/Sema/ConstraintSystem.h | 2 -- lib/Sema/CSSolver.cpp | 24 ------------------------ 2 files changed, 26 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index dacb453045009..468ca1b6036ef 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -5032,8 +5032,6 @@ class ConstraintSystem { /// \returns The selected disjunction. Constraint *selectDisjunction(); - Constraint *selectApplyDisjunction(); - /// Solve the system of constraints generated from provided expression. /// /// \param target The target to generate constraints from. diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index db48cc1f57295..bdc519dec4758 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1929,30 +1929,6 @@ Constraint *ConstraintSystem::getUnboundBindOverloadDisjunction( return result->first; } -// Find a disjunction associated with an ApplicableFunction constraint -// where we have some information about all of the types of in the -// function application (even if we only know something about what the -// types conform to and not actually a concrete type). -Constraint *ConstraintSystem::selectApplyDisjunction() { - for (auto &constraint : InactiveConstraints) { - if (constraint.getKind() != ConstraintKind::ApplicableFunction) - continue; - - auto *applicable = &constraint; - if (haveTypeInformationForAllArguments( - applicable->getFirstType()->castTo())) { - auto *tyvar = applicable->getSecondType()->castTo(); - - // If we have created the disjunction for this apply, find it. - auto *disjunction = getUnboundBindOverloadDisjunction(tyvar); - if (disjunction) - return disjunction; - } - } - - return nullptr; -} - static bool isOperatorBindOverload(Constraint *bindOverload) { if (bindOverload->getKind() != ConstraintKind::BindOverload) return false; From 1d4fd87ee603a954ec078be88bf0d64962c001c8 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Wed, 14 Oct 2020 17:48:43 -0700 Subject: [PATCH 466/745] [update-checkout] Add scheme to test a branch --- .../update-checkout-config.json | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/utils/update_checkout/update-checkout-config.json b/utils/update_checkout/update-checkout-config.json index 763ff44570e32..a606d752da268 100644 --- a/utils/update_checkout/update-checkout-config.json +++ b/utils/update_checkout/update-checkout-config.json @@ -306,6 +306,30 @@ "sourcekit-lsp": "release/5.3", "swift-format": "main" } + }, + "release/5.3-20201012": { + "aliases": ["release/5.3-20201012"], + "repos": { + "llvm-project": "swift/release/5.3", + "swift": "release/5.3-20201012", + "cmark": "release/5.3", + "llbuild": "release/5.3", + "swift-tools-support-core": "release/5.3", + "swiftpm": "release/5.3", + "swift-syntax": "release/5.3", + "swift-stress-tester": "release/5.3", + "swift-corelibs-xctest": "release/5.3", + "swift-corelibs-foundation": "release/5.3", + "swift-corelibs-libdispatch": "release/5.3", + "swift-integration-tests": "release/5.3", + "swift-xcode-playground-support": "release/5.3", + "ninja": "release", + "icu": "release-65-1", + "cmake": "v3.16.5", + "indexstore-db": "release/5.3-20201012", + "sourcekit-lsp": "release/5.3-20201012", + "swift-format": "main" + } } } } From 7145afbd9d5683eeb54d142b0eab5ea2ba91fe32 Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Wed, 14 Oct 2020 19:25:22 -0700 Subject: [PATCH 467/745] [Demangler] Null-check a pointer before using it in arithmetic This addresses a UBSan -fsanitize=null diagnostic seen when compiling the stdlib: runtime error: applying non-zero offset 128 to null pointer --- include/swift/Demangling/Demangler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/Demangling/Demangler.h b/include/swift/Demangling/Demangler.h index 3616ff426af86..fdff286bd8f4e 100644 --- a/include/swift/Demangling/Demangler.h +++ b/include/swift/Demangling/Demangler.h @@ -140,7 +140,7 @@ class NodeFactory { #endif // Do we have enough space in the current slab? - if (CurPtr + ObjectSize > End) { + if (!CurPtr || CurPtr + ObjectSize > End) { // No. We have to malloc a new slab. // We double the slab size for each allocated slab. SlabSize = std::max(SlabSize * 2, ObjectSize + alignof(T)); From 3d0e35e082c5f18da4a6eff30fe08706f26695fa Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 14 Oct 2020 17:25:34 -0400 Subject: [PATCH 468/745] Sema: Remove a bit of dead code --- lib/Sema/TypeCheckAvailability.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index e072bf594ba84..7e1d9f74610fa 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -2394,11 +2394,6 @@ class AvailabilityWalker : public ASTWalker { FragileKind = DC->getFragileFunctionKind(); } - // FIXME: Remove this - bool shouldWalkAccessorsTheOldWay() override { - return true; - } - bool shouldWalkIntoSeparatelyCheckedClosure(ClosureExpr *expr) override { return false; } From 94e999a1b55dd14284d96b60e1b67de7aae71dac Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 12 Oct 2020 23:21:40 -0400 Subject: [PATCH 469/745] Sema: Pull availability checking out of resolveType() We used to diagnose references to unavailable declarations in two places: - inside Exprs, right after type checking the expression - inside TypeReprs, from resolveType() In broad terms, resolveType() is called with TypeReprs stored inside both Stmts and Decls. To handle the first case, I added a new overload of diagAvailability() that takes a Stmt, to be called from typeCheckStmt(). This doesn't actually walk into any Exprs stored inside the statement; this means it only walks Patterns and such. For the second case, a new DeclAvailabilityChecker is now defined in TypeCheckAccess.cpp. It's structure is analogous to the other three walkers there: - AccessControlChecker - UsableFromInlineChecker - ExportabilityChecker The new implementation of availability checking for types introduces a lot more code than the old online logic it replaces. However, I hope to consolidate some of the code duplication among the four checkers that are defined in TypeCheckAccess.cpp, and do some other cleanups that will make the benefit of the new approach apparent. --- lib/Sema/MiscDiagnostics.cpp | 7 +- lib/Sema/MiscDiagnostics.h | 2 +- lib/Sema/TypeCheckAccess.cpp | 264 +++++++++++++++++- lib/Sema/TypeCheckAvailability.cpp | 170 +++++++++++ lib/Sema/TypeCheckAvailability.h | 20 +- lib/Sema/TypeCheckConstraints.cpp | 2 +- lib/Sema/TypeCheckStmt.cpp | 2 +- lib/Sema/TypeCheckType.cpp | 38 --- .../Headers/APINotesFrameworkTest.h | 1 + test/APINotes/versioned.swift | 1 + test/ClangImporter/SceneKit_test.swift | 86 +++--- .../attr-swift_name_renaming.swift | 2 +- test/ClangImporter/attr-swift_private.swift | 4 +- test/ClangImporter/availability.swift | 9 +- test/ClangImporter/swift2_warnings.swift | 3 +- ...ntation_only_spi_import_exposability.swift | 6 +- test/SPI/local_spi_decls.swift | 16 +- test/SPI/spi_client.swift | 5 +- .../Inputs/availability_multi_other.swift | 17 +- .../implementation-only-imports/directs.swift | 1 + test/Sema/availability.swift | 8 +- test/Sema/availability_compound.swift | 9 + test/Sema/diag_erroneous_iuo.swift | 6 +- ...ation-only-import-inlinable-indirect.swift | 4 +- ...tion-only-import-inlinable-multifile.swift | 4 +- ...implementation-only-import-inlinable.swift | 4 +- test/attr/attr_availability.swift | 4 +- test/attr/attr_inlinable.swift | 12 +- test/decl/typealias/protocol.swift | 2 +- .../StringCompatibilityDiagnostics.swift | 6 +- 30 files changed, 588 insertions(+), 127 deletions(-) diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 5660d71c0ff17..98f2462be9cab 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -4607,7 +4607,9 @@ void swift::performSyntacticExprDiagnostics(const Expr *E, checkActorIsolation(E, DC); } -void swift::performStmtDiagnostics(ASTContext &ctx, const Stmt *S) { +void swift::performStmtDiagnostics(const Stmt *S, DeclContext *DC) { + auto &ctx = DC->getASTContext(); + TypeChecker::checkUnsupportedProtocolType(ctx, const_cast(S)); if (auto switchStmt = dyn_cast(S)) @@ -4619,6 +4621,9 @@ void swift::performStmtDiagnostics(ASTContext &ctx, const Stmt *S) { if (auto *lcs = dyn_cast(S)) for (const auto &elt : lcs->getCond()) checkImplicitPromotionsInCondition(elt, ctx); + + if (!ctx.LangOpts.DisableAvailabilityChecking) + diagAvailability(S, const_cast(DC)); } //===----------------------------------------------------------------------===// diff --git a/lib/Sema/MiscDiagnostics.h b/lib/Sema/MiscDiagnostics.h index 7c385c52ae913..2a5a1313ba80d 100644 --- a/lib/Sema/MiscDiagnostics.h +++ b/lib/Sema/MiscDiagnostics.h @@ -38,7 +38,7 @@ void performSyntacticExprDiagnostics(const Expr *E, const DeclContext *DC, bool isExprStmt); /// Emit diagnostics for a given statement. -void performStmtDiagnostics(ASTContext &ctx, const Stmt *S); +void performStmtDiagnostics(const Stmt *S, DeclContext *DC); void performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD); diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 0214c9f9b91f5..b6932bcfea1df 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -15,6 +15,8 @@ //===----------------------------------------------------------------------===// #include "TypeCheckAccess.h" +#include "TypeChecker.h" +#include "TypeCheckAvailability.h" #include "TypeAccessScopeChecker.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" @@ -1772,7 +1774,11 @@ class ExportabilityChecker : public DeclVisitor { checkOverride(VD); } - DeclVisitor::visit(D); + // Note: references to @_spi and @_implementationOnly declarations from + // @inlinable code are diagnosed by DeclAvailabilityChecker below. + auto *DC = D->getInnermostDeclContext(); + if (DC->getFragileFunctionKind().kind == FragileFunctionKind::None) + DeclVisitor::visit(D); } // Force all kinds to be handled at a lower level. @@ -2035,6 +2041,261 @@ class ExportabilityChecker : public DeclVisitor { }); } }; + +/// Diagnose declarations whose signatures refer to unavailable types. +class DeclAvailabilityChecker : public DeclVisitor { + DeclContext *DC; + + void checkType(Type type, const TypeRepr *typeRepr, const Decl *context, + bool allowUnavailableProtocol=false) { + // Don't bother checking errors. + if (type && type->hasError()) + return; + + // Check the TypeRepr for references to unavailable declarations. + if (typeRepr) { + DeclAvailabilityFlags flags = None; + + // We allow a type to conform to a protocol that is less available than + // the type itself. This enables a type to retroactively model or directly + // conform to a protocol only available on newer OSes and yet still be used on + // older OSes. + // + // To support this, inside inheritance clauses we allow references to + // protocols that are unavailable in the current type refinement context. + if (allowUnavailableProtocol) + flags |= DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol; + + diagnoseTypeReprAvailability(typeRepr, DC, flags); + } + + // Check the type for references to unavailable conformances. + if (type) + if (!context->getDeclContext()->isLocalContext()) + diagnoseTypeAvailability(type, context->getLoc(), DC); + } + + void checkGenericParams(const GenericContext *ownerCtx, + const ValueDecl *ownerDecl) { + // FIXME: What if we have a where clause and no generic params? + const auto params = ownerCtx->getGenericParams(); + if (!params) + return; + + for (auto param : *params) { + if (param->getInherited().empty()) + continue; + assert(param->getInherited().size() == 1); + auto inherited = param->getInherited().front(); + checkType(inherited.getType(), inherited.getTypeRepr(), ownerDecl); + } + + forAllRequirementTypes(WhereClauseOwner( + const_cast(ownerCtx)), + [&](Type type, TypeRepr *typeRepr) { + checkType(type, typeRepr, ownerDecl); + }); + } + +public: + explicit DeclAvailabilityChecker(Decl *D) + : DC(D->getInnermostDeclContext()) {} + + void visit(Decl *D) { + if (D->isImplicit()) + return; + + DeclVisitor::visit(D); + } + + // Force all kinds to be handled at a lower level. + void visitDecl(Decl *D) = delete; + void visitValueDecl(ValueDecl *D) = delete; + +#define UNREACHABLE(KIND, REASON) \ + void visit##KIND##Decl(KIND##Decl *D) { \ + llvm_unreachable(REASON); \ + } + UNREACHABLE(Import, "not applicable") + UNREACHABLE(TopLevelCode, "not applicable") + UNREACHABLE(Module, "not applicable") + + UNREACHABLE(Param, "handled by the enclosing declaration") + UNREACHABLE(GenericTypeParam, "handled by the enclosing declaration") + UNREACHABLE(MissingMember, "handled by the enclosing declaration") +#undef UNREACHABLE + +#define UNINTERESTING(KIND) \ + void visit##KIND##Decl(KIND##Decl *D) {} + + UNINTERESTING(PrefixOperator) // Does not reference other decls. + UNINTERESTING(PostfixOperator) // Does not reference other decls. + UNINTERESTING(InfixOperator) // Does not reference other decls. + UNINTERESTING(IfConfig) // Not applicable. + UNINTERESTING(PoundDiagnostic) // Not applicable. + UNINTERESTING(EnumCase) // Handled at the EnumElement level. + UNINTERESTING(Destructor) // Always correct. + UNINTERESTING(Accessor) // Handled by the Var or Subscript. + UNINTERESTING(OpaqueType) // TODO + UNINTERESTING(PrecedenceGroup) // Doesn't reference anything with availability. + + // Handled at the PatternBinding level; if the pattern has a simple + // "name: TheType" form, we can get better results by diagnosing the TypeRepr. + UNINTERESTING(Var) + + /// \see visitPatternBindingDecl + void checkNamedPattern(const NamedPattern *NP, + const llvm::DenseSet &seenVars) { + const VarDecl *theVar = NP->getDecl(); + + // Only check the type of individual variables if we didn't check an + // enclosing TypedPattern. + if (seenVars.count(theVar)) + return; + + checkType(theVar->getValueInterfaceType(), /*typeRepr*/nullptr, theVar); + } + + /// \see visitPatternBindingDecl + void checkTypedPattern(PatternBindingDecl *PBD, + const TypedPattern *TP, + llvm::DenseSet &seenVars) { + // FIXME: We need to figure out if this is a stored or computed property, + // so we pull out some random VarDecl in the pattern. They're all going to + // be the same, but still, ick. + const VarDecl *anyVar = nullptr; + TP->forEachVariable([&](VarDecl *V) { + seenVars.insert(V); + anyVar = V; + }); + + checkType(TP->hasType() ? TP->getType() : Type(), + TP->getTypeRepr(), PBD); + + // Check the property wrapper types. + if (anyVar) + for (auto attr : anyVar->getAttachedPropertyWrappers()) + checkType(attr->getType(), attr->getTypeRepr(), anyVar); + } + + void visitPatternBindingDecl(PatternBindingDecl *PBD) { + llvm::DenseSet seenVars; + for (auto idx : range(PBD->getNumPatternEntries())) { + PBD->getPattern(idx)->forEachNode([&](const Pattern *P) { + if (auto *NP = dyn_cast(P)) { + checkNamedPattern(NP, seenVars); + return; + } + + auto *TP = dyn_cast(P); + if (!TP) + return; + checkTypedPattern(PBD, TP, seenVars); + }); + seenVars.clear(); + } + } + + void visitTypeAliasDecl(TypeAliasDecl *TAD) { + checkGenericParams(TAD, TAD); + checkType(TAD->getUnderlyingType(), + TAD->getUnderlyingTypeRepr(), TAD); + } + + void visitAssociatedTypeDecl(AssociatedTypeDecl *assocType) { + llvm::for_each(assocType->getInherited(), + [&](TypeLoc requirement) { + checkType(requirement.getType(), requirement.getTypeRepr(), + assocType); + }); + checkType(assocType->getDefaultDefinitionType(), + assocType->getDefaultDefinitionTypeRepr(), assocType); + + if (assocType->getTrailingWhereClause()) { + forAllRequirementTypes(assocType, + [&](Type type, TypeRepr *typeRepr) { + checkType(type, typeRepr, assocType); + }); + } + } + + void visitNominalTypeDecl(const NominalTypeDecl *nominal) { + checkGenericParams(nominal, nominal); + + llvm::for_each(nominal->getInherited(), + [&](TypeLoc inherited) { + checkType(inherited.getType(), inherited.getTypeRepr(), + nominal, /*allowUnavailableProtocol=*/true); + }); + } + + void visitProtocolDecl(ProtocolDecl *proto) { + llvm::for_each(proto->getInherited(), + [&](TypeLoc requirement) { + checkType(requirement.getType(), requirement.getTypeRepr(), proto, + /*allowUnavailableProtocol=*/true); + }); + + if (proto->getTrailingWhereClause()) { + forAllRequirementTypes(proto, [&](Type type, TypeRepr *typeRepr) { + checkType(type, typeRepr, proto); + }); + } + } + + void visitSubscriptDecl(SubscriptDecl *SD) { + checkGenericParams(SD, SD); + + for (auto &P : *SD->getIndices()) { + checkType(P->getInterfaceType(), P->getTypeRepr(), SD); + } + checkType(SD->getElementInterfaceType(), SD->getElementTypeRepr(), SD); + } + + void visitAbstractFunctionDecl(AbstractFunctionDecl *fn) { + checkGenericParams(fn, fn); + + for (auto *P : *fn->getParameters()) + checkType(P->getInterfaceType(), P->getTypeRepr(), fn); + } + + void visitFuncDecl(FuncDecl *FD) { + visitAbstractFunctionDecl(FD); + checkType(FD->getResultInterfaceType(), FD->getResultTypeRepr(), FD); + } + + void visitEnumElementDecl(EnumElementDecl *EED) { + if (!EED->hasAssociatedValues()) + return; + for (auto &P : *EED->getParameterList()) + checkType(P->getInterfaceType(), P->getTypeRepr(), EED); + } + + void checkConstrainedExtensionRequirements(ExtensionDecl *ED) { + if (!ED->getTrailingWhereClause()) + return; + forAllRequirementTypes(ED, [&](Type type, TypeRepr *typeRepr) { + checkType(type, typeRepr, ED); + }); + } + + void visitExtensionDecl(ExtensionDecl *ED) { + auto extendedType = ED->getExtendedNominal(); + assert(extendedType && "valid extension with no extended type?"); + if (!extendedType) + return; + + llvm::for_each(ED->getInherited(), + [&](TypeLoc inherited) { + checkType(inherited.getType(), inherited.getTypeRepr(), + ED, /*allowUnavailableProtocol=*/true); + }); + + checkType(ED->getExtendedType(), ED->getExtendedTypeRepr(), ED); + checkConstrainedExtensionRequirements(ED); + } +}; + } // end anonymous namespace static void checkExtensionGenericParamAccess(const ExtensionDecl *ED) { @@ -2086,4 +2347,5 @@ void swift::checkAccessControl(Decl *D) { } ExportabilityChecker().visit(D); + DeclAvailabilityChecker(D).visit(D); } diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 7e1d9f74610fa..4b1cee20ac37e 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -23,6 +23,7 @@ #include "swift/AST/NameLookup.h" #include "swift/AST/Pattern.h" #include "swift/AST/SourceFile.h" +#include "swift/AST/TypeDeclFinder.h" #include "swift/AST/TypeRefinementContext.h" #include "swift/Basic/Defer.h" #include "swift/Basic/SourceManager.h" @@ -2468,6 +2469,11 @@ class AvailabilityWalker : public ASTWalker { return E; } + bool walkToTypeReprPre(TypeRepr *T) override { + diagnoseTypeReprAvailability(T, DC); + return false; + } + bool diagAvailability(ConcreteDeclRef declRef, SourceRange R, const ApplyExpr *call = nullptr, DeclAvailabilityFlags flags = None) const; @@ -2871,6 +2877,170 @@ void swift::diagAvailability(const Expr *E, DeclContext *DC) { const_cast(E)->walk(walker); } +namespace { + +class StmtAvailabilityWalker : public ASTWalker { + DeclContext *DC; + +public: + explicit StmtAvailabilityWalker(DeclContext *DC) : DC(DC) {} + + /// We'll visit the expression from performSyntacticExprDiagnostics(). + std::pair walkToExprPre(Expr *E) override { + return std::make_pair(false, E); + } + + bool walkToTypeReprPre(TypeRepr *T) override { + diagnoseTypeReprAvailability(T, DC); + return false; + } +}; + +} + +void swift::diagAvailability(const Stmt *S, DeclContext *DC) { + // We'll visit the individual statements when we check them. + if (isa(S)) + return; + + StmtAvailabilityWalker walker(DC); + const_cast(S)->walk(walker); +} + +namespace { + +class TypeReprAvailabilityWalker : public ASTWalker { + DeclContext *DC; + DeclAvailabilityFlags flags; + + bool checkComponentIdentTypeRepr(ComponentIdentTypeRepr *ITR) { + if (auto *typeDecl = ITR->getBoundDecl()) { + auto range = ITR->getNameLoc().getSourceRange(); + if (diagnoseDeclAvailability(typeDecl, DC, range, flags)) + return true; + } + + bool foundAnyIssues = false; + + if (auto *GTR = dyn_cast(ITR)) { + auto genericFlags = flags; + genericFlags -= DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol; + + for (auto *genericArg : GTR->getGenericArgs()) { + if (diagnoseTypeReprAvailability(genericArg, DC, genericFlags)) + foundAnyIssues = true; + } + } + + return foundAnyIssues; + } + +public: + bool foundAnyIssues = false; + + TypeReprAvailabilityWalker(DeclContext *DC, + DeclAvailabilityFlags flags) + : DC(DC), flags(flags) {} + + bool walkToTypeReprPre(TypeRepr *T) override { + if (auto *ITR = dyn_cast(T)) { + if (auto *CTR = dyn_cast(ITR)) { + for (auto *comp : CTR->getComponents()) { + // If a parent type is unavailable, don't go on to diagnose + // the member since that will just produce a redundant + // diagnostic. + if (checkComponentIdentTypeRepr(comp)) { + foundAnyIssues = true; + break; + } + } + } else if (auto *GTR = dyn_cast(T)) { + if (checkComponentIdentTypeRepr(GTR)) + foundAnyIssues = true; + } else if (auto *STR = dyn_cast(T)) { + if (checkComponentIdentTypeRepr(STR)) + foundAnyIssues = true; + } + + // We've already visited all the children above, so we don't + // need to recurse. + return false; + } + + return true; + } +}; + +} + +bool swift::diagnoseTypeReprAvailability(const TypeRepr *T, DeclContext *DC, + DeclAvailabilityFlags flags) { + TypeReprAvailabilityWalker walker(DC, flags); + const_cast(T)->walk(walker); + return walker.foundAnyIssues; +} + +namespace { + +class ProblematicTypeFinder : public TypeDeclFinder { + DeclContext *DC; + FragileFunctionKind FragileKind; + SourceLoc Loc; + +public: + ProblematicTypeFinder(DeclContext *DC, SourceLoc Loc) + : DC(DC), Loc(Loc) { + FragileKind = DC->getFragileFunctionKind(); + } + + Action visitNominalType(NominalType *ty) override { + if (FragileKind.kind != FragileFunctionKind::None) + TypeChecker::diagnoseGenericTypeExportability(Loc, ty, DC); + return Action::Continue; + } + + Action visitBoundGenericType(BoundGenericType *ty) override { + if (FragileKind.kind != FragileFunctionKind::None) + TypeChecker::diagnoseGenericTypeExportability(Loc, ty, DC); + return Action::Continue; + } + + Action visitTypeAliasType(TypeAliasType *ty) override { + if (FragileKind.kind != FragileFunctionKind::None) + TypeChecker::diagnoseGenericTypeExportability(Loc, ty, DC); + return Action::Continue; + } + + // We diagnose unserializable Clang function types in the + // post-visitor so that we diagnose any unexportable component + // types first. + Action walkToTypePost(Type T) override { + if (FragileKind.kind != FragileFunctionKind::None) { + if (auto fnType = T->getAs()) { + if (auto clangType = fnType->getClangTypeInfo().getType()) { + auto &ctx = DC->getASTContext(); + auto loader = ctx.getClangModuleLoader(); + // Serialization will serialize the sugared type if it can, + // but we need the canonical type to be serializable or else + // canonicalization (e.g. in SIL) might break things. + if (!loader->isSerializable(clangType, /*check canonical*/ true)) { + ctx.Diags.diagnose(Loc, diag::unexportable_clang_function_type, + fnType); + } + } + } + } + + return TypeDeclFinder::walkToTypePost(T); + } +}; + +} + +void swift::diagnoseTypeAvailability(Type T, SourceLoc loc, DeclContext *DC) { + T.walk(ProblematicTypeFinder(DC, loc)); +} + /// Run the Availability-diagnostics algorithm otherwise used in an expr /// context, but for non-expr contexts such as TypeDecls referenced from /// TypeReprs. diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index 526db08e892a8..a13151fa93fd0 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -28,11 +28,11 @@ namespace swift { class Expr; class InFlightDiagnostic; class Decl; + class Stmt; + class Type; + class TypeRepr; class ValueDecl; -/// Diagnose uses of unavailable declarations. -void diagAvailability(const Expr *E, DeclContext *DC); - enum class DeclAvailabilityFlag : uint8_t { /// Do not diagnose uses of protocols in versions before they were introduced. /// Used when type-checking protocol conformances, since conforming to a @@ -57,6 +57,20 @@ enum class DeclAvailabilityFlag : uint8_t { }; using DeclAvailabilityFlags = OptionSet; +/// Diagnose uses of unavailable declarations in expressions. +void diagAvailability(const Expr *E, DeclContext *DC); + +/// Diagnose uses of unavailable declarations in statements (via patterns, etc), +/// without walking into expressions. +void diagAvailability(const Stmt *S, DeclContext *DC); + +/// Diagnose uses of unavailable declarations in types. +bool diagnoseTypeReprAvailability(const TypeRepr *T, DeclContext *DC, + DeclAvailabilityFlags flags = None); + +/// Diagnose uses of unavailable conformances in types. +void diagnoseTypeAvailability(Type T, SourceLoc loc, DeclContext *DC); + /// Run the Availability-diagnostics algorithm otherwise used in an expr /// context, but for non-expr contexts such as TypeDecls referenced from /// TypeReprs. diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index ea471f7759455..982326728bcec 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -254,7 +254,7 @@ class FunctionSyntacticDiagnosticWalker : public ASTWalker { } std::pair walkToStmtPre(Stmt *stmt) override { - performStmtDiagnostics(dcStack.back()->getASTContext(), stmt); + performStmtDiagnostics(stmt, dcStack.back()); return {true, stmt}; } diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 8422792d52210..250d762a69da0 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -677,7 +677,7 @@ class StmtChecker : public StmtVisitor { if (S2 == nullptr) return true; S = S2; - performStmtDiagnostics(getASTContext(), S); + performStmtDiagnostics(S, DC); return false; } diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 476e02939136d..d90db44c76536 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1598,26 +1598,6 @@ resolveIdentTypeComponent(TypeResolution resolution, comp); } -static bool diagnoseAvailability(IdentTypeRepr *IdType, - DeclContext *DC, - bool AllowPotentiallyUnavailableProtocol) { - DeclAvailabilityFlags flags = - DeclAvailabilityFlag::ContinueOnPotentialUnavailability; - if (AllowPotentiallyUnavailableProtocol) - flags |= DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol; - auto componentRange = IdType->getComponentRange(); - for (auto comp : componentRange) { - if (auto *typeDecl = comp->getBoundDecl()) { - if (diagnoseDeclAvailability(typeDecl, DC, - comp->getNameLoc().getSourceRange(), flags)) { - return true; - } - } - } - - return false; -} - // Hack to apply context-specific @escaping to an AST function type. static Type applyNonEscapingIfNecessary(Type ty, TypeResolutionOptions options) { @@ -3331,24 +3311,6 @@ Type TypeResolver::resolveIdentifierType(IdentTypeRepr *IdType, if (result->is()) result = applyNonEscapingIfNecessary(result, options); - // Check the availability of the type. - - // We allow a type to conform to a protocol that is less available than - // the type itself. This enables a type to retroactively model or directly - // conform to a protocol only available on newer OSes and yet still be used on - // older OSes. - // To support this, inside inheritance clauses we allow references to - // protocols that are unavailable in the current type refinement context. - - if (!options.contains(TypeResolutionFlags::SilenceErrors) && - !options.contains(TypeResolutionFlags::AllowUnavailable) && - diagnoseAvailability( - IdType, getDeclContext(), - options.contains(TypeResolutionFlags::AllowUnavailableProtocol))) { - Components.back()->setInvalid(); - return ErrorType::get(getASTContext()); - } - return result; } diff --git a/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.h b/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.h index 401497b379f70..72a1265d33cca 100644 --- a/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.h +++ b/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.h @@ -20,6 +20,7 @@ __attribute__((objc_root_class)) __attribute__((objc_root_class)) @interface Base +-(nonnull instancetype)init; @end @interface B : A diff --git a/test/APINotes/versioned.swift b/test/APINotes/versioned.swift index 96b3be37c26f1..6740bbd3e665c 100644 --- a/test/APINotes/versioned.swift +++ b/test/APINotes/versioned.swift @@ -107,6 +107,7 @@ func testRenamedTopLevelDiags() { // CHECK-DIAGS-4-NOT: versioned.swift:[[@LINE-1]]: _ = s.value // CHECK-DIAGS-4-NOT: versioned.swift:[[@LINE-1]]: + _ = t } func testAKA(structValue: ImportantCStruct, aliasValue: ImportantCAlias) { diff --git a/test/ClangImporter/SceneKit_test.swift b/test/ClangImporter/SceneKit_test.swift index 7c24a0b0d8142..316f4ff09b3fb 100644 --- a/test/ClangImporter/SceneKit_test.swift +++ b/test/ClangImporter/SceneKit_test.swift @@ -10,91 +10,91 @@ import Foundation // wrapper types with nestest values. @available(macOS 10.11, *) func testNestingRenames() { - let _ = SCNGeometrySourceSemantic + let _ = SCNGeometrySourceSemantic.self // expected-error@-1{{'SCNGeometrySourceSemantic' has been renamed to 'SCNGeometrySource.Semantic'}} - let _ = SCNLightType + let _ = SCNLightType.self // expected-error@-1{{'SCNLightType' has been renamed to 'SCNLight.LightType'}} - let _ = SCNLightingModel + let _ = SCNLightingModel.self // expected-error@-1{{'SCNLightingModel' has been renamed to 'SCNMaterial.LightingModel'}} - let _ = SCNParticleProperty + let _ = SCNParticleProperty.self // expected-error@-1{{'SCNParticleProperty' has been renamed to 'SCNParticleSystem.ParticleProperty'}} - let _ = SCNPhysicsShapeOption + let _ = SCNPhysicsShapeOption.self // expected-error@-1{{'SCNPhysicsShapeOption' has been renamed to 'SCNPhysicsShape.Option'}} - let _ = SCNPhysicsShapeType + let _ = SCNPhysicsShapeType.self // expected-error@-1{{'SCNPhysicsShapeType' has been renamed to 'SCNPhysicsShape.ShapeType'}} - let _ = SCNPhysicsTestOption + let _ = SCNPhysicsTestOption.self // expected-error@-1{{'SCNPhysicsTestOption' has been renamed to 'SCNPhysicsWorld.TestOption'}} - let _ = SCNPhysicsTestSearchMode + let _ = SCNPhysicsTestSearchMode.self // expected-error@-1{{'SCNPhysicsTestSearchMode' has been renamed to 'SCNPhysicsWorld.TestSearchMode'}} - let _ = SCNSceneAttribute + let _ = SCNSceneAttribute.self // expected-error@-1{{'SCNSceneAttribute' has been renamed to 'SCNScene.Attribute'}} - let _ = SCNSceneSourceAnimationImportPolicy + let _ = SCNSceneSourceAnimationImportPolicy.self // expected-error@-1{{'SCNSceneSourceAnimationImportPolicy' has been renamed to 'SCNSceneSource.AnimationImportPolicy'}} - let _ = SCNSceneSourceLoadingOption + let _ = SCNSceneSourceLoadingOption.self // expected-error@-1{{'SCNSceneSourceLoadingOption' has been renamed to 'SCNSceneSource.LoadingOption'}} - let _ = SCNViewOption + let _ = SCNViewOption.self // expected-error@-1{{'SCNViewOption' has been renamed to 'SCNView.Option'}} - let _ = SCNHitTestFirstFoundOnlyKey + let _ = SCNHitTestFirstFoundOnlyKey.self // expected-error@-1{{'SCNHitTestFirstFoundOnlyKey' has been renamed to 'SCNHitTestOption.firstFoundOnly'}} - let _ = SCNHitTestSortResultsKey + let _ = SCNHitTestSortResultsKey.self // expected-error@-1{{'SCNHitTestSortResultsKey' has been renamed to 'SCNHitTestOption.sortResults'}} - let _ = SCNHitTestClipToZRangeKey + let _ = SCNHitTestClipToZRangeKey.self // expected-error@-1{{'SCNHitTestClipToZRangeKey' has been renamed to 'SCNHitTestOption.clipToZRange'}} - let _ = SCNHitTestBackFaceCullingKey + let _ = SCNHitTestBackFaceCullingKey.self // expected-error@-1{{'SCNHitTestBackFaceCullingKey' has been renamed to 'SCNHitTestOption.backFaceCulling'}} - let _ = SCNHitTestBoundingBoxOnlyKey + let _ = SCNHitTestBoundingBoxOnlyKey.self // expected-error@-1{{'SCNHitTestBoundingBoxOnlyKey' has been renamed to 'SCNHitTestOption.boundingBoxOnly'}} - let _ = SCNHitTestIgnoreChildNodesKey + let _ = SCNHitTestIgnoreChildNodesKey.self // expected-error@-1{{'SCNHitTestIgnoreChildNodesKey' has been renamed to 'SCNHitTestOption.ignoreChildNodes'}} - let _ = SCNHitTestRootNodeKey + let _ = SCNHitTestRootNodeKey.self // expected-error@-1{{'SCNHitTestRootNodeKey' has been renamed to 'SCNHitTestOption.rootNode'}} - let _ = SCNHitTestIgnoreHiddenNodesKey + let _ = SCNHitTestIgnoreHiddenNodesKey.self // expected-error@-1{{'SCNHitTestIgnoreHiddenNodesKey' has been renamed to 'SCNHitTestOption.ignoreHiddenNodes'}} - let _ = SCNPhysicsShapeTypeKey + let _ = SCNPhysicsShapeTypeKey.self // expected-error@-1{{'SCNPhysicsShapeTypeKey' has been renamed to 'SCNPhysicsShape.Option.type'}} - let _ = SCNPhysicsShapeKeepAsCompoundKey + let _ = SCNPhysicsShapeKeepAsCompoundKey.self // expected-error@-1{{'SCNPhysicsShapeKeepAsCompoundKey' has been renamed to 'SCNPhysicsShape.Option.keepAsCompound'}} - let _ = SCNPhysicsShapeScaleKey + let _ = SCNPhysicsShapeScaleKey.self // expected-error@-1{{'SCNPhysicsShapeScaleKey' has been renamed to 'SCNPhysicsShape.Option.scale'}} - let _ = SCNPhysicsTestCollisionBitMaskKey + let _ = SCNPhysicsTestCollisionBitMaskKey.self // expected-error@-1{{'SCNPhysicsTestCollisionBitMaskKey' has been renamed to 'SCNPhysicsWorld.TestOption.collisionBitMask'}} - let _ = SCNPhysicsTestSearchModeKey + let _ = SCNPhysicsTestSearchModeKey.self // expected-error@-1{{'SCNPhysicsTestSearchModeKey' has been renamed to 'SCNPhysicsWorld.TestOption.searchMode'}} - let _ = SCNPhysicsTestBackfaceCullingKey + let _ = SCNPhysicsTestBackfaceCullingKey.self // expected-error@-1{{'SCNPhysicsTestBackfaceCullingKey' has been renamed to 'SCNPhysicsWorld.TestOption.backfaceCulling'}} - let _ = SCNSceneStartTimeAttributeKey + let _ = SCNSceneStartTimeAttributeKey.self // expected-error@-1{{'SCNSceneStartTimeAttributeKey' has been renamed to 'SCNScene.Attribute.startTime'}} - let _ = SCNSceneEndTimeAttributeKey + let _ = SCNSceneEndTimeAttributeKey.self // expected-error@-1{{'SCNSceneEndTimeAttributeKey' has been renamed to 'SCNScene.Attribute.endTime'}} - let _ = SCNSceneFrameRateAttributeKey + let _ = SCNSceneFrameRateAttributeKey.self // expected-error@-1{{'SCNSceneFrameRateAttributeKey' has been renamed to 'SCNScene.Attribute.frameRate'}} - let _ = SCNSceneUpAxisAttributeKey + let _ = SCNSceneUpAxisAttributeKey.self // expected-error@-1{{'SCNSceneUpAxisAttributeKey' has been renamed to 'SCNScene.Attribute.upAxis'}} - let _ = SCNSceneSourceCreateNormalsIfAbsentKey + let _ = SCNSceneSourceCreateNormalsIfAbsentKey.self // expected-error@-1{{'SCNSceneSourceCreateNormalsIfAbsentKey' has been renamed to 'SCNSceneSource.LoadingOption.createNormalsIfAbsent'}} - let _ = SCNSceneSourceCheckConsistencyKey + let _ = SCNSceneSourceCheckConsistencyKey.self // expected-error@-1{{'SCNSceneSourceCheckConsistencyKey' has been renamed to 'SCNSceneSource.LoadingOption.checkConsistency'}} - let _ = SCNSceneSourceFlattenSceneKey + let _ = SCNSceneSourceFlattenSceneKey.self // expected-error@-1{{'SCNSceneSourceFlattenSceneKey' has been renamed to 'SCNSceneSource.LoadingOption.flattenScene'}} - let _ = SCNSceneSourceUseSafeModeKey + let _ = SCNSceneSourceUseSafeModeKey.self // expected-error@-1{{'SCNSceneSourceUseSafeModeKey' has been renamed to 'SCNSceneSource.LoadingOption.useSafeMode'}} - let _ = SCNSceneSourceAssetDirectoryURLsKey + let _ = SCNSceneSourceAssetDirectoryURLsKey.self // expected-error@-1{{'SCNSceneSourceAssetDirectoryURLsKey' has been renamed to 'SCNSceneSource.LoadingOption.assetDirectoryURLs'}} - let _ = SCNSceneSourceOverrideAssetURLsKey + let _ = SCNSceneSourceOverrideAssetURLsKey.self // expected-error@-1{{'SCNSceneSourceOverrideAssetURLsKey' has been renamed to 'SCNSceneSource.LoadingOption.overrideAssetURLs'}} - let _ = SCNSceneSourceStrictConformanceKey + let _ = SCNSceneSourceStrictConformanceKey.self // expected-error@-1{{'SCNSceneSourceStrictConformanceKey' has been renamed to 'SCNSceneSource.LoadingOption.strictConformance'}} - let _ = SCNSceneSourceConvertUnitsToMetersKey + let _ = SCNSceneSourceConvertUnitsToMetersKey.self // expected-error@-1{{'SCNSceneSourceConvertUnitsToMetersKey' has been renamed to 'SCNSceneSource.LoadingOption.convertUnitsToMeters'}} - let _ = SCNSceneSourceConvertToYUpKey + let _ = SCNSceneSourceConvertToYUpKey.self // expected-error@-1{{'SCNSceneSourceConvertToYUpKey' has been renamed to 'SCNSceneSource.LoadingOption.convertToYUp'}} - let _ = SCNSceneSourceAnimationImportPolicyKey + let _ = SCNSceneSourceAnimationImportPolicyKey.self // expected-error@-1{{'SCNSceneSourceAnimationImportPolicyKey' has been renamed to 'SCNSceneSource.LoadingOption.animationImportPolicy'}} - let _ = SCNPreferredRenderingAPIKey + let _ = SCNPreferredRenderingAPIKey.self // expected-error@-1{{'SCNPreferredRenderingAPIKey' has been renamed to 'SCNView.Option.preferredRenderingAPI'}} - let _ = SCNPreferredDeviceKey + let _ = SCNPreferredDeviceKey.self // expected-error@-1{{'SCNPreferredDeviceKey' has been renamed to 'SCNView.Option.preferredDevice'}} - let _ = SCNPreferLowPowerDeviceKey + let _ = SCNPreferLowPowerDeviceKey.self // expected-error@-1{{'SCNPreferLowPowerDeviceKey' has been renamed to 'SCNView.Option.preferLowPowerDevice'}} } diff --git a/test/ClangImporter/attr-swift_name_renaming.swift b/test/ClangImporter/attr-swift_name_renaming.swift index 8987076701c91..df5a9d9b40169 100644 --- a/test/ClangImporter/attr-swift_name_renaming.swift +++ b/test/ClangImporter/attr-swift_name_renaming.swift @@ -9,7 +9,7 @@ func test() { // Enum name remapping. var color: ColorKind = CT_red - var color2: ColorType = CT_Red // expected-error{{'ColorType' has been renamed to 'ColorKind'}}{{15-24=ColorKind}} + var color2: ColorType = CT_red // expected-error{{'ColorType' has been renamed to 'ColorKind'}}{{15-24=ColorKind}} // Enumerator remapping. var excuse: HomeworkExcuse = .dogAteIt diff --git a/test/ClangImporter/attr-swift_private.swift b/test/ClangImporter/attr-swift_private.swift index 252f8c74d513c..fd9af2910913f 100644 --- a/test/ClangImporter/attr-swift_private.swift +++ b/test/ClangImporter/attr-swift_private.swift @@ -103,8 +103,8 @@ func makeSureAnyObject(_: AnyObject) {} #if !IRGEN func testUnavailableRefs() { - var x: __PrivCFTypeRef // expected-error {{'__PrivCFTypeRef' has been renamed to '__PrivCFType'}} - var y: __PrivCFSubRef // expected-error {{'__PrivCFSubRef' has been renamed to '__PrivCFSub'}} + var _: __PrivCFTypeRef // expected-error {{'__PrivCFTypeRef' has been renamed to '__PrivCFType'}} + var _: __PrivCFSubRef // expected-error {{'__PrivCFSubRef' has been renamed to '__PrivCFSub'}} } #endif diff --git a/test/ClangImporter/availability.swift b/test/ClangImporter/availability.swift index 8ee1e1acc7c16..011b4af216327 100644 --- a/test/ClangImporter/availability.swift +++ b/test/ClangImporter/availability.swift @@ -137,6 +137,7 @@ func test_swift_unavailable() { NSSwiftNewUnavailableFunctionPremium() // expected-error {{'NSSwiftNewUnavailableFunctionPremium()' is unavailable in Swift: You didn't want to use it anyway.}} let x: NSSwiftUnavailableStruct? = nil // expected-error {{'NSSwiftUnavailableStruct' is unavailable in Swift}} + _ = x } func test_CFReleaseRetainAutorelease(_ x: CFTypeRef, color: CGColor) { @@ -152,9 +153,9 @@ func testRedeclarations() { unavail2() // expected-error {{is unavailable: middle}} unavail3() // expected-error {{is unavailable: last}} - UnavailClass1.self // expected-error {{is unavailable: first}} - UnavailClass2.self // expected-error {{is unavailable: middle}} - UnavailClass3.self // expected-error {{is unavailable: last}} + _ = UnavailClass1.self // expected-error {{is unavailable: first}} + _ = UnavailClass2.self // expected-error {{is unavailable: middle}} + _ = UnavailClass3.self // expected-error {{is unavailable: last}} let _: UnavailStruct1 // expected-error {{is unavailable: first}} let _: UnavailStruct2 // expected-error {{is unavailable: first}} @@ -186,7 +187,9 @@ func test_DistributedObjects(_ o: NSObject, i: NSPortCoder) { // expected-error {{'NSPortCoder' is unavailable in Swift: Use NSXPCConnection instead}} let ca = NSConnectionDidDieNotification // expected-error {{'NSConnectionDidDieNotification' is unavailable in Swift: Use NSXPCConnection instead}} + _ = ca let cc = NSConnectionReplyMode // expected-error {{'NSConnectionReplyMode' is unavailable in Swift: Use NSXPCConnection instead}} + _ = cc _ = o.classForPortCoder // expected-error {{'classForPortCoder' is unavailable in Swift: Use NSXPCConnection instead}} } diff --git a/test/ClangImporter/swift2_warnings.swift b/test/ClangImporter/swift2_warnings.swift index ed26e7d445ba4..ce5a26af75319 100644 --- a/test/ClangImporter/swift2_warnings.swift +++ b/test/ClangImporter/swift2_warnings.swift @@ -8,8 +8,7 @@ import ImportAsMember.A import AppKit func testOldTypeNames() { - var ps: NSPostingStyle? // expected-error{{'NSPostingStyle' has been renamed to 'NotificationQueue.PostingStyle'}}{{11-25=NotificationQueue.PostingStyle}} - + let _: NSPostingStyle? // expected-error{{'NSPostingStyle' has been renamed to 'NotificationQueue.PostingStyle'}}{{10-24=NotificationQueue.PostingStyle}} _ = NSPostingStyle(rawValue: 1) // expected-error{{'NSPostingStyle' has been renamed to 'NotificationQueue.PostingStyle'}}{{7-21=NotificationQueue.PostingStyle}} diff --git a/test/SPI/implementation_only_spi_import_exposability.swift b/test/SPI/implementation_only_spi_import_exposability.swift index 6fe3bde426227..c4c230b901bad 100644 --- a/test/SPI/implementation_only_spi_import_exposability.swift +++ b/test/SPI/implementation_only_spi_import_exposability.swift @@ -38,8 +38,10 @@ public struct PublicStruct : IOIProtocol, SPIProtocol { // expected-error {{cann public func publicInlinable() { spiFunc() // expected-error {{global function 'spiFunc()' is '@_spi' and cannot be referenced from an '@inlinable' function}} ioiFunc() // expected-error {{global function 'ioiFunc()' cannot be used in an '@inlinable' function because 'Lib' was imported implementation-only}} - let s = SPIStruct() // expected-error {{struct 'SPIStruct' is '@_spi' and cannot be referenced from an '@inlinable' function}} - let i = IOIStruct() // expected-error {{struct 'IOIStruct' cannot be used in an '@inlinable' function because 'Lib' was imported implementation-only}} + let _ = SPIStruct() // expected-error {{struct 'SPIStruct' is '@_spi' and cannot be referenced from an '@inlinable' function}} + // expected-error@-1 {{initializer 'init()' is '@_spi' and cannot be referenced from an '@inlinable' function}} + let _ = IOIStruct() // expected-error {{struct 'IOIStruct' cannot be used in an '@inlinable' function because 'Lib' was imported implementation-only}} + // expected-error@-1 {{initializer 'init()' cannot be used in an '@inlinable' function because 'Lib' was imported implementation-only}} } } diff --git a/test/SPI/local_spi_decls.swift b/test/SPI/local_spi_decls.swift index adb14a43bc5fe..3b4e68142d525 100644 --- a/test/SPI/local_spi_decls.swift +++ b/test/SPI/local_spi_decls.swift @@ -17,6 +17,8 @@ // expected-note @-1 3 {{class 'SPIClass' is not '@usableFromInline' or public}} // expected-note @-2 {{class 'SPIClass' is not public}} public init() {} + // expected-note@-1 2 {{initializer 'init()' is not '@usableFromInline' or public}} + // expected-note@-2 {{initializer 'init()' is not public}} } class InternalClass {} // expected-note 2 {{type declared here}} private class PrivateClass {} // expected-note 2 {{type declared here}} @@ -32,17 +34,25 @@ public func useOfSPITypeInvalid() -> SPIClass { fatalError() } // expected-error func inlinable() -> SPIClass { // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}} spiFunc() // expected-error {{global function 'spiFunc()' is '@_spi' and cannot be referenced from an '@inlinable' function}} _ = SPIClass() // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}} + // expected-error@-1 {{initializer 'init()' is '@_spi' and cannot be referenced from an '@inlinable' function}} } @_spi(S) public struct SPIStruct { // expected-note 2 {{struct 'SPIStruct' is not '@usableFromInline' or public}} + // FIXME: Misleading diagnostic here public init() {} + // expected-note@-1 2 {{initializer 'init()' is not '@usableFromInline' or public}} } @frozen public struct FrozenStruct { @_spi(S) public var spiInFrozen = SPIStruct() // expected-error {{struct 'SPIStruct' is '@_spi' and cannot be referenced from a property initializer in a '@frozen' type}} - // expected-error @-1 {{stored property 'spiInFrozen' cannot be declared '@_spi' in a '@frozen' struct}} + // expected-error@-1 {{stored property 'spiInFrozen' cannot be declared '@_spi' in a '@frozen' struct}} + // expected-error@-2 {{cannot use struct 'SPIStruct' here; it is SPI}} + // expected-error@-3 {{initializer 'init()' is '@_spi' and cannot be referenced from a property initializer in a '@frozen' type}} var spiTypeInFrozen = SPIStruct() // expected-error {{struct 'SPIStruct' is '@_spi' and cannot be referenced from a property initializer in a '@frozen' type}} + // expected-error@-1 {{cannot use struct 'SPIStruct' here; it is SPI}} + // expected-error@-2 {{initializer 'init()' is '@_spi' and cannot be referenced from a property initializer in a '@frozen' type}} + private var spiTypeInFrozen1: SPIClass // expected-error {{cannot use class 'SPIClass' here; it is SPI}} } @@ -98,7 +108,8 @@ public struct NestedParent { } public func publicFuncWithDefaultValue(_ p: SPIClass = SPIClass()) {} // expected-error {{cannot use class 'SPIClass' here; it is SPI}} -// expected-error @-1 {{class 'SPIClass' is '@_spi' and cannot be referenced from a default argument value}} +// expected-error@-1 {{class 'SPIClass' is '@_spi' and cannot be referenced from a default argument value}} +// expected-error@-2 {{initializer 'init()' is '@_spi' and cannot be referenced from a default argument value}} @_spi(S) public func spiFuncWithDefaultValue(_ p: SPIClass = SPIClass()) {} @@ -107,6 +118,7 @@ public func spiFuncWithDefaultValue(_ p: SPIClass = SPIClass()) {} public func inlinablePublic() { spiFunc() // expected-error {{global function 'spiFunc()' is '@_spi' and cannot be referenced from an '@inlinable' function}} let _ = SPIClass() // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}} + // expected-error@-1 {{initializer 'init()' is '@_spi' and cannot be referenced from an '@inlinable' function}} } @_spi(S) diff --git a/test/SPI/spi_client.swift b/test/SPI/spi_client.swift index ff02276f759aa..c3d803b8a722a 100644 --- a/test/SPI/spi_client.swift +++ b/test/SPI/spi_client.swift @@ -53,15 +53,16 @@ public func publicUseOfSPI(param: SPIClass) -> SPIClass {} // expected-error 2{{ public func publicUseOfSPI2() -> [SPIClass] {} // expected-error {{cannot use class 'SPIClass' here; it is an SPI imported from 'SPIHelper'}} @inlinable -public func inlinable() -> SPIClass { // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}} +public func inlinable1() -> SPIClass { // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}} spiFunc() // expected-error {{global function 'spiFunc()' is '@_spi' and cannot be referenced from an '@inlinable' function}} _ = SPIClass() // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}} + // expected-error@-1 {{initializer 'init()' is '@_spi' and cannot be referenced from an '@inlinable' function}} _ = [SPIClass]() // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}} } @_spi(S) @inlinable -public func inlinable() -> SPIClass { +public func inlinable2() -> SPIClass { spiFunc() _ = SPIClass() _ = [SPIClass]() diff --git a/test/Sema/Inputs/availability_multi_other.swift b/test/Sema/Inputs/availability_multi_other.swift index ccd1c58e88f25..40c6c97ae4d61 100644 --- a/test/Sema/Inputs/availability_multi_other.swift +++ b/test/Sema/Inputs/availability_multi_other.swift @@ -1,6 +1,10 @@ -// This file is used by Sema/availability_versions_multi.swift to +// This file was used by Sema/availability_versions_multi.swift to // test that we build enough of the type refinement context as needed to // validate declarations when resolving declaration signatures. +// +// These days, we don't validate availability in secondary files at all, +// so the test here is just to make sure we don't crash. +// // This file relies on the minimum deployment target for OS X being 10.9. @available(OSX, introduced: 99.52) @@ -18,11 +22,9 @@ class OtherIntroduced99_51 { _ = PrivateIntroduced99_52() } - // This method uses a 99_52 only type in its signature, so validating - // the declaration should produce an availability error - func returns99_52() -> OtherIntroduced99_52 { // expected-error {{'OtherIntroduced99_52' is only available in macOS 99.52 or newer}} - // expected-note@-1 {{add @available attribute to enclosing instance method}} - + // This method uses a 99_52 only type in its signature, but we don't + // validate it since it comes from a secondary file. + func returns99_52() -> OtherIntroduced99_52 { // Body is not type checked (by design) so no error is expected for unavailable type used in return. return OtherIntroduced99_52() } @@ -86,5 +88,4 @@ extension OtherIntroduced99_51 { class OtherIntroduced99_53 { } -var globalFromOtherOn99_52 : OtherIntroduced99_52? = nil // expected-error {{'OtherIntroduced99_52' is only available in macOS 99.52 or newer}} - // expected-note@-1 {{add @available attribute to enclosing var}} +var globalFromOtherOn99_52 : OtherIntroduced99_52? = nil diff --git a/test/Sema/Inputs/implementation-only-imports/directs.swift b/test/Sema/Inputs/implementation-only-imports/directs.swift index 4053dce984f72..d08c505d508e8 100644 --- a/test/Sema/Inputs/implementation-only-imports/directs.swift +++ b/test/Sema/Inputs/implementation-only-imports/directs.swift @@ -1,6 +1,7 @@ @_exported import indirects public struct StructFromDirect { + public init() {} public func method() {} public var property: Int { get { return 0 } diff --git a/test/Sema/availability.swift b/test/Sema/availability.swift index 58b0cf0b2a62a..375ff77f8a51b 100644 --- a/test/Sema/availability.swift +++ b/test/Sema/availability.swift @@ -19,12 +19,16 @@ struct Outer { func foo(x : NSUInteger) { // expected-error {{'NSUInteger' is unavailable: use 'Int' instead}} let y : NSUInteger = 42 // expected-error {{'NSUInteger' is unavailable: use 'Int' instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to specified type 'NSUInteger'}} let z : MyModule.NSUInteger = 42 // expected-error {{'NSUInteger' is unavailable: use 'Int' instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to specified type 'NSUInteger'}} - let z2 : Outer.NSUInteger = 42 // expected-error {{'NSUInteger' is unavailable: use 'UInt' instead}} + let z2 : Outer.NSUInteger = 42 // expected-error {{'NSUInteger' is unavailable: use 'UInt' instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to specified type 'Outer.NSUInteger'}} - let z3 : MyModule.Outer.NSUInteger = 42 // expected-error {{'NSUInteger' is unavailable: use 'UInt' instead}} + let z3 : MyModule.Outer.NSUInteger = 42 // expected-error {{'NSUInteger' is unavailable: use 'UInt' instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to specified type 'Outer.NSUInteger'}} } // Test preventing overrides (but allowing shadowing) of unavailable methods. diff --git a/test/Sema/availability_compound.swift b/test/Sema/availability_compound.swift index bb88775182f15..eb8de9f69ef64 100644 --- a/test/Sema/availability_compound.swift +++ b/test/Sema/availability_compound.swift @@ -12,7 +12,16 @@ public struct PublicStruct { public typealias ObsoleteAlias = PublicStruct // expected-note * {{marked unavailable here}} public let a: ObsoleteAlias.Inner? // expected-error {{'ObsoleteAlias' has been renamed to 'PublicStruct'}} + public let b: ObsoleteAlias.Obsolete? // expected-error {{'ObsoleteAlias' has been renamed to 'PublicStruct'}} +// expected-error@-1 {{constant cannot be declared public because its type uses an internal type}} + public let c: Pair? // expected-error {{'ObsoleteAlias' has been renamed to 'PublicStruct'}} +// expected-error@-1 {{'Obsolete' is unavailable}} +// expected-error@-2 {{constant cannot be declared public because its type uses an internal type}} + public let c2: Pair? // expected-error {{'Obsolete' is unavailable}} +// expected-error@-1 {{'ObsoleteAlias' has been renamed to 'PublicStruct'}} +// expected-error@-2 {{constant cannot be declared public because its type uses an internal type}} + public let d: ObsoleteAlias? // expected-error {{'ObsoleteAlias' has been renamed to 'PublicStruct'}} diff --git a/test/Sema/diag_erroneous_iuo.swift b/test/Sema/diag_erroneous_iuo.swift index b6dbdfafa8508..e3c86ec8e7b46 100644 --- a/test/Sema/diag_erroneous_iuo.swift +++ b/test/Sema/diag_erroneous_iuo.swift @@ -70,11 +70,11 @@ func genericFunctionSigilArray( } protocol P { - associatedtype T // expected-note {{protocol requires nested type 'T'; do you want to add it?}} - associatedtype U // expected-note {{protocol requires nested type 'U'; do you want to add it?}} + associatedtype T + associatedtype U } -struct S : P { // expected-error {{type 'S' does not conform to protocol 'P'}} +struct S : P { typealias T = ImplicitlyUnwrappedOptional // expected-error {{'ImplicitlyUnwrappedOptional' has been renamed to 'Optional'}}{{17-44=Optional}} typealias U = Optional> // expected-error {{'ImplicitlyUnwrappedOptional' has been renamed to 'Optional'}}{{26-53=Optional}} diff --git a/test/Sema/implementation-only-import-inlinable-indirect.swift b/test/Sema/implementation-only-import-inlinable-indirect.swift index 398726a0b9d56..ecc711bacae6b 100644 --- a/test/Sema/implementation-only-import-inlinable-indirect.swift +++ b/test/Sema/implementation-only-import-inlinable-indirect.swift @@ -11,16 +11,18 @@ @inlinable public func testStructFromIndirect() { _ = StructFromIndirect() // expected-error {{struct 'StructFromIndirect' cannot be used in an '@inlinable' function because 'indirects' was imported implementation-only}} + // expected-error@-1 {{initializer 'init()' cannot be used in an '@inlinable' function because 'indirects' was imported implementation-only}} } @inlinable public func testAliasFromIndirect() { _ = AliasFromIndirect() // expected-error {{type alias 'AliasFromIndirect' cannot be used in an '@inlinable' function because 'indirects' was imported implementation-only}} + // expected-error@-1 {{initializer 'init()' cannot be used in an '@inlinable' function because 'indirects' was imported implementation-only}} } @inlinable public func testGenericAliasFromIndirect() { - _ = GenericAliasFromIndirect() // expected-error {{type alias 'GenericAliasFromIndirect' cannot be used in an '@inlinable' function because 'indirects' was imported implementation-only}} + _ = GenericAliasFromIndirect.self // expected-error {{type alias 'GenericAliasFromIndirect' cannot be used in an '@inlinable' function because 'indirects' was imported implementation-only}} } // Functions diff --git a/test/Sema/implementation-only-import-inlinable-multifile.swift b/test/Sema/implementation-only-import-inlinable-multifile.swift index a28dc9216a78c..0f6eda73096b5 100644 --- a/test/Sema/implementation-only-import-inlinable-multifile.swift +++ b/test/Sema/implementation-only-import-inlinable-multifile.swift @@ -12,6 +12,7 @@ @inlinable public func testStructFromDirect() { _ = StructFromDirect() // expected-error {{struct 'StructFromDirect' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} + // expected-error@-1 {{initializer 'init()' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} } @inlinable @@ -22,6 +23,7 @@ public func testStructFromIndirect() { @inlinable public func testAliasFromDirect() { _ = AliasFromDirect() // expected-error {{type alias 'AliasFromDirect' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} + // expected-error@-1 {{initializer 'init()' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} } @inlinable @@ -31,7 +33,7 @@ public func testAliasFromIndirect() { @inlinable public func testGenericAliasFromDirect() { - _ = GenericAliasFromDirect() // expected-error {{type alias 'GenericAliasFromDirect' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} + _ = GenericAliasFromDirect.self // expected-error {{type alias 'GenericAliasFromDirect' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} } @inlinable diff --git a/test/Sema/implementation-only-import-inlinable.swift b/test/Sema/implementation-only-import-inlinable.swift index c457545e9132e..bf9558fbb5487 100644 --- a/test/Sema/implementation-only-import-inlinable.swift +++ b/test/Sema/implementation-only-import-inlinable.swift @@ -12,6 +12,7 @@ import indirects @inlinable public func testStructFromDirect() { _ = StructFromDirect() // expected-error {{struct 'StructFromDirect' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} + // expected-error@-1 {{initializer 'init()' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} } @inlinable @@ -22,6 +23,7 @@ public func testStructFromIndirect() { @inlinable public func testAliasFromDirect() { _ = AliasFromDirect() // expected-error {{type alias 'AliasFromDirect' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} + // expected-error@-1 {{initializer 'init()' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} } @inlinable @@ -31,7 +33,7 @@ public func testAliasFromIndirect() { @inlinable public func testGenericAliasFromDirect() { - _ = GenericAliasFromDirect() // expected-error {{type alias 'GenericAliasFromDirect' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} + _ = GenericAliasFromDirect.self // expected-error {{type alias 'GenericAliasFromDirect' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} } @inlinable diff --git a/test/attr/attr_availability.swift b/test/attr/attr_availability.swift index f1631720b8263..5a498da129905 100644 --- a/test/attr/attr_availability.swift +++ b/test/attr/attr_availability.swift @@ -54,8 +54,8 @@ typealias YourCollection = MyCollection // expected-note {{'Yo var x : YourCollection // expected-error {{'YourCollection' has been renamed to 'MyCollection'}}{{9-23=MyCollection}} -var x : int // expected-error {{'int' is unavailable: oh no you don't}} -var y : float // expected-error {{'float' has been renamed to 'Float'}}{{9-14=Float}} +var y : int // expected-error {{'int' is unavailable: oh no you don't}} +var z : float // expected-error {{'float' has been renamed to 'Float'}}{{9-14=Float}} // Encoded message @available(*, unavailable, message: "This message has a double quote \"") diff --git a/test/attr/attr_inlinable.swift b/test/attr/attr_inlinable.swift index ade8d9e7278c1..2ce90feff2374 100644 --- a/test/attr/attr_inlinable.swift +++ b/test/attr/attr_inlinable.swift @@ -19,8 +19,10 @@ public func publicFunction() {} private struct PrivateStruct {} // expected-note@-1 3{{struct 'PrivateStruct' is not '@usableFromInline' or public}} +// expected-note@-2 {{initializer 'init()' is not '@usableFromInline' or public}} struct InternalStruct {} // expected-note@-1 3{{struct 'InternalStruct' is not '@usableFromInline' or public}} +// expected-note@-2 {{initializer 'init()' is not '@usableFromInline' or public}} @usableFromInline struct VersionedStruct { @usableFromInline init() {} } @@ -75,8 +77,10 @@ public struct Struct { let _ = VersionedStruct() let _ = InternalStruct() // expected-error@-1 {{struct 'InternalStruct' is internal and cannot be referenced from an '@inlinable' function}} + // expected-error@-2 {{initializer 'init()' is internal and cannot be referenced from an '@inlinable' function}} let _ = PrivateStruct() // expected-error@-1 {{struct 'PrivateStruct' is private and cannot be referenced from an '@inlinable' function}} + // expected-error@-2 {{initializer 'init()' is private and cannot be referenced from an '@inlinable' function}} } private func privateMethod() {} @@ -107,7 +111,7 @@ public struct Struct { private func privateInlinableMethod() { // expected-error@-2 {{'@inlinable' attribute can only be applied to public declarations, but 'privateInlinableMethod' is private}} struct Nested {} - // OK + // expected-error@-1 {{type 'Nested' cannot be nested inside an '@inlinable' function}} } @inline(__always) @@ -143,14 +147,18 @@ enum InternalEnum { // expected-note@-1 2{{enum 'InternalEnum' is not '@usableFromInline' or public}} // expected-note@-2 {{type declared here}} case apple + // expected-note@-1 {{enum case 'apple' is not '@usableFromInline' or public}} case orange + // expected-note@-1 {{enum case 'orange' is not '@usableFromInline' or public}} } @inlinable public func usesInternalEnum() { _ = InternalEnum.apple // expected-error@-1 {{enum 'InternalEnum' is internal and cannot be referenced from an '@inlinable' function}} + // expected-error@-2 {{enum case 'apple' is internal and cannot be referenced from an '@inlinable' function}} let _: InternalEnum = .orange // expected-error@-1 {{enum 'InternalEnum' is internal and cannot be referenced from an '@inlinable' function}} + // expected-error@-2 {{enum case 'orange' is internal and cannot be referenced from an '@inlinable' function}} } @usableFromInline enum VersionedEnum { @@ -318,4 +326,4 @@ public struct PrivateInlinableCrash { func innerFunction3(x: () = versionedFunction()) {} func innerFunction4(x: () = publicFunction()) {} -} \ No newline at end of file +} diff --git a/test/decl/typealias/protocol.swift b/test/decl/typealias/protocol.swift index aa5892f4df7c0..7cb08899aa901 100644 --- a/test/decl/typealias/protocol.swift +++ b/test/decl/typealias/protocol.swift @@ -276,7 +276,7 @@ extension P10 where A == X { } extension P10 where A == X { } -extension P10 where V == Int { } // expected-warning 2{{'V' is deprecated: just use Int, silly}} +extension P10 where V == Int { } // expected-warning {{'V' is deprecated: just use Int, silly}} // expected-warning@-1{{neither type in same-type constraint ('Self.V' (aka 'Int') or 'Int') refers to a generic parameter or associated type}} // rdar://problem/36003312 diff --git a/test/stdlib/StringCompatibilityDiagnostics.swift b/test/stdlib/StringCompatibilityDiagnostics.swift index 5a594f1fa40b1..c69144f71fffa 100644 --- a/test/stdlib/StringCompatibilityDiagnostics.swift +++ b/test/stdlib/StringCompatibilityDiagnostics.swift @@ -1,8 +1,8 @@ // RUN: %target-swift-frontend -typecheck -swift-version 5 %s -verify func testPopFirst() { - var str = "abc" - var charView: String.CharacterView // expected-error{{'CharacterView' is unavailable: Please use String directly}} + let str = "abc" + let charView: String.CharacterView // expected-error{{'CharacterView' is unavailable: Please use String directly}} _ = str.characters // expected-error{{'characters' is unavailable: Please use String directly}} dump(charView) @@ -11,7 +11,7 @@ func testPopFirst() { _ = substr.characters.popFirst() // expected-error{{'characters' is unavailable: Please use Substring directly}} _ = substr.unicodeScalars.popFirst() // ok - var charSubView: Substring.CharacterView // expected-error{{'CharacterView' is unavailable: Please use Substring directly}} + let charSubView: Substring.CharacterView // expected-error{{'CharacterView' is unavailable: Please use Substring directly}} _ = substr.characters // expected-error{{'characters' is unavailable: Please use Substring directly}} dump(charSubView) } From baa9d7142aa1e24f4cf07672d250307e71bc90ab Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 14 Oct 2020 22:53:46 -0400 Subject: [PATCH 470/745] Sema: Pull conformance exportability checking out of resolveType() --- lib/Sema/TypeCheckAccess.cpp | 3 +-- lib/Sema/TypeCheckAvailability.cpp | 22 +++++++++++++++++++ lib/Sema/TypeCheckType.cpp | 7 ------ ...n-only-import-inlinable-conformances.swift | 2 +- test/Sema/spi-inlinable-conformances.swift | 2 +- 5 files changed, 25 insertions(+), 11 deletions(-) diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index b6932bcfea1df..3ce68a68e315e 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -2071,8 +2071,7 @@ class DeclAvailabilityChecker : public DeclVisitor { // Check the type for references to unavailable conformances. if (type) - if (!context->getDeclContext()->isLocalContext()) - diagnoseTypeAvailability(type, context->getLoc(), DC); + diagnoseTypeAvailability(type, context->getLoc(), DC); } void checkGenericParams(const GenericContext *ownerCtx, diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 4b1cee20ac37e..6f667accc96d8 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -2458,6 +2458,20 @@ class AvailabilityWalker : public ASTWalker { walkInOutExpr(IO); return skipChildren(); } + if (auto T = dyn_cast(E)) { + if (!T->isImplicit()) + if (auto type = T->getType()) + diagnoseTypeAvailability(type, E->getLoc(), DC); + } + if (auto CE = dyn_cast(E)) { + for (auto *param : *CE->getParameters()) { + diagnoseTypeAvailability(param->getInterfaceType(), E->getLoc(), DC); + } + diagnoseTypeAvailability(CE->getResultType(), E->getLoc(), DC); + } + if (auto IE = dyn_cast(E)) { + diagnoseTypeAvailability(IE->getCastType(), E->getLoc(), DC); + } return visitChildren(); } @@ -2894,6 +2908,14 @@ class StmtAvailabilityWalker : public ASTWalker { diagnoseTypeReprAvailability(T, DC); return false; } + + std::pair walkToPatternPre(Pattern *P) override { + if (auto *IP = dyn_cast(P)) + if (auto T = IP->getCastType()) + diagnoseTypeAvailability(T, P->getLoc(), DC); + + return std::make_pair(true, P); + } }; } diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index d90db44c76536..d1f0b16a526d4 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -813,13 +813,6 @@ static Type applyGenericArguments(Type type, TypeResolution resolution, const auto result = TypeChecker::applyUnboundGenericArguments( decl, unboundType->getParent(), loc, resolution, args); - const auto genericOptions = genericResolution.getOptions(); - if (!genericOptions.contains(TypeResolutionFlags::AllowUnavailable)) { - if (genericOptions.isAnyExpr() || dc->getParent()->isLocalContext()) - if (dc->getResilienceExpansion() == ResilienceExpansion::Minimal) - TypeChecker::diagnoseGenericTypeExportability(loc, result, dc); - } - // Migration hack. bool isMutablePointer; if (isPointerToVoid(dc->getASTContext(), result, isMutablePointer)) { diff --git a/test/Sema/implementation-only-import-inlinable-conformances.swift b/test/Sema/implementation-only-import-inlinable-conformances.swift index 525018df05dbd..7cc5fd13eac9a 100644 --- a/test/Sema/implementation-only-import-inlinable-conformances.swift +++ b/test/Sema/implementation-only-import-inlinable-conformances.swift @@ -33,7 +33,7 @@ public struct NormalProtoAssocHolder { _ = x // FIXME: We get this error twice: once for the TypeExpr and once for the implicit init. _ = NormalProtoAssocHolder() // expected-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}} - _ = NormalProtoAssocHolder(nil as NormalStruct?) // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}} + _ = NormalProtoAssocHolder(nil as NormalStruct?) // expected-error 2{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}} } func internalConformanceInBoundGeneric() { diff --git a/test/Sema/spi-inlinable-conformances.swift b/test/Sema/spi-inlinable-conformances.swift index 46333cb8f595a..e8e7f25f8160f 100644 --- a/test/Sema/spi-inlinable-conformances.swift +++ b/test/Sema/spi-inlinable-conformances.swift @@ -67,7 +67,7 @@ public struct NormalProtoAssocHolder { _ = x // FIXME: We get this error twice: once for the TypeExpr and once for the implicit init. _ = NormalProtoAssocHolder() // expected-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; the conformance is declared as SPI}} - _ = NormalProtoAssocHolder(nil as NormalStruct?) // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; the conformance is declared as SPI}} + _ = NormalProtoAssocHolder(nil as NormalStruct?) // expected-error 2{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; the conformance is declared as SPI}} } @_spi(AcceptInSPI) From 2a678f29dbcfd48b7ace489937e404ff68fb8f1e Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 13 Oct 2020 15:42:07 -0400 Subject: [PATCH 471/745] Sema: Remove TypeResolutionFlags::AllowUnavailable{,Protocol} --- lib/Sema/PreCheckExpr.cpp | 5 +---- lib/Sema/TypeCheckRequestFunctions.cpp | 8 ++------ lib/Sema/TypeCheckType.cpp | 4 ---- lib/Sema/TypeCheckType.h | 22 ++++++++-------------- 4 files changed, 11 insertions(+), 28 deletions(-) diff --git a/lib/Sema/PreCheckExpr.cpp b/lib/Sema/PreCheckExpr.cpp index 2867f7176643c..32a05f091e5c9 100644 --- a/lib/Sema/PreCheckExpr.cpp +++ b/lib/Sema/PreCheckExpr.cpp @@ -1326,11 +1326,8 @@ TypeExpr *PreCheckExpression::simplifyNestedTypeExpr(UnresolvedDotExpr *UDE) { // Fold 'T.U' into a nested type. if (auto *ITR = dyn_cast(InnerTypeRepr)) { // Resolve the TypeRepr to get the base type for the lookup. - // Disable availability diagnostics here, because the final - // TypeRepr will be resolved again when generating constraints. const auto options = - TypeResolutionOptions(TypeResolverContext::InExpression) | - TypeResolutionFlags::AllowUnavailable; + TypeResolutionOptions(TypeResolverContext::InExpression); const auto resolution = TypeResolution::forContextual(DC, options, [](auto unboundTy) { // FIXME: Don't let unbound generic types escape type resolution. diff --git a/lib/Sema/TypeCheckRequestFunctions.cpp b/lib/Sema/TypeCheckRequestFunctions.cpp index c3abd1a067820..7337cbc612b81 100644 --- a/lib/Sema/TypeCheckRequestFunctions.cpp +++ b/lib/Sema/TypeCheckRequestFunctions.cpp @@ -30,31 +30,27 @@ Type InheritedTypeRequest::evaluate( llvm::PointerUnion decl, unsigned index, TypeResolutionStage stage) const { // Figure out how to resolve types. - TypeResolutionOptions options = None; DeclContext *dc; if (auto typeDecl = decl.dyn_cast()) { if (auto nominal = dyn_cast(typeDecl)) { dc = (DeclContext *)nominal; - - options |= TypeResolutionFlags::AllowUnavailableProtocol; } else { dc = typeDecl->getDeclContext(); } } else { dc = (DeclContext *)decl.get(); - options |= TypeResolutionFlags::AllowUnavailableProtocol; } Optional resolution; switch (stage) { case TypeResolutionStage::Structural: resolution = - TypeResolution::forStructural(dc, options, /*unboundTyOpener*/ nullptr); + TypeResolution::forStructural(dc, None, /*unboundTyOpener*/ nullptr); break; case TypeResolutionStage::Interface: resolution = - TypeResolution::forInterface(dc, options, /*unboundTyOpener*/ nullptr); + TypeResolution::forInterface(dc, None, /*unboundTyOpener*/ nullptr); break; case TypeResolutionStage::Contextual: { diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index d1f0b16a526d4..915bdf67b758e 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -575,7 +575,6 @@ static TypeResolutionOptions adjustOptionsForGenericArgs(TypeResolutionOptions options) { options.setContext(None); options -= TypeResolutionFlags::SILType; - options -= TypeResolutionFlags::AllowUnavailableProtocol; return options; } @@ -1878,9 +1877,6 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr, options.setContext(None); } - if (getASTContext().LangOpts.DisableAvailabilityChecking) - options |= TypeResolutionFlags::AllowUnavailable; - bool isDirect = false; if ((options & TypeResolutionFlags::Direct) && !isa(repr)){ isDirect = true; diff --git a/lib/Sema/TypeCheckType.h b/lib/Sema/TypeCheckType.h index 098e9fd1e31bd..b7d39796099c8 100644 --- a/lib/Sema/TypeCheckType.h +++ b/lib/Sema/TypeCheckType.h @@ -36,37 +36,31 @@ enum class TypeResolutionFlags : uint16_t { /// Whether to allow unspecified types within a pattern. AllowUnspecifiedTypes = 1 << 0, - /// Whether an unavailable protocol can be referenced. - AllowUnavailableProtocol = 1 << 1, - - /// Whether we should allow references to unavailable types. - AllowUnavailable = 1 << 2, - /// Whether the given type can override the type of a typed pattern. - OverrideType = 1 << 3, + OverrideType = 1 << 1, /// Whether we are validating the type for SIL. // FIXME: Move this flag to TypeResolverContext. - SILType = 1 << 4, + SILType = 1 << 2, /// Whether we are parsing a SIL file. Not the same as SILType, /// because the latter is not set if we're parsing an AST type. - SILMode = 1 << 5, + SILMode = 1 << 3, /// Whether this is a resolution based on a non-inferred type pattern. - FromNonInferredPattern = 1 << 6, + FromNonInferredPattern = 1 << 4, /// Whether we are at the direct base of a type expression. - Direct = 1 << 7, + Direct = 1 << 5, /// Whether we should not produce diagnostics if the type is invalid. - SilenceErrors = 1 << 8, + SilenceErrors = 1 << 6, /// Whether to allow module declaration types. - AllowModule = 1 << 9, + AllowModule = 1 << 7, /// Make internal @usableFromInline and @inlinable decls visible. - AllowInlinable = 1 << 10, + AllowInlinable = 1 << 8, }; /// Type resolution contexts that require special handling. From 5b38671b1c6f7199ee8b074a9be560517bad2b7a Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 7 Oct 2020 22:38:19 -0400 Subject: [PATCH 472/745] sil-opt: Disable parser lookup This caught some invalid SIL in a SIL parser test. --- test/SIL/Parser/basic.sil | 4 ++-- tools/sil-opt/SILOpt.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/SIL/Parser/basic.sil b/test/SIL/Parser/basic.sil index f23816c7cde69..ec70bf3877e71 100644 --- a/test/SIL/Parser/basic.sil +++ b/test/SIL/Parser/basic.sil @@ -278,8 +278,8 @@ bb0(%0 : $*T): %2 = apply %1(%0) : $@convention(witness_method: Runcible) (@in Self) -> Int %3 = metatype $@thick T.Type // CHECK: witness_method $T, #Runcible.static_method - %4 = witness_method $T, #Runcible.static_method : $@convention(witness_method: Runcible) (@thick T.Type) -> () - %5 = apply %4(%3) : $@convention(witness_method: Runcible) (@thick T.Type) -> () + %4 = witness_method $T, #Runcible.static_method : $@convention(witness_method: Runcible) (@thick Self.Type) -> () + %5 = apply %4(%3) : $@convention(witness_method: Runcible) (@thick Self.Type) -> () %6 = tuple () destroy_addr %0 : $*T return %6 : $() diff --git a/tools/sil-opt/SILOpt.cpp b/tools/sil-opt/SILOpt.cpp index 2c864cc7d4da5..daefe3dae00d7 100644 --- a/tools/sil-opt/SILOpt.cpp +++ b/tools/sil-opt/SILOpt.cpp @@ -337,6 +337,7 @@ int main(int argc, char **argv) { // cache. Invocation.getClangImporterOptions().ModuleCachePath = ModuleCachePath; Invocation.setParseStdlib(); + Invocation.getLangOptions().DisableParserLookup = true; Invocation.getLangOptions().DisableAvailabilityChecking = true; Invocation.getLangOptions().EnableAccessControl = false; Invocation.getLangOptions().EnableObjCAttrRequiresFoundation = false; From 1cc3a57e9107f0b788c382739ea845f192f1546e Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 14 Oct 2020 03:01:56 -0400 Subject: [PATCH 473/745] Test the just-built dylibs when building unittests on Darwin --- cmake/modules/AddSwiftUnittests.cmake | 5 ++ utils/swift-rpathize.py | 86 +++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100755 utils/swift-rpathize.py diff --git a/cmake/modules/AddSwiftUnittests.cmake b/cmake/modules/AddSwiftUnittests.cmake index 531916e071b79..aff795346e6c0 100644 --- a/cmake/modules/AddSwiftUnittests.cmake +++ b/cmake/modules/AddSwiftUnittests.cmake @@ -40,8 +40,13 @@ function(add_swift_unittest test_dirname) endif() if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") + # Add an @rpath to the swift library directory. set_target_properties(${test_dirname} PROPERTIES BUILD_RPATH ${SWIFT_LIBRARY_OUTPUT_INTDIR}/swift/macosx) + # Force all the swift libraries to be found via rpath. + add_custom_command(TARGET "${test_dirname}" POST_BUILD + COMMAND "${SWIFT_SOURCE_DIR}/utils/swift-rpathize.py" + "$") elseif("${SWIFT_HOST_VARIANT}" STREQUAL "android") swift_android_lib_for_arch(${SWIFT_HOST_VARIANT_ARCH} android_system_libs) set_property(TARGET "${test_dirname}" APPEND PROPERTY LINK_DIRECTORIES diff --git a/utils/swift-rpathize.py b/utils/swift-rpathize.py new file mode 100755 index 0000000000000..e88bdd81988ce --- /dev/null +++ b/utils/swift-rpathize.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python + +# On Darwin, dynamic libraries have an install name. At link time, the +# linker can work with a dylib anywhere in the filesystem, but it will +# write the dylib's install name into the resulting image, and at load +# time that dylib will normally be expected to be found at exactly that +# path. However, if the install name in an image begins with `@rpath`, +# it will instead be searched for in the image's runtime search path +# list. That list may contain absolute paths, but it may also contain +# paths beginning with `@executable_path` or `@loader_path`, meaning the +# path containing the running executable or the image being loaded, +# respectively. +# +# Many of Swift's dylibs are meant to be installed on the system, which +# means they have install names like this: +# /usr/lib/swift/libswiftFoo.dylib +# To support back-deployment, they also provide magic override symbols +# ($ld$install_name) for all the OS versions preceding the addition of +# of the library. When the linker finds a dylib with a matching override +# for the OS deployment target, it ignores the normal install name and +# uses the override path in the linked image's load command. Swift's +# libraries use override paths that begin with `@rpath`, and Swift +# builds images with a runtime search path list that starts with +# /usr/lib/swift but then falls back on a path relative to the image; +# thus, apps will use the system libraries if available but will +# otherwise use fallback libraries. +# +# When we're working on Swift, we usually want to test the libraries +# we just built rather than the system libraries. There are two ways +# to achieve that. The first is to override dyld's runtime search path +# with DYLD_LIBRARY_PATH; this will take precedence over even an +# absolute install name. The second is to make sure the dylibs are +# loaded via an @rpath install name and then link the program with an +# rpath that will use the just-built libraries. Unfortunately, the +# toolchain will ordinarily use an absolute install name instead of +# an @rpath if the deployment target is old enough, subverting testing. +# +# This script looks for dependent dylibs with an absolute path in +# /usr/lib/swift and changes them to use @rpath. + +import argparse +import re +import subprocess +import sys + + +def main(arguments): + parser = argparse.ArgumentParser( + description='Change absolute install names to use @rpath') + parser.add_argument('bin', help='the binary') + + args = parser.parse_args(arguments) + rpathize(args.bin) + + +def rpathize(filename): + dylibsOutput = subprocess.check_output( + ['xcrun', 'dyldinfo', '-dylibs', filename]) + + # The output from dyldinfo -dylibs is a line of header followed by one + # install name per line, indented with spaces. + dylib_regex = re.compile( + r"^\s*(?P/usr/lib/swift/(?P.*\.dylib))\s*$") + + # Build a command to invoke install_name_tool. + command = ['install_name_tool'] + for line in dylibsOutput.splitlines(): + match = dylib_regex.match(line) + if match: + command.append('-change') + command.append(match.group('path')) + command.append('@rpath/' + match.group('filename')) + continue + + # Don't run the command if we didn't find any dylibs to change: + # it's invalid to invoke install_name_tool without any operations. + if len(command) == 1: + return + + # The last argument is the filename to operate on. + command.append(filename) + + subprocess.check_call(command) + + +sys.exit(main(sys.argv[1:])) From 8ac43627540483dfb8c7bd0562a37cfd59ed8388 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 6 Oct 2020 02:56:59 -0400 Subject: [PATCH 474/745] Implement a simple library for task cancellation and status management. There are things about this that I'm far from sold on. In particular, I'm concerned that in order to implement escalation correctly, we're going to have to add a status record for the fact that the task is being executed, which means we're going to have to potentially wait to acquire the status lock; overall, that means making an extra runtime function call and doing some atomics whenever we resume or suspend a task, which is an uncomfortable amount of overhead. The testing here is pretty grossly inadequate, but I wanted to lay down the groundwork here. --- include/swift/ABI/MetadataValues.h | 171 +++++++ include/swift/ABI/Task.h | 275 +++++++++++ include/swift/ABI/TaskStatus.h | 220 +++++++++ include/swift/Runtime/Concurrency.h | 106 ++++ include/swift/Runtime/Config.h | 32 ++ include/swift/Runtime/Mutex.h | 4 + stdlib/public/Concurrency/CMakeLists.txt | 6 + stdlib/public/Concurrency/Mutex.cpp | 21 + stdlib/public/Concurrency/TaskStatus.cpp | 588 +++++++++++++++++++++++ stdlib/public/SwiftShims/Visibility.h | 5 + unittests/runtime/CMakeLists.txt | 15 + unittests/runtime/TaskStatus.cpp | 269 +++++++++++ 12 files changed, 1712 insertions(+) create mode 100644 include/swift/ABI/Task.h create mode 100644 include/swift/ABI/TaskStatus.h create mode 100644 include/swift/Runtime/Concurrency.h create mode 100644 stdlib/public/Concurrency/Mutex.cpp create mode 100644 stdlib/public/Concurrency/TaskStatus.cpp create mode 100644 unittests/runtime/TaskStatus.cpp diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index bd044c5e36361..e3212c146807c 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -1155,6 +1155,16 @@ namespace SpecialPointerAuthDiscriminators { /// Actor enqueue(partialTask:). const uint16_t ActorEnqueuePartialTask = 0x8f3d; + + /// Jobs, tasks, and continuations. + const uint16_t JobInvokeFunction = 0xcc64; // = 52324 + const uint16_t TaskResumeFunction = 0x2c42; // = 11330 + const uint16_t TaskResumeContext = 0x753a; // = 30010 + const uint16_t AsyncContextParent = 0xbda2; // = 48546 + const uint16_t AsyncContextResume = 0xd707; // = 55047 + const uint16_t AsyncContextYield = 0xe207; // = 57863 + const uint16_t CancellationNotificationFunction = 0x1933; // = 6451 + const uint16_t EscalationNotificationFunction = 0x5be4; // = 23524 } /// The number of arguments that will be passed directly to a generic @@ -1867,6 +1877,167 @@ class IntegerLiteralFlags { } }; +/// Kinds of schedulable job.s +enum class JobKind : size_t { + // There are 256 possible job kinds. + + /// An AsyncTask. + Task = 0, + + /// Job kinds >= 192 are private to the implementation. + First_Reserved = 192 +}; + +/// The priority of a job. Higher priorities are larger values. +enum class JobPriority : size_t { + // This is modelled off of Dispatch.QoS, and the values are directly + // stolen from there. + UserInteractive = 0x21, + UserInitiated = 0x19, + Default = 0x15, + Utility = 0x11, + Background = 0x09, + Unspecified = 0x00, +}; + +/// Flags for schedulable jobs. +class JobFlags : public FlagSet { +public: + enum { + Kind = 0, + Kind_width = 8, + + Priority = 8, + Priority_width = 8, + + // 8 bits reserved for more generic job flags. + + // Kind-specific flags. + + Task_IsHeapObject = 24, + Task_IsChildTask = 25, + Task_IsFuture = 26 + }; + + explicit JobFlags(size_t bits) : FlagSet(bits) {} + JobFlags(JobKind kind) { setKind(kind); } + constexpr JobFlags() {} + + FLAGSET_DEFINE_FIELD_ACCESSORS(Kind, Kind_width, JobKind, + getKind, setKind) + + FLAGSET_DEFINE_FIELD_ACCESSORS(Priority, Priority_width, JobPriority, + getPriority, setPriority) + + bool isAsyncTask() const { + return getKind() == JobKind::Task; + } + + FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsHeapObject, + task_isHeapObject, + task_setIsHeapObject) + FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsChildTask, + task_isChildTask, + task_setIsChildTask) + FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsFuture, + task_isFuture, + task_setIsFuture) + +}; + +/// Kinds of task status record. +enum class TaskStatusRecordKind : uint8_t { + /// A DeadlineStatusRecord, which represents an active deadline. + Deadline = 0, + + /// A ChildTaskStatusRecord, which represents the potential for + /// active child tasks. + ChildTask = 1, + + /// A CancellationNotificationStatusRecord, which represents the + /// need to call a custom function when the task is cancelled. + CancellationNotification = 2, + + /// An EscalationNotificationStatusRecord, which represents the + /// need to call a custom function when the task's priority is + /// escalated. + EscalationNotification = 3, + + // Kinds >= 192 are private to the implementation. + First_Reserved = 192, + Private_RecordLock = 192 +}; + +/// Flags for cancellation records. +class TaskStatusRecordFlags : public FlagSet { +public: + enum { + Kind = 0, + Kind_width = 8, + }; + + explicit TaskStatusRecordFlags(size_t bits) : FlagSet(bits) {} + constexpr TaskStatusRecordFlags() {} + TaskStatusRecordFlags(TaskStatusRecordKind kind) { + setKind(kind); + } + + FLAGSET_DEFINE_FIELD_ACCESSORS(Kind, Kind_width, TaskStatusRecordKind, + getKind, setKind) +}; + +/// Kinds of async context. +enum class AsyncContextKind { + /// An ordinary asynchronous function. + Ordinary = 0, + + /// A context which can yield to its caller. + Yielding = 1, + + // Other kinds are reserved for interesting special + // intermediate contexts. + + // Kinds >= 192 are private to the implementation. + First_Reserved = 192 +}; + +/// Flags for async contexts. +class AsyncContextFlags : public FlagSet { +public: + enum { + Kind = 0, + Kind_width = 8, + + CanThrow = 8, + ShouldNotDeallocate = 9 + }; + + explicit AsyncContextFlags(uint32_t bits) : FlagSet(bits) {} + constexpr AsyncContextFlags() {} + AsyncContextFlags(AsyncContextKind kind) { + setKind(kind); + } + + /// The kind of context this represents. + FLAGSET_DEFINE_FIELD_ACCESSORS(Kind, Kind_width, AsyncContextKind, + getKind, setKind) + + /// Whether this context is permitted to throw. + FLAGSET_DEFINE_FLAG_ACCESSORS(CanThrow, canThrow, setCanThrow) + + /// Whether a function should avoid deallocating its context before + /// returning. It should still pass its caller's context to its + /// return continuation. + /// + /// This flag can be set in the caller to optimize context allocation, + /// e.g. if the callee's context size is known statically and simply + /// allocated as part of the caller's context, or if the callee will + /// be called multiple times. + FLAGSET_DEFINE_FLAG_ACCESSORS(ShouldNotDeallocate, + shouldNotDeallocateInCaller, + setShouldNotDeallocateInCaller) +}; + } // end namespace swift #endif // SWIFT_ABI_METADATAVALUES_H diff --git a/include/swift/ABI/Task.h b/include/swift/ABI/Task.h new file mode 100644 index 0000000000000..fdd8465acd681 --- /dev/null +++ b/include/swift/ABI/Task.h @@ -0,0 +1,275 @@ +//===--- Task.h - ABI structures for asynchronous tasks ---------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Swift ABI describing tasks. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_ABI_TASK_H +#define SWIFT_ABI_TASK_H + +#include "swift/ABI/HeapObject.h" +#include "swift/ABI/MetadataValues.h" +#include "swift/Runtime/Config.h" +#include "swift/Basic/STLExtras.h" + +namespace swift { + +class AsyncTask; +class AsyncContext; +class Executor; +class Job; +class TaskStatusRecord; + +/// An ExecutorRef isn't necessarily just a pointer to an executor +/// object; it may have other bits set. +using ExecutorRef = Executor *; + +using JobInvokeFunction = + SWIFT_CC(swift) + void (Job *, ExecutorRef); + +using TaskContinuationFunction = + SWIFT_CC(swift) + void (AsyncTask *, ExecutorRef, AsyncContext *); + +/// A schedulable job. +class alignas(2 * alignof(void*)) Job { +public: + // Reserved for the use of the scheduler. + void *SchedulerPrivate[2]; + + JobFlags Flags; + + // We use this union to avoid having to do a second indirect branch + // when resuming an asynchronous task, which we expect will be the + // common case. + union { + // A function to run a job that isn't an AsyncTask. + JobInvokeFunction * __ptrauth_swift_job_invoke_function RunJob; + + // A function to resume an AsyncTask. + TaskContinuationFunction * __ptrauth_swift_task_resume_function ResumeTask; + }; + + Job(JobFlags flags, JobInvokeFunction *invoke) + : Flags(flags), RunJob(invoke) { + assert(!isAsyncTask() && "wrong constructor for a task"); + } + + Job(JobFlags flags, TaskContinuationFunction *invoke) + : Flags(flags), ResumeTask(invoke) { + assert(isAsyncTask() && "wrong constructor for a non-task job"); + } + + bool isAsyncTask() const { + return Flags.isAsyncTask(); + } + + /// Run this job. + void run(Executor *currentExecutor); +}; + +// The compiler will eventually assume these. +static_assert(sizeof(Job) == 4 * sizeof(void*), + "Job size is wrong"); +static_assert(alignof(Job) == 2 * alignof(void*), + "Job alignment is wrong"); + +/// The current state of a task's status records. +class ActiveTaskStatus { + enum : uintptr_t { + IsCancelled = 0x1, + IsLocked = 0x2, + RecordMask = ~uintptr_t(IsCancelled | IsLocked) + }; + + uintptr_t Value; + +public: + constexpr ActiveTaskStatus() : Value(0) {} + ActiveTaskStatus(TaskStatusRecord *innermostRecord, + bool cancelled, bool locked) + : Value(reinterpret_cast(innermostRecord) + + (locked ? IsLocked : 0) + + (cancelled ? IsCancelled : 0)) {} + + /// Is the task currently cancelled? + bool isCancelled() const { return Value & IsCancelled; } + + /// Is there an active lock on the cancellation information? + bool isLocked() const { return Value & IsLocked; } + + /// Return the innermost cancellation record. Code running + /// asynchronously with this task should not access this record + /// without having first locked it; see swift_taskCancel. + TaskStatusRecord *getInnermostRecord() const { + return reinterpret_cast(Value & RecordMask); + } + + static TaskStatusRecord *getStatusRecordParent(TaskStatusRecord *ptr); + + using record_iterator = + LinkedListIterator; + llvm::iterator_range records() const { + return record_iterator::rangeBeginning(getInnermostRecord()); + } +}; + +/// An asynchronous task. Tasks are the analogue of threads for +/// asynchronous functions: that is, they are a persistent identity +/// for the overall async computation. +class AsyncTask : public Job { +public: + /// The context for resuming the job. When a task is scheduled + /// as a job, the next continuation should be installed as the + /// ResumeTask pointer in the job header, with this serving as + /// the context pointer. + /// + /// We can't protect the data in the context from being overwritten + /// by attackers, but we can at least sign the context pointer to + /// prevent it from being corrupted in flight. + AsyncContext * __ptrauth_swift_task_resume_context ResumeContext; + + /// The currntly-active information about cancellation. + std::atomic Status; + + /// Reserved for the use of the task-local stack allocator. + void *AllocatorPrivate[4]; + + AsyncTask(JobFlags flags, TaskContinuationFunction *run, + AsyncContext *initialContext) + : Job(flags, run), + ResumeContext(initialContext), + Status(ActiveTaskStatus()) { + assert(flags.isAsyncTask()); + } + + void run(ExecutorRef currentExecutor) { + ResumeTask(this, currentExecutor, ResumeContext); + } + + /// Check whether this task has been cancelled. Checking this is, + /// of course, inherently race-prone on its own. + bool isCancelled() const { + return Status.load(std::memory_order_relaxed).isCancelled(); + } + + bool isHeapObject() const { return Flags.task_isHeapObject(); } + HeapObject *heapObjectHeader() { + assert(isHeapObject()); + return reinterpret_cast(this) - 1; + } + + /// A fragment of an async task structure that happens to be a child task. + class ChildFragment { + /// The parent task of this task. + AsyncTask *Parent; + + /// The next task in the singly-linked list of child tasks. + /// The list must start in a `ChildTaskStatusRecord` registered + /// with the parent task. + /// Note that the parent task may have multiple such records. + AsyncTask *NextChild = nullptr; + + public: + AsyncTask *getParent() const { + return Parent; + } + + AsyncTask *getNextChild() const { + return NextChild; + } + }; + + bool hasChildFragment() const { return Flags.task_isChildTask(); } + ChildFragment *childFragment() { + assert(hasChildFragment()); + return reinterpret_cast(this + 1); + } + + // TODO: Future fragment + + static bool classof(const Job *job) { + return job->isAsyncTask(); + } +}; + +// The compiler will eventually assume these. +static_assert(sizeof(AsyncTask) == 10 * sizeof(void*), + "AsyncTask size is wrong"); +static_assert(alignof(AsyncTask) == 2 * alignof(void*), + "AsyncTask alignment is wrong"); + +inline void Job::run(ExecutorRef currentExecutor) { + if (auto task = dyn_cast(this)) + task->run(currentExecutor); + else + RunJob(this, currentExecutor); +} + +/// An asynchronous context within a task. Generally contexts are +/// allocated using the task-local stack alloc/dealloc operations, but +/// there's no guarantee of that, and the ABI is designed to permit +/// contexts to be allocated within their caller's frame. +class alignas(MaximumAlignment) AsyncContext { +public: + /// The parent context. + AsyncContext * __ptrauth_swift_async_context_parent Parent; + + /// The function to call to resume running in the parent context. + /// Generally this means a semantic return, but for some temporary + /// translation contexts it might mean initiating a call. + /// + /// Eventually, the actual type here will depend on the types + /// which need to be passed to the parent. For now, arguments + /// are always written into the context, and so the type is + /// always the same. + TaskContinuationFunction * __ptrauth_swift_async_context_resume + ResumeParent; + + /// Flags describing this context. + /// + /// Note that this field is only 32 bits; any alignment padding + /// following this on 64-bit platforms can be freely used by the + /// function. If the function is a yielding function, that padding + /// is of course interrupted by the YieldToParent field. + AsyncContextFlags Flags; + + // Fields following this point may not be valid in all instances + // of AsyncContext. + + /// The function to call to temporarily resume running in the + /// parent context temporarily. Generally this means a semantic + /// yield. Requires Flags.hasYieldFunction(). + TaskContinuationFunction * __ptrauth_swift_async_context_yield + YieldToParent; + + AsyncContext(AsyncContextFlags flags, + TaskContinuationFunction *resumeParent, + AsyncContext *parent) + : Parent(parent), ResumeParent(resumeParent), Flags(flags) {} + + AsyncContext(AsyncContextFlags flags, + TaskContinuationFunction *resumeParent, + TaskContinuationFunction *yieldToParent, + AsyncContext *parent) + : Parent(parent), ResumeParent(resumeParent), Flags(flags), + YieldToParent(yieldToParent) {} + + AsyncContext(const AsyncContext &) = delete; + AsyncContext &operator=(const AsyncContext &) = delete; +}; + +} // end namespace swift + +#endif diff --git a/include/swift/ABI/TaskStatus.h b/include/swift/ABI/TaskStatus.h new file mode 100644 index 0000000000000..55b48c0675bba --- /dev/null +++ b/include/swift/ABI/TaskStatus.h @@ -0,0 +1,220 @@ +//===--- TaskStatusRecord.h - Structures to track task status --*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Swift ABI describing "status records", the mechanism by which +// tasks track dynamic information about their child tasks, custom +// cancellation hooks, and other information which may need to be exposed +// asynchronously outside of the task. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_ABI_TASKSTATUS_H +#define SWIFT_ABI_TASKSTATUS_H + +#include "swift/ABI/MetadataValues.h" +#include "swift/ABI/Task.h" + +namespace swift { + +/// The abstract base class for all sttatus records. +/// +/// TaskStatusRecords are typically allocated on the stack (possibly +/// in the task context), partially initialized, and then atomically +/// added to the task with `swift_task_addTaskStatusRecord`. While +/// registered with the task, a status record should only be +/// modified in ways that respect the possibility of asynchronous +/// access by a cancelling thread. In particular, the chain of +/// status records must not be disturbed. When the task leaves +/// the scope that requires the status record, the record can +/// be unregistered from the task with `swift_task_removeTaskStatusRecord`, +/// at which point the memory can be returned to the system. +class TaskStatusRecord { +public: + TaskStatusRecordFlags Flags; + TaskStatusRecord *Parent; + + TaskStatusRecord(TaskStatusRecordKind kind, + TaskStatusRecord *parent = nullptr) + : Flags(kind) { + resetParent(parent); + } + + TaskStatusRecord(const TaskStatusRecord &) = delete; + TaskStatusRecord &operator=(const TaskStatusRecord &) = delete; + + TaskStatusRecordKind getKind() const { + return Flags.getKind(); + } + + TaskStatusRecord *getParent() const { + return Parent; + } + + /// Change the parent of this unregistered status record to the + /// given record. + /// + /// This should be used when the record has been previously initialized + /// without knowing what the true parent is. If we decide to cache + /// important information (e.g. the earliest timeout) in the innermost + /// status record, this is the method that should fill that in + /// from the parent. + void resetParent(TaskStatusRecord *newParent) { + Parent = newParent; + // TODO: cache + } + + /// Splice a record out of the status-record chain. + /// + /// Unlike resetParent, this assumes that it's just removing one or + /// more records from the chain and that there's no need to do any + /// extra cache manipulation. + void spliceParent(TaskStatusRecord *newParent) { + Parent = newParent; + } +}; + +TaskStatusRecord * +ActiveTaskStatus::getStatusRecordParent(TaskStatusRecord *ptr) { + return ptr->getParent(); +} + +/// A deadline for the task. If this is reached, the task will be +/// automatically cancelled. The deadline can also be queried and used +/// in other ways. +struct TaskDeadline { + // FIXME: I don't really know what this should look like right now. + // It's probably target-specific. + uint64_t Value; + + bool operator==(const TaskDeadline &other) const { + return Value == other.Value; + } + bool operator<(const TaskDeadline &other) const { + return Value < other.Value; + } +}; + +/// A status record which states that there's an active deadline +/// within the task. +class DeadlineStatusRecord : public TaskStatusRecord { + TaskDeadline Deadline; +public: + DeadlineStatusRecord(TaskDeadline deadline) + : TaskStatusRecord(TaskStatusRecordKind::Deadline), + Deadline(deadline) {} + + TaskDeadline getDeadline() const { + return Deadline; + } + + static bool classof(const TaskStatusRecord *record) { + return record->getKind() == TaskStatusRecordKind::Deadline; + } +}; + +/// A status record which states that a task has one or +/// more active child tasks. +class ChildTaskStatusRecord : public TaskStatusRecord { + /// FIXME: should this be an array? How are things like task + /// nurseries supposed to actually manage this? Should it be + /// atomically moodifiable? + AsyncTask *FirstChild; + +public: + ChildTaskStatusRecord(AsyncTask *child) + : TaskStatusRecord(TaskStatusRecordKind::ChildTask), + FirstChild(child) {} + + /// Return the first child linked by this record. This may be null; + /// if not, it (and all of its successors) are guaranteed to satisfy + /// `isChildTask()`. + AsyncTask *getFirstChild() const { + return FirstChild; + } + + static AsyncTask *getNextChildTask(AsyncTask *task) { + return task->childFragment()->getNextChild(); + } + + using child_iterator = LinkedListIterator; + llvm::iterator_range children() const { + return child_iterator::rangeBeginning(getFirstChild()); + } + + static bool classof(const TaskStatusRecord *record) { + return record->getKind() == TaskStatusRecordKind::ChildTask; + } +}; + +/// A cancellation record which states that a task has an arbitrary +/// function that needs to be called if the task is cancelled. +/// +/// The end of any call to the function will be ordered before the +/// end of a call to unregister this record from the task. That is, +/// code may call `swift_task_removeTaskStatusRecord` and freely +/// assume after it returns that this function will not be +/// subsequently used. +class CancellationNotificationStatusRecord : public TaskStatusRecord { +public: + using FunctionType = void (void *); + +private: + FunctionType * __ptrauth_swift_cancellation_notification_function Function; + void *Argument; + +public: + CancellationNotificationStatusRecord(FunctionType *fn, void *arg) + : TaskStatusRecord(TaskStatusRecordKind::CancellationNotification), + Function(fn), Argument(arg) {} + + void run() { + Function(Argument); + } + + static bool classof(const TaskStatusRecord *record) { + return record->getKind() == TaskStatusRecordKind::CancellationNotification; + } +}; + +/// A status record which says that a task has an arbitrary +/// function that needs to be called if the task's priority is escalated. +/// +/// The end of any call to the function will be ordered before the +/// end of a call to unregister this record from the task. That is, +/// code may call `swift_task_removeTaskStatusRecord` and freely +/// assume after it returns that this function will not be +/// subsequently used. +class EscalationNotificationStatusRecord : public TaskStatusRecord { +public: + using FunctionType = void (void *, JobPriority); + +private: + FunctionType * __ptrauth_swift_escalation_notification_function Function; + void *Argument; + +public: + EscalationNotificationStatusRecord(FunctionType *fn, void *arg) + : TaskStatusRecord(TaskStatusRecordKind::EscalationNotification), + Function(fn), Argument(arg) {} + + void run(JobPriority newPriority) { + Function(Argument, newPriority); + } + + static bool classof(const TaskStatusRecord *record) { + return record->getKind() == TaskStatusRecordKind::EscalationNotification; + } +}; + +} // end namespace swift + +#endif diff --git a/include/swift/Runtime/Concurrency.h b/include/swift/Runtime/Concurrency.h new file mode 100644 index 0000000000000..46daae006991b --- /dev/null +++ b/include/swift/Runtime/Concurrency.h @@ -0,0 +1,106 @@ +//===--- Concurrency.h - Runtime interface for concurrency ------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// The runtime interface for concurrency. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RUNTIME_CONCURRENCY_H +#define SWIFT_RUNTIME_CONCURRENCY_H + +#include "swift/ABI/TaskStatus.h" + +namespace swift { + +/// Cancel a task and all of its child tasks. +/// +/// This can be called from any thread. +/// +/// This has no effect if the task is already cancelled. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void swift_task_cancel(AsyncTask *task); + +/// Escalate the priority of a task and all of its child tasks. +/// +/// This can be called from any thread. +/// +/// This has no effect if the task already has at least the given priority. +/// Returns the priority of the task. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +JobPriority +swift_task_escalate(AsyncTask *task, JobPriority newPriority); + +/// Add a status record to a task. The record should not be +/// modified while it is registered with a task. +/// +/// This must be called synchronously with the task. +/// +/// If the task is already cancelled, returns `false` but still adds +/// the status record. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +bool swift_task_addStatusRecord(AsyncTask *task, + TaskStatusRecord *record); + +/// Add a status record to a task if the task has not already +/// been cancelled. The record should not be modified while it is +/// registered with a task. +/// +/// This must be called synchronously with the task. +/// +/// If the task is already cancelled, returns `false` and does not +/// add the status record. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +bool swift_task_tryAddStatusRecord(AsyncTask *task, + TaskStatusRecord *record); + +/// Remove a status record from a task. After this call returns, +/// the record's memory can be freely modified or deallocated. +/// +/// This must be called synchronously with the task. The record must +/// be registered with the task or else this may crash. +/// +/// The given record need not be the last record added to +/// the task, but the operation may be less efficient if not. +///s +/// Returns false if the task has been cancelled. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +bool swift_task_removeStatusRecord(AsyncTask *task, + TaskStatusRecord *record); + +/// This should have the same representation as an enum like this: +/// enum NearestTaskDeadline { +/// case none +/// case alreadyCancelled +/// case active(TaskDeadline) +/// } +/// TODO: decide what this interface should really be. +struct NearestTaskDeadline { + enum Kind : uint8_t { + None, + AlreadyCancelled, + Active + }; + + TaskDeadline Value; + Kind ValueKind; +}; + +/// Returns the nearest deadline that's been registered with this task. +/// +/// This must be called synchronously with the task. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +NearestTaskDeadline +swift_task_getNearestDeadline(AsyncTask *task); + +} + +#endif diff --git a/include/swift/Runtime/Config.h b/include/swift/Runtime/Config.h index da49b452f3b6e..64c726311e455 100644 --- a/include/swift/Runtime/Config.h +++ b/include/swift/Runtime/Config.h @@ -209,6 +209,30 @@ extern uintptr_t __COMPATIBILITY_LIBRARIES_CANNOT_CHECK_THE_IS_SWIFT_BIT_DIRECTL #define __ptrauth_swift_dynamic_replacement_key \ __ptrauth(ptrauth_key_process_independent_data, 1, \ SpecialPointerAuthDiscriminators::DynamicReplacementKey) +#define __ptrauth_swift_job_invoke_function \ + __ptrauth(ptrauth_key_function_pointer, 1, \ + SpecialPointerAuthDiscriminators::JobInvokeFunction) +#define __ptrauth_swift_task_resume_function \ + __ptrauth(ptrauth_key_function_pointer, 1, \ + SpecialPointerAuthDiscriminators::TaskResumeFunction) +#define __ptrauth_swift_task_resume_context \ + __ptrauth(ptrauth_key_process_independent_data, 1, \ + SpecialPointerAuthDiscriminators::TaskResumeContext) +#define __ptrauth_swift_async_context_parent \ + __ptrauth(ptrauth_key_process_independent_data, 1, \ + SpecialPointerAuthDiscriminators::AsyncContextParent) +#define __ptrauth_swift_async_context_resume \ + __ptrauth(ptrauth_key_function_pointer, 1, \ + SpecialPointerAuthDiscriminators::AsyncContextResume) +#define __ptrauth_swift_async_context_yield \ + __ptrauth(ptrauth_key_function_pointer, 1, \ + SpecialPointerAuthDiscriminators::AsyncContextYield) +#define __ptrauth_swift_cancellation_notification_function \ + __ptrauth(ptrauth_key_function_pointer, 1, \ + SpecialPointerAuthDiscriminators::CancellationNotificationFunction) +#define __ptrauth_swift_escalation_notification_function \ + __ptrauth(ptrauth_key_function_pointer, 1, \ + SpecialPointerAuthDiscriminators::EscalationNotificationFunction) #define swift_ptrauth_sign_opaque_read_resume_function(__fn, __buffer) \ ptrauth_auth_and_resign(__fn, ptrauth_key_function_pointer, 0, \ ptrauth_key_process_independent_code, \ @@ -226,6 +250,14 @@ extern uintptr_t __COMPATIBILITY_LIBRARIES_CANNOT_CHECK_THE_IS_SWIFT_BIT_DIRECTL #define __ptrauth_swift_protocol_witness_function_pointer(__declkey) #define __ptrauth_swift_value_witness_function_pointer(__key) #define __ptrauth_swift_type_metadata_instantiation_function +#define __ptrauth_swift_job_invoke_function +#define __ptrauth_swift_task_resume_function +#define __ptrauth_swift_task_resume_context +#define __ptrauth_swift_async_context_parent +#define __ptrauth_swift_async_context_resume +#define __ptrauth_swift_async_context_yield +#define __ptrauth_swift_cancellation_notification_function +#define __ptrauth_swift_escalation_notification_function #define __ptrauth_swift_runtime_function_entry #define __ptrauth_swift_runtime_function_entry_with_key(__key) #define __ptrauth_swift_runtime_function_entry_strip(__fn) (__fn) diff --git a/include/swift/Runtime/Mutex.h b/include/swift/Runtime/Mutex.h index aff2005d87514..eb568ce314740 100644 --- a/include/swift/Runtime/Mutex.h +++ b/include/swift/Runtime/Mutex.h @@ -76,6 +76,7 @@ class ScopedNotifyAllT { /// multi-threaded producers and consumers to signal each other in a safe way. class ConditionVariable { friend class Mutex; + friend class StaticMutex; ConditionVariable(const ConditionVariable &) = delete; ConditionVariable &operator=(const ConditionVariable &) = delete; @@ -615,6 +616,9 @@ class StaticMutex { void wait(StaticConditionVariable &condition) { ConditionPlatformHelper::wait(condition.Handle, Handle); } + void wait(ConditionVariable &condition) { + ConditionPlatformHelper::wait(condition.Handle, Handle); + } /// See Mutex::lock template diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index a62a63462e6b3..c7addf55947ec 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -13,6 +13,8 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB Actor.swift PartialAsyncTask.swift + TaskStatus.cpp + Mutex.cpp SWIFT_MODULE_DEPENDS_OSX Darwin SWIFT_MODULE_DEPENDS_IOS Darwin @@ -25,6 +27,10 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I SWIFT_MODULE_DEPENDS_HAIKU Glibc SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT + LINK_LIBRARIES swiftCore + + C_COMPILE_FLAGS + -Dswift_Concurrency_EXPORTS SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} -parse-stdlib diff --git a/stdlib/public/Concurrency/Mutex.cpp b/stdlib/public/Concurrency/Mutex.cpp new file mode 100644 index 0000000000000..95dd58e4e05d8 --- /dev/null +++ b/stdlib/public/Concurrency/Mutex.cpp @@ -0,0 +1,21 @@ +//===--- Mutex.cpp - Mutex support code -----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// Include the runtime's mutex support code. +// FIXME: figure out some reasonable way to share this stuff + +#include "../runtime/MutexPThread.cpp" +#include "../runtime/MutexWin32.cpp" + +SWIFT_NORETURN void swift::fatalError(uint32_t flags, const char *format, ...) { + abort(); +} \ No newline at end of file diff --git a/stdlib/public/Concurrency/TaskStatus.cpp b/stdlib/public/Concurrency/TaskStatus.cpp new file mode 100644 index 0000000000000..11f9518e3b20f --- /dev/null +++ b/stdlib/public/Concurrency/TaskStatus.cpp @@ -0,0 +1,588 @@ +//===--- Metadata.cpp - Swift Language ABI Metadata Support ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Routines for cancelling tasks. +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/Concurrency.h" +#include "swift/Runtime/Mutex.h" +#include "swift/ABI/TaskStatus.h" +#include + +using namespace swift; + +/**************************************************************************/ +/************************* RECORD LOCK MANAGEMENT *************************/ +/**************************************************************************/ + +/// A lock used to protect management of task-specific status +/// record locks. +static StaticMutex StatusRecordLockLock; + +namespace { + +/// A lock record which can be used to protect a task's active +/// status records. +/// +/// For the most part, the active task status records of a task are +/// only accessed by the task itself. If that were always true, +/// no synchronization would be required to change them. However, +/// cancellation and escalation can occur asynchronously, and they +/// must be able to inspect the status records without worrying about +/// their concurrent modification or destruction of the records. +/// Therefore, these operations freeze the active status records +/// for their duration. They do this by (1) setting a bit in the +/// task's `Status` field state which says that the records are +/// locked and (2) creating a lock record as the new innermost +/// status record. When the operation is complete, it removes this +/// record and clears the lock bit, then notifies the lock record that +/// the locking operation is complete. +/// +/// When a task wants to change its active status record, but +/// it sees that the locked bit is set in the `Status` field, it +/// must acquire the global status-record lock, find this record +/// (which should be the innermost record), and wait for an unlock. +class StatusRecordLockRecord : public TaskStatusRecord { + /// A lock held by the locking thread for the duration of some + /// operation. The real lock for the status record state is the + /// isLocked() bit in the active state; this lock is just a + /// mechanism to allow threads to wait for that lock. This is + /// rather unfortunately heavyweight, but we're willing make + /// locking expensive if it makes a task's normal record + /// manipulations as cheap as possible. + Mutex LockingThreadLock; + + /// A condition variable that the locking thread waits for if + /// there are active unlock waiters when it tries to unlock. + ConditionVariable LockerQueue; + + // These fields are protected by StatusRecordLockLock, + // not LockingThreadLock. + + /// The number of threads waiting for Locked to become false. + size_t NumUnlockWaiters : CHAR_BIT * sizeof(size_t) - 1; + + /// True if the lock has been cleared. + size_t Locked : 1; + +public: + StatusRecordLockRecord(TaskStatusRecord *parent) + : TaskStatusRecord(TaskStatusRecordKind::Private_RecordLock, parent), + NumUnlockWaiters(0), Locked(true) { + // This is always initialized on the locking thread, and + // the private lock starts off locked. + LockingThreadLock.lock(); + } + + ~StatusRecordLockRecord() { + // Unlock the lock before destroying it. + if (Locked) LockingThreadLock.unlock(); + } + + /// Wait on the queue until there's an unlock. + void waitForUnlock(StaticScopedLock &globalLock) { + assert(Locked); + + // Flag that we're waiting, then drop the global lock. + NumUnlockWaiters++; + { + StaticScopedLock globalUnlock(StatusRecordLockLock); + + // Attempt to acquire the locking-thread lock, thereby + // waiting until the locking thread unlocks the record. + { + ScopedLock acquirePrivateLock(LockingThreadLock); + } + + // Now reacquire the global lock. + } + + // The record should always be unlocked now. + assert(!Locked); + + // Remove ourselves from the count, and if the count is zero, + // wake the locking thread. + NumUnlockWaiters--; + if (NumUnlockWaiters == 0) + LockerQueue.notifyAll(); + } + + /// Wake up any threads that were waiting for unlock. Must be + /// called by the locking thread. + void unlock() { + StaticScopedLock globalLock(StatusRecordLockLock); + assert(Locked); + Locked = false; + + // Unlock the locking-thread lock, balancing out the lock() + // call in the constructor. This allows any unlock waiters + // to wake up. + LockingThreadLock.unlock(); + + // As soon as we don't have any unlock waiters, we're done. + while (NumUnlockWaiters) { + // In the meantime, wait on the locker queue, temporarily + // releasing the global lock. + // FIXME: this is a priority inversion; we really want to + // escalate the priority of the waiting threads. + StatusRecordLockLock.wait(LockerQueue); + } + } + + static bool classof(const TaskStatusRecord *record) { + return record->getKind() == TaskStatusRecordKind::Private_RecordLock; + } +}; + +} + +/// Wait for a task's status record lock to be unlocked. +/// +/// When this function returns, `oldStatus` will have been updated +/// to the last value read and `isLocked()` will be false. +/// Of course, another thread may still be concurrently trying +/// to acquire the record lock. +static void waitForStatusRecordUnlock(AsyncTask *task, + ActiveTaskStatus &oldStatus) { + assert(oldStatus.isLocked()); + + // Acquire the lock. + StaticScopedLock globalLock(StatusRecordLockLock); + + while (true) { + // Check that oldStatus is still correct. + oldStatus = task->Status.load(std::memory_order_acquire); + if (!oldStatus.isLocked()) + return; + + // The innermost entry should be a record lock record; wait + // for it to be unlocked. + auto record = oldStatus.getInnermostRecord(); + auto recordLockRecord = cast(record); + recordLockRecord->waitForUnlock(globalLock); + } +} + +/// Acquire a task's status record lock and return the +/// previous value of its status record state. +/// +/// If `forCancellation` is true, the cancelled bit will be set in the +/// state, and the lock will not be acquired if the task is already +/// cancelled or can be cancelled without the lock. If this occurs, +/// `isCancelled()` will be true for the return value. +static ActiveTaskStatus +acquireStatusRecordLock(AsyncTask *task, + Optional &recordLockRecord, + bool forCancellation) { + auto loadOrdering = forCancellation + ? std::memory_order_acquire + : std::memory_order_relaxed; + + // Load the current state. We can use relaxed loads if this isn't + // for cancellation because (1) this operation should be synchronous + // with the task, so the only thing that can modify it asynchronously + // is a cancelling thread, and (2) we'll reload with acquire ordering + // if a cancelling thread forces us to wait for an unlock. + auto oldStatus = task->Status.load(loadOrdering); + + while (true) { + // Cancellation should be idempotent: if the task has already + // been cancelled (or is being cancelled concurrently), there + // shouldn't be any need to do this work again. + if (oldStatus.isCancelled() && forCancellation) + return oldStatus; + + // If the old info says we're locked, wait for the lock to clear. + if (oldStatus.isLocked()) { + waitForStatusRecordUnlock(task, oldStatus); + continue; + } + + // If we're cancelling and the task has no active status records, + // try to just set the cancelled bit and return. + auto oldRecord = oldStatus.getInnermostRecord(); + if (!oldRecord && forCancellation) { + ActiveTaskStatus newStatus(nullptr, + /*cancelled*/ true, + /*locked*/ false); + if (task->Status.compare_exchange_weak(oldStatus, newStatus, + /*success*/ std::memory_order_relaxed, + /*failure*/ loadOrdering)) + return newStatus; + + // If that failed, just restart. + continue; + } + + // Make (or reconfigure) a lock record. + if (!recordLockRecord) { + recordLockRecord.emplace(oldRecord); + } else { + recordLockRecord->resetParent(oldRecord); + } + + // Install the lock record as the active cancellation info, or + // restart if that fails. + bool newIsCancelled = forCancellation || oldStatus.isCancelled(); + ActiveTaskStatus newStatus(&*recordLockRecord, + /*cancelled*/ newIsCancelled, + /*locked*/ true); + if (task->Status.compare_exchange_weak(oldStatus, newStatus, + /*success*/ std::memory_order_release, + /*failure*/ loadOrdering)) + return oldStatus; + } +} + +/// Release a task's status record lock that was previously +/// acquired on this thread. +static void releaseStatusRecordLock(AsyncTask *task, + ActiveTaskStatus newStatus, + Optional &recordLockRecord) { + assert(!newStatus.isLocked()); + + // We can just unconditionally store because nobody can be modifying + // the state while we've locked it. The task shouldn't depend + // on memory-ordering with anything we've done, so we can use a + // relaxed store. + task->Status.store(newStatus, std::memory_order_relaxed); + + // Unlock the record lock. + recordLockRecord->unlock(); +} + +/**************************************************************************/ +/*************************** RECORD MANAGEMENT ****************************/ +/**************************************************************************/ + +bool swift::swift_task_addStatusRecord(AsyncTask *task, + TaskStatusRecord *newRecord) { + // Load the current state. We can use a relaxed load because we're + // synchronous with the task. + auto oldStatus = task->Status.load(std::memory_order_relaxed); + + while (true) { + // Wait for any active lock to be released. + if (oldStatus.isLocked()) + waitForStatusRecordUnlock(task, oldStatus); + + // Reset the parent of the new record. + newRecord->resetParent(oldStatus.getInnermostRecord()); + + // Set the record as the new innermost record. + // We have to use a release on success to make the initialization of + // the new record visible to the cancelling thread. + ActiveTaskStatus newStatus(newRecord, + oldStatus.isCancelled(), + /*locked*/ false); + if (task->Status.compare_exchange_weak(oldStatus, newStatus, + /*success*/ std::memory_order_release, + /*failure*/ std::memory_order_relaxed)) + return !oldStatus.isCancelled(); + } +} + +bool swift::swift_task_tryAddStatusRecord(AsyncTask *task, + TaskStatusRecord *newRecord) { + // Load the current state. We can use a relaxed load because we're + // synchronous with the task. + auto oldStatus = task->Status.load(std::memory_order_relaxed); + + while (true) { + // If the old info is already cancelled, do nothing. + if (oldStatus.isCancelled()) + return false; + + // Wait for any active lock to be released. + if (oldStatus.isLocked()) { + waitForStatusRecordUnlock(task, oldStatus); + + if (oldStatus.isCancelled()) + return false; + } + + // Reset the parent of the new record. + newRecord->resetParent(oldStatus.getInnermostRecord()); + + // Set the record as the new innermost record. + // We have to use a release on success to make the initialization of + // the new record visible to the cancelling thread. + ActiveTaskStatus newStatus(newRecord, + /*cancelled*/ false, + /*locked*/ false); + if (task->Status.compare_exchange_weak(oldStatus, newStatus, + /*success*/ std::memory_order_release, + /*failure*/ std::memory_order_relaxed)) + return true; + } +} + +bool swift::swift_task_removeStatusRecord(AsyncTask *task, + TaskStatusRecord *record) { + // Load the current state. + auto oldStatus = task->Status.load(std::memory_order_relaxed); + + while (true) { + // Wait for any active lock to be released. + if (oldStatus.isLocked()) + waitForStatusRecordUnlock(task, oldStatus); + + // If the record is the innermost record, try to just pop it off. + if (oldStatus.getInnermostRecord() == record) { + ActiveTaskStatus newStatus(record->getParent(), + oldStatus.isCancelled(), + /*locked*/ false); + if (task->Status.compare_exchange_weak(oldStatus, newStatus, + /*success*/ std::memory_order_release, + /*failure*/ std::memory_order_relaxed)) + return !oldStatus.isCancelled(); + + // Otherwise, restart. + continue; + } + + // If the record is not the innermost record, we need to acquire the + // record lock; there's no way to splice the record list safely with + // a thread that's attempting to acquire the lock. + break; + } + + // Acquire the status record lock. + Optional recordLockRecord; + oldStatus = acquireStatusRecordLock(task, recordLockRecord, + /*forCancellation*/ false); + assert(!oldStatus.isLocked()); + + // We can't observe the record to be the innermost record here because + // that would require some other thread to be concurrently structurally + // changing the set of status records, but we're running + // synchronously with the task. + auto cur = oldStatus.getInnermostRecord(); + assert(cur != record); + + // Splice the record out. + while (true) { + auto next = cur->getParent(); + if (next == record) { + cur->spliceParent(record->getParent()); + break; + } + } + + // Release the lock. Since the record can't be the root, we don't + // have to worry about replacing the root, and oldStatus is always + // exactly what we want to restore. + releaseStatusRecordLock(task, oldStatus, recordLockRecord); + + return !oldStatus.isCancelled(); +} + +/**************************************************************************/ +/****************************** CANCELLATION ******************************/ +/**************************************************************************/ + +/// Perform any cancellation actions required by the given record. +static void performCancellationAction(TaskStatusRecord *record) { + switch (record->getKind()) { + // Deadlines don't require any special support. + case TaskStatusRecordKind::Deadline: + return; + + // Child tasks need to be recursively cancelled. + case TaskStatusRecordKind::ChildTask: { + auto childRecord = cast(record); + for (AsyncTask *child: childRecord->children()) + swift_task_cancel(child); + return; + } + + // Cancellation notifications need to be called. + case TaskStatusRecordKind::CancellationNotification: { + auto notification = + cast(record); + notification->run(); + return; + } + + // Escalation notifications can be ignored. + case TaskStatusRecordKind::EscalationNotification: + return; + + // Record locks shouldn't be found this way, but they don't have + // anything to do anyway. + case TaskStatusRecordKind::Private_RecordLock: + return; + } + + // Other cases can fall through here and be ignored. + // FIXME: allow dynamic extension/correction? +} + +void swift::swift_task_cancel(AsyncTask *task) { + Optional recordLockRecord; + + // Acquire the status record lock. + auto oldStatus = acquireStatusRecordLock(task, recordLockRecord, + /*forCancellation*/ true); + assert(!oldStatus.isLocked()); + + // If we were already cancelled or were able to cancel without acquiring + // the lock, there's nothing else to do. + if (oldStatus.isCancelled()) + return; + + // Otherwise, we've installed the lock record and are now the + // locking thread. + + // Carry out the cancellation operations associated with all + // the active records. + for (auto cur: oldStatus.records()) { + performCancellationAction(cur); + } + + // Release the status record lock, being sure to flag that + // the task is now cancelled. + ActiveTaskStatus cancelledStatus(oldStatus.getInnermostRecord(), + /*cancelled*/ true, + /*locked*/ false); + releaseStatusRecordLock(task, cancelledStatus, recordLockRecord); +} + +/**************************************************************************/ +/******************************* ESCALATION *******************************/ +/**************************************************************************/ + +/// Perform any escalation actions required by the given record. +static void performEscalationAction(TaskStatusRecord *record, + JobPriority newPriority) { + switch (record->getKind()) { + // Deadlines don't require any special support. + case TaskStatusRecordKind::Deadline: + return; + + // Child tasks need to be recursively escalated. + case TaskStatusRecordKind::ChildTask: { + auto childRecord = cast(record); + for (AsyncTask *child: childRecord->children()) + swift_task_escalate(child, newPriority); + return; + } + + // Cancellation notifications can be ignore. + case TaskStatusRecordKind::CancellationNotification: + return; + + // Escalation notifications need to be called. + case TaskStatusRecordKind::EscalationNotification: { + auto notification = + cast(record); + notification->run(newPriority); + return; + } + + // Record locks shouldn't be found this way, but they don't have + // anything to do anyway. + case TaskStatusRecordKind::Private_RecordLock: + return; + } + + // Other cases can fall through here and be ignored. + // FIXME: allow dynamic extension/correction? +} + +JobPriority +swift::swift_task_escalate(AsyncTask *task, JobPriority newPriority) { + Optional recordLockRecord; + + // Fast path: check that the task's priority is not already at laest + // as high as the target. The task's priority can only be modified + // under the status record lock; it's possible that the priority could + // be getting simultaneously escalated, but it's okay for us to return + // before that's complete. + if (task->Flags.getPriority() >= newPriority) + return task->Flags.getPriority(); + + // Acquire the status record lock. + auto oldStatus = acquireStatusRecordLock(task, recordLockRecord, + /*forCancellation*/ false); + assert(!oldStatus.isLocked()); + + // Now that we have the task's status lock, check again that the + // priority is still too low. + auto priorityToReturn = task->Flags.getPriority(); + if (priorityToReturn < newPriority) { + // Change the priority. + task->Flags.setPriority(newPriority); + priorityToReturn = newPriority; + + // TODO: attempt to escalate the thread running the task, if it's + // currently running. This probably requires the task to be enqueued + // on a standard executor. + + // Perform escalation operations for all the status records. + for (auto cur: oldStatus.records()) { + performEscalationAction(cur, newPriority); + } + } + + // Release the status record lock, restoring the old status. + releaseStatusRecordLock(task, oldStatus, recordLockRecord); + + return priorityToReturn; +} + +/**************************************************************************/ +/******************************** DEADLINE ********************************/ +/**************************************************************************/ + +NearestTaskDeadline swift::swift_task_getNearestDeadline(AsyncTask *task) { + // We don't have to worry about the deadline records being + // concurrently modified, so we can just walk the record chain, + // ignoring the possibility of a concurrent cancelling task. + + // Load the current state. + auto oldStatus = task->Status.load(std::memory_order_relaxed); + + NearestTaskDeadline result; + + // If it's already cancelled, we're done. + if (oldStatus.isCancelled()) { + result.ValueKind = NearestTaskDeadline::AlreadyCancelled; + return result; + } + + // If it's locked, wait for the lock; we can't safely step through + // the RecordLockStatusRecord on a different thread. + if (oldStatus.isLocked()) { + waitForStatusRecordUnlock(task, oldStatus); + assert(!oldStatus.isLocked()); + } + + // Walk all the records looking for deadlines. + result.ValueKind = NearestTaskDeadline::None; + for (const auto *record: oldStatus.records()) { + auto deadlineRecord = dyn_cast(record); + if (!deadlineRecord) continue; + auto recordDeadline = deadlineRecord->getDeadline(); + + // If we already have a deadline, pick the earlier. + if (result.ValueKind == NearestTaskDeadline::Active) { + if (recordDeadline < result.Value) + result.Value = recordDeadline; + } else { + result.Value = recordDeadline; + result.ValueKind = NearestTaskDeadline::Active; + } + } + return result; +} diff --git a/stdlib/public/SwiftShims/Visibility.h b/stdlib/public/SwiftShims/Visibility.h index d98425af300a3..4ddd34a12eaf7 100644 --- a/stdlib/public/SwiftShims/Visibility.h +++ b/stdlib/public/SwiftShims/Visibility.h @@ -173,6 +173,11 @@ #else #define SWIFT_IMAGE_EXPORTS_swiftCore 0 #endif +#if defined(swift_Concurrency_EXPORTS) +#define SWIFT_IMAGE_EXPORTS_swift_Concurrency 1 +#else +#define SWIFT_IMAGE_EXPORTS_swift_Concurrency 0 +#endif #define SWIFT_EXPORT_FROM_ATTRIBUTE(LIBRARY) \ SWIFT_MACRO_IF(SWIFT_IMAGE_EXPORTS_##LIBRARY, \ diff --git a/unittests/runtime/CMakeLists.txt b/unittests/runtime/CMakeLists.txt index 33fa05245c412..5070c96876713 100644 --- a/unittests/runtime/CMakeLists.txt +++ b/unittests/runtime/CMakeLists.txt @@ -53,6 +53,21 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND list(APPEND PLATFORM_TARGET_LINK_LIBRARIES DbgHelp) endif() + if(SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY) + list(APPEND PLATFORM_SOURCES + TaskStatus.cpp + ) + list(APPEND PLATFORM_TARGET_LINK_LIBRARIES + swift_Concurrency${SWIFT_PRIMARY_VARIANT_SUFFIX} + ) + endif() + + # Don't complain about these files not being in the sources list. + set(LLVM_OPTIONAL_SOURCES + weak.mm + Refcounting.mm + TaskStatus.cpp) + add_swift_unittest(SwiftRuntimeTests Array.cpp CompatibilityOverride.cpp diff --git a/unittests/runtime/TaskStatus.cpp b/unittests/runtime/TaskStatus.cpp new file mode 100644 index 0000000000000..d4fb10c4ebcc9 --- /dev/null +++ b/unittests/runtime/TaskStatus.cpp @@ -0,0 +1,269 @@ +//===--- TaskStatus.cpp - Unit tests for the task-status API --------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/Concurrency.h" +#include "swift/Basic/STLExtras.h" +#include "gtest/gtest.h" + +using namespace swift; + +namespace { +template struct ValueContext; + +template +using InvokeFunctionRef = + llvm::function_ref *context)>; +using BodyFunctionRef = + llvm::function_ref; + +template struct ValueContext : AsyncContext, Storage { + InvokeFunctionRef StoredInvokeFn; + ValueContext(JobFlags flags, InvokeFunctionRef invokeFn, + Storage &&value) + : AsyncContext(AsyncContextKind::Ordinary, nullptr, nullptr), + Storage(std::forward(value)), + StoredInvokeFn(invokeFn) {} +}; + +// Disable template argument deduction. +template +using undeduced = + typename std::enable_if::value, T>::type; + +template +SWIFT_CC(swift) +static void simpleTaskInvokeFunction(AsyncTask *task, ExecutorRef executor, + AsyncContext *context) { + auto valueContext = static_cast*>(context); + valueContext->StoredInvokeFn(task, executor, valueContext); +} + +template +static void withSimpleTask(JobFlags flags, T &&value, + undeduced> invokeFn, + BodyFunctionRef body) { + TaskContinuationFunction *invokeFP = &simpleTaskInvokeFunction; + ValueContext initialContext(flags, invokeFn, std::forward(value)); + AsyncTask task(flags, invokeFP, &initialContext); + body(&task); +} + +template +static void withSimpleTask(T &&value, + undeduced> invokeFn, + BodyFunctionRef bodyFn) { + withSimpleTask(JobKind::Task, std::forward(value), invokeFn, bodyFn); +} + +static ExecutorRef createFakeExecutor(uintptr_t value) { + return reinterpret_cast(value); +} + +} // end anonymous namespace + +TEST(TaskStatusTest, basicTasks) { + AsyncTask *createdTask = nullptr; + auto createdExecutor = createFakeExecutor(1234); + bool hasRun = false; + + struct Storage { int value; }; + withSimpleTask(Storage{47}, + [&](AsyncTask *task, ExecutorRef executor, + ValueContext *context) { + // The task passed in should be the task we created. + EXPECT_EQ(createdTask, task); + EXPECT_EQ(createdExecutor, executor); + + if (hasRun) { + EXPECT_EQ(94, context->value); + } else { + EXPECT_EQ(47, context->value); + context->value *= 2; + } + + hasRun = true; + + }, [&](AsyncTask *task) { + createdTask = task; + + EXPECT_FALSE(hasRun); + createdTask->run(createdExecutor); + EXPECT_TRUE(hasRun); + createdTask->run(createdExecutor); + EXPECT_TRUE(hasRun); + }); + + EXPECT_TRUE(hasRun); +} + +TEST(TaskStatusTest, cancellation_simple) { + struct Storage { int value; }; + withSimpleTask(Storage{47}, + [&](AsyncTask *task, ExecutorRef executor, + ValueContext *context) { + EXPECT_FALSE(task->isCancelled()); + swift_task_cancel(task); + EXPECT_TRUE(task->isCancelled()); + swift_task_cancel(task); + EXPECT_TRUE(task->isCancelled()); + }, [&](AsyncTask *task) { + task->run(createFakeExecutor(1234)); + }); +} + +// Test basic deadline mechanics (other than actually setting up +// something to cancel the task). Also tests adding and removing +// records quite a bit. +TEST(TaskStatusTest, deadline) { + struct Storage { int value; }; + withSimpleTask(Storage{47}, + [&](AsyncTask *task, ExecutorRef executor, + ValueContext *context) { + EXPECT_FALSE(task->isCancelled()); + + TaskDeadline deadlineOne = { 1234 }; + TaskDeadline deadlineTwo = { 2345 }; + DeadlineStatusRecord recordOne(deadlineOne); + DeadlineStatusRecord recordTwo(deadlineTwo); + bool result; + + NearestTaskDeadline nearest = swift_task_getNearestDeadline(task); + EXPECT_EQ(NearestTaskDeadline::None, nearest.ValueKind); + + // Add deadline 1. Check that we haven't been cancelled yet. + result = swift_task_addStatusRecord(task, &recordOne); + EXPECT_TRUE(result); + + // There should now be an active deadline. + nearest = swift_task_getNearestDeadline(task); + EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind); + EXPECT_EQ(deadlineOne, nearest.Value); + + // Remove deadline 1. Check that we haven't been cancelled yet. + result = swift_task_removeStatusRecord(task, &recordOne); + EXPECT_TRUE(result); + + // There shouldn't be an active deadline anymore. + nearest = swift_task_getNearestDeadline(task); + EXPECT_EQ(NearestTaskDeadline::None, nearest.ValueKind); + + // Add deadline 1, then 2. + result = swift_task_addStatusRecord(task, &recordOne); + EXPECT_TRUE(result); + result = swift_task_addStatusRecord(task, &recordTwo); + EXPECT_TRUE(result); + + // The nearest deadline should be deadline 1. + nearest = swift_task_getNearestDeadline(task); + EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind); + EXPECT_EQ(deadlineOne, nearest.Value); + + // Remove the deadlines. + result = swift_task_removeStatusRecord(task, &recordTwo); + EXPECT_TRUE(result); + result = swift_task_removeStatusRecord(task, &recordOne); + EXPECT_TRUE(result); + + // Add deadline 2, then 1s. + result = swift_task_addStatusRecord(task, &recordTwo); + EXPECT_TRUE(result); + + // In the middle, the nearest deadline should be deadline 2. + nearest = swift_task_getNearestDeadline(task); + EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind); + EXPECT_EQ(deadlineTwo, nearest.Value); + + result = swift_task_addStatusRecord(task, &recordOne); + EXPECT_TRUE(result); + + // The nearest deadline should be deadline 1. + nearest = swift_task_getNearestDeadline(task); + EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind); + EXPECT_EQ(deadlineOne, nearest.Value); + + // Remove the deadlines. + result = swift_task_removeStatusRecord(task, &recordOne); + EXPECT_TRUE(result); + result = swift_task_removeStatusRecord(task, &recordTwo); + EXPECT_TRUE(result); + + // Do the same thing with tryAddStatus. + result = swift_task_tryAddStatusRecord(task, &recordTwo); + EXPECT_TRUE(result); + result = swift_task_tryAddStatusRecord(task, &recordOne); + EXPECT_TRUE(result); + // The nearest deadline should be deadline 1. + nearest = swift_task_getNearestDeadline(task); + EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind); + EXPECT_EQ(deadlineOne, nearest.Value); + result = swift_task_removeStatusRecord(task, &recordOne); + EXPECT_TRUE(result); + result = swift_task_removeStatusRecord(task, &recordTwo); + EXPECT_TRUE(result); + + // Remove out of order. + result = swift_task_addStatusRecord(task, &recordTwo); + EXPECT_TRUE(result); + result = swift_task_addStatusRecord(task, &recordOne); + EXPECT_TRUE(result); + // The nearest deadline should be deadline 1. + nearest = swift_task_getNearestDeadline(task); + EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind); + EXPECT_EQ(deadlineOne, nearest.Value); + result = swift_task_removeStatusRecord(task, &recordTwo); + EXPECT_TRUE(result); + result = swift_task_removeStatusRecord(task, &recordOne); + EXPECT_TRUE(result); + + // Add deadline 2, then cancel. + result = swift_task_addStatusRecord(task, &recordTwo); + EXPECT_TRUE(result); + + // The nearest deadline should be deadline 2. + nearest = swift_task_getNearestDeadline(task); + EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind); + EXPECT_EQ(deadlineTwo, nearest.Value); + + // Cancel. + swift_task_cancel(task); + EXPECT_TRUE(task->isCancelled()); + + // We should report already cancelled now. + nearest = swift_task_getNearestDeadline(task); + EXPECT_EQ(NearestTaskDeadline::AlreadyCancelled, nearest.ValueKind); + + // Add deadline 1. + result = swift_task_addStatusRecord(task, &recordOne); + EXPECT_FALSE(result); + + nearest = swift_task_getNearestDeadline(task); + EXPECT_EQ(NearestTaskDeadline::AlreadyCancelled, nearest.ValueKind); + + result = swift_task_removeStatusRecord(task, &recordOne); + EXPECT_FALSE(result); + + result = swift_task_tryAddStatusRecord(task, &recordOne); + EXPECT_FALSE(result); + + result = swift_task_removeStatusRecord(task, &recordTwo); + EXPECT_FALSE(result); + + nearest = swift_task_getNearestDeadline(task); + EXPECT_EQ(NearestTaskDeadline::AlreadyCancelled, nearest.ValueKind); + + EXPECT_TRUE(task->isCancelled()); + }, [&](AsyncTask *task) { + task->run(createFakeExecutor(1234)); + }); +} From 6c85f267bf5e3c63b9fb1c69b27102c6800269bf Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Thu, 15 Oct 2020 15:04:16 +0200 Subject: [PATCH 475/745] SimplifyCFG: fix a crash caused by an unreachable CFG cycles with block arguments. When SimplifyCFG (temporarily) produces an unreachable CFG cycle, some other transformations in SimplifyCFG didn't deal with this situation correctly. Unfortunately I couldn't create a SIL test case for this bug, so I just added a swift test case. https://bugs.swift.org/browse/SR-13650 rdar://problem/69942431 --- lib/SILOptimizer/Transforms/SimplifyCFG.cpp | 32 +++++++++++-------- lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp | 5 +++ test/SILOptimizer/simplify-cfg-crash.swift | 23 +++++++++++++ 3 files changed, 46 insertions(+), 14 deletions(-) create mode 100644 test/SILOptimizer/simplify-cfg-crash.swift diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index 2038abf6e7dde..bcaac377c3200 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -1248,21 +1248,8 @@ bool SimplifyCFG::simplifyBranchBlock(BranchInst *BI) { LLVM_DEBUG(llvm::dbgs() << "merge bb" << BB->getDebugID() << " with bb" << DestBB->getDebugID() << '\n'); - // If there are any BB arguments in the destination, replace them with the - // branch operands, since they must dominate the dest block. for (unsigned i = 0, e = BI->getArgs().size(); i != e; ++i) { - if (DestBB->getArgument(i) != BI->getArg(i)) { - SILValue Val = BI->getArg(i); - DestBB->getArgument(i)->replaceAllUsesWith(Val); - if (!isVeryLargeFunction) { - if (auto *I = dyn_cast(Val)) { - // Replacing operands may trigger constant folding which then could - // trigger other simplify-CFG optimizations. - ConstFolder.addToWorklist(I); - ConstFolder.processWorkList(); - } - } - } else { + if (DestBB->getArgument(i) == BI->getArg(i)) { // We must be processing an unreachable part of the cfg with a cycle. // bb1(arg1): // preds: bb3 // br bb2 @@ -1273,6 +1260,23 @@ bool SimplifyCFG::simplifyBranchBlock(BranchInst *BI) { // bb3: // preds: bb2 // br bb1(arg1) assert(!isReachable(BB) && "Should only occur in unreachable block"); + return Simplified; + } + } + + // If there are any BB arguments in the destination, replace them with the + // branch operands, since they must dominate the dest block. + for (unsigned i = 0, e = BI->getArgs().size(); i != e; ++i) { + assert(DestBB->getArgument(i) != BI->getArg(i)); + SILValue Val = BI->getArg(i); + DestBB->getArgument(i)->replaceAllUsesWith(Val); + if (!isVeryLargeFunction) { + if (auto *I = dyn_cast(Val)) { + // Replacing operands may trigger constant folding which then could + // trigger other simplify-CFG optimizations. + ConstFolder.addToWorklist(I); + ConstFolder.processWorkList(); + } } } diff --git a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp index 6b5a52240da00..8ef2dd4a1b451 100644 --- a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp +++ b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp @@ -44,6 +44,11 @@ bool ReachableBlocks::visit(SILFunction *f, /// Remove all instructions in the body of \p bb in safe manner by using /// undef. void swift::clearBlockBody(SILBasicBlock *bb) { + + for (SILArgument *arg : bb->getArguments()) { + arg->replaceAllUsesWithUndef(); + } + // Instructions in the dead block may be used by other dead blocks. Replace // any uses of them with undef values. while (!bb->empty()) { diff --git a/test/SILOptimizer/simplify-cfg-crash.swift b/test/SILOptimizer/simplify-cfg-crash.swift new file mode 100644 index 0000000000000..c753a567a2465 --- /dev/null +++ b/test/SILOptimizer/simplify-cfg-crash.swift @@ -0,0 +1,23 @@ +// RUN: %target-swift-frontend -O -emit-sil %s | %FileCheck %s + +// Check that the optimizer does not crash. + + +public class Base { + @inline(never) + final func next() -> Base? { + return self + } +} + +public class Derived : Base {} + +// CHECK: sil {{.*}}testit +public func testit(_ x: Base?) -> Derived? { + var i: Base? = x + while (i is Derived) == false && i != nil { + i = i?.next() + } + return i as? Derived +} + From 7ce04dccf5b05f4fe64ecd50a1c6c40535a8a9eb Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Tue, 13 Oct 2020 16:02:17 -0700 Subject: [PATCH 476/745] [Clang Importer] Set IsSystem module flag for Clang input files when precompiling a clang module This bit is important when we are pre-building system modules to be loaded later in Explicit Module Builds. --- lib/ClangImporter/ClangImporter.cpp | 7 ++++--- test/ClangImporter/pcm-emit-system-module.swift | 8 ++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 test/ClangImporter/pcm-emit-system-module.swift diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 37ad1a8ce665e..3a7122c4eb598 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -1624,11 +1624,12 @@ bool ClangImporter::emitPrecompiledModule(StringRef moduleMapPath, LangOpts->CurrentModule = LangOpts->ModuleName; auto language = getLanguageFromOptions(LangOpts); - auto inputFile = clang::FrontendInputFile( - moduleMapPath, clang::InputKind( - language, clang::InputKind::ModuleMap, false)); auto &FrontendOpts = invocation.getFrontendOpts(); + auto inputFile = clang::FrontendInputFile( + moduleMapPath, clang::InputKind( + language, clang::InputKind::ModuleMap, false), + FrontendOpts.IsSystemModule); FrontendOpts.Inputs = {inputFile}; FrontendOpts.OriginalModuleMap = moduleMapPath.str(); FrontendOpts.OutputFile = outputPath.str(); diff --git a/test/ClangImporter/pcm-emit-system-module.swift b/test/ClangImporter/pcm-emit-system-module.swift new file mode 100644 index 0000000000000..df7199bc268cd --- /dev/null +++ b/test/ClangImporter/pcm-emit-system-module.swift @@ -0,0 +1,8 @@ +// Emit the explicit system module. +// RUN: %empty-directory(%t) +// RUN: %target-swift-emit-pcm -module-name script -Xcc -Xclang -Xcc -emit-module -Xcc -Xclang -Xcc -fsystem-module -o %t/script.pcm %S/Inputs/custom-modules/module.map + +// Verify that the input modulemap if marked [System] in the output of the -dump-pcm action. +// RUN: %swift-dump-pcm %t/script.pcm | %FileCheck %s --check-prefix=CHECK-SYSTEM-INPUT +// CHECK-SYSTEM-INPUT: Input file: {{.*[/\\]}}ClangImporter{{/|\\}}Inputs{{/|\\}}custom-modules{{/|\\}}module.map [System] +import script From 20222bb2ad2cb09778ee110cc80b3a2b945e9fdd Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sat, 10 Oct 2020 15:07:30 -0700 Subject: [PATCH 477/745] [cxx-interop] Bail if trying to convert call result to void. Don't try to coerce the result of a call to void. If the "expected" result type is void, just bail. --- lib/IRGen/GenCall.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 60f4de3fccac7..6795d41cbd7a6 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -1924,6 +1924,9 @@ class SyncCallEmission final : public CallEmission { // This can happen when calling C functions, or class method dispatch thunks // for methods that have covariant ABI-compatible overrides. auto expectedNativeResultType = nativeSchema.getExpandedType(IGF.IGM); + // If the expected result type is void, bail. + if (expectedNativeResultType->isVoidTy()) + return; if (result->getType() != expectedNativeResultType) { result = IGF.coerceValue(result, expectedNativeResultType, IGF.IGM.DataLayout); From b2bb47dee5330234c8eb2fb2d0a41d67949831ec Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sat, 10 Oct 2020 16:08:49 -0700 Subject: [PATCH 478/745] [cxx-interop] Fix Windows tests for non-trivial C++ types. * Replace `??1` with `??0` prefix in SILGen tests. * Replace `call void ` with `call {{.*}} ` because Windows ABI sometimes returns a pointer from the constructor for non-trivial types. * Make `noalias` an optional check. --- .../Cxx/class/type-classification-non-trivial-irgen.swift | 8 ++++---- .../class/type-classification-non-trivial-silgen.swift | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift b/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift index 501b444fa4774..2939b78732840 100644 --- a/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift +++ b/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift @@ -13,7 +13,7 @@ import TypeClassification // CHECK-LABEL: define {{.*}}i1 @"$s4main37testStructWithCopyConstructorAndValueSbyF" // CHECK: [[OBJ:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV // CHECK: [[STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* [[OBJ]] to %struct.StructWithCopyConstructorAndValue* -// CHECK: call void @{{_ZN33StructWithCopyConstructorAndValueC(1|2)Ei|"\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z"}}(%struct.StructWithCopyConstructorAndValue* noalias [[STRUCT]], i32 42) +// CHECK: call {{.*}}@{{_ZN33StructWithCopyConstructorAndValueC(1|2)Ei|"\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z"}}(%struct.StructWithCopyConstructorAndValue* {{(noalias )?}}[[STRUCT]], i32 42) // CHECK: [[OBJ_VAL:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[OBJ]], i32 0, i32 0 // CHECK: [[I_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[OBJ_VAL]], i32 0, i32 0 // CHECK: [[I_VAL_VAL:%.*]] = load i32, i32* [[OBJ_VAL]] @@ -30,7 +30,7 @@ public func testStructWithCopyConstructorAndValue() -> Bool { // CHECK: alloca %TSo33StructWithCopyConstructorAndValueV // CHECK: [[TMP:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV // CHECK: [[MEMBER_STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* %member to %struct.StructWithCopyConstructorAndValue* -// CHECK: call void @{{_ZN33StructWithCopyConstructorAndValueC(1|2)Ei|"\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z"}}(%struct.StructWithCopyConstructorAndValue* noalias [[MEMBER_STRUCT]], i32 42) +// CHECK: call {{.*}}@{{_ZN33StructWithCopyConstructorAndValueC(1|2)Ei|"\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z"}}(%struct.StructWithCopyConstructorAndValue* {{(noalias )?}}[[MEMBER_STRUCT]], i32 42) // CHECK: [[TEMP_MEMBER:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[TMP]], i32 0, i32 0 // CHECK: [[TEMP_MEMBER_VALUE:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[TEMP_MEMBER]], i32 0, i32 0 // CHECK: [[LHS:%.*]] = load i32, i32* [[TEMP_MEMBER_VALUE]] @@ -48,10 +48,10 @@ public func testStructWithSubobjectCopyConstructorAndValue() -> Bool { // CHECK: [[TEMP:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV // CHECK: [[TEMP2:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV // CHECK: [[MEMBER_STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* %member to %struct.StructWithCopyConstructorAndValue* -// CHECK: call void @{{_ZN33StructWithCopyConstructorAndValueC(1|2)Ei|"\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z"}}(%struct.StructWithCopyConstructorAndValue* noalias [[MEMBER_STRUCT]], i32 42) +// CHECK: call {{.*}}@{{_ZN33StructWithCopyConstructorAndValueC(1|2)Ei|"\?\?0StructWithCopyConstructorAndValue@@QEAA@H@Z"}}(%struct.StructWithCopyConstructorAndValue* {{(noalias )?}}[[MEMBER_STRUCT]], i32 42) // CHECK: [[TEMP_AS_STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* [[TEMP]] to %struct.StructWithCopyConstructorAndValue* // CHECK: [[OBJ_AS_STRUCT:%.*]] = bitcast %TSo037StructWithCopyConstructorAndSubobjectcdE5ValueV* [[OBJ]] to %struct.StructWithCopyConstructorAndSubobjectCopyConstructorAndValue* -// CHECK: call void @{{_ZN60StructWithCopyConstructorAndSubobjectCopyConstructorAndValueC(1|2)E33StructWithCopyConstructorAndValue|"\?\?0StructWithCopyConstructorAndSubobjectCopyConstructorAndValue@@QEAA@UStructWithCopyConstructorAndValue@@@Z"}}(%struct.StructWithCopyConstructorAndSubobjectCopyConstructorAndValue* noalias [[OBJ_AS_STRUCT]], %struct.StructWithCopyConstructorAndValue* [[TEMP_AS_STRUCT]]) +// CHECK: call {{.*}}@{{_ZN60StructWithCopyConstructorAndSubobjectCopyConstructorAndValueC(1|2)E33StructWithCopyConstructorAndValue|"\?\?0StructWithCopyConstructorAndSubobjectCopyConstructorAndValue@@QEAA@UStructWithCopyConstructorAndValue@@@Z"}}(%struct.StructWithCopyConstructorAndSubobjectCopyConstructorAndValue* {{(noalias )?}}[[OBJ_AS_STRUCT]], %struct.StructWithCopyConstructorAndValue* [[TEMP_AS_STRUCT]]) // CHECK: [[TEMP_MEMBER:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[TEMP2]], i32 0, i32 0 // CHECK: [[TEMP_MEMBER_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[TEMP_MEMBER]], i32 0, i32 0 // CHECK: [[LHS:%.*]] = load i32, i32* [[TEMP_MEMBER_VAL]] diff --git a/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift b/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift index 51f00d5268432..156665a06912b 100644 --- a/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift +++ b/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift @@ -21,12 +21,12 @@ public func testStructWithDestructor() { // for a "destroy_addr". // CHECK-LABEL: sil [ossa] @$s4main33testStructWithSubobjectDestructoryyF : $@convention(thin) () -> () // CHECK: [[AS:%.*]] = alloc_stack $StructWithSubobjectDestructor -// CHECK: [[FN:%.*]] = function_ref @{{_ZN29StructWithSubobjectDestructorC1Ev|\?\?1StructWithSubobjectDestructor@@QEAA@XZ}} : $@convention(c) () -> @out StructWithSubobjectDestructor +// CHECK: [[FN:%.*]] = function_ref @{{_ZN29StructWithSubobjectDestructorC1Ev|\?\?0StructWithSubobjectDestructor@@QEAA@XZ}} : $@convention(c) () -> @out StructWithSubobjectDestructor // CHECK: apply [[FN]]([[AS]]) : $@convention(c) () -> @out StructWithSubobjectDestructor // CHECK: destroy_addr [[AS]] // CHECK-LABEL: end sil function '$s4main33testStructWithSubobjectDestructoryyF' -// CHECK-LABEL: sil [clang StructWithSubobjectDestructor.init] @{{_ZN29StructWithSubobjectDestructorC1Ev|\?\?1StructWithSubobjectDestructor@@QEAA@XZ}} : $@convention(c) () -> @out StructWithSubobjectDestructor +// CHECK-LABEL: sil [clang StructWithSubobjectDestructor.init] @{{_ZN29StructWithSubobjectDestructorC1Ev|\?\?0StructWithSubobjectDestructor@@QEAA@XZ}} : $@convention(c) () -> @out StructWithSubobjectDestructor public func testStructWithSubobjectDestructor() { let d = StructWithSubobjectDestructor() } From 1d3b051151adee97b51408159f8205e9f28fcb9d Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 13 Oct 2020 11:44:45 -0700 Subject: [PATCH 479/745] [cxx-interop] Remove logic around applying attributes. Removes the logic around applying attributes to C++ constructor's indirect results. Also fixes some commenting. --- lib/ClangImporter/ImportDecl.cpp | 5 +---- lib/IRGen/GenCall.cpp | 18 ++--------------- .../Cxx/class/constructors-irgen.swift | 20 +++---------------- .../Cxx/class/constructors-objc-irgen.swift | 2 +- 4 files changed, 7 insertions(+), 38 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 2e05ff0f8190e..b2128bea8b732 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3522,9 +3522,6 @@ namespace { if (decl->getDefinition() && !decl->isBeingDefined() && !decl->isDependentContext() && decl->needsImplicitDefaultConstructor()) { - // Casting away const here should be OK because - // SwiftDeclConverter::Visit() is in practice called with a non-const - // argument. clang::CXXConstructorDecl *ctor = clangSema.DeclareImplicitDefaultConstructor( const_cast(decl)); @@ -3927,7 +3924,7 @@ namespace { FuncDecl *func = createFuncOrAccessor(Impl.SwiftContext, loc, accessorInfo, name, nameLoc, bodyParams, resultTy, - /*async*/ false, /*throws*/ false, dc, decl); + /*async=*/false, /*throws=*/false, dc, decl); result = func; if (!dc->isModuleScopeContext()) { diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 6795d41cbd7a6..b795d16d433d8 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -338,12 +338,10 @@ llvm::CallingConv::ID irgen::expandCallingConv(IRGenModule &IGM, static void addIndirectResultAttributes(IRGenModule &IGM, llvm::AttributeList &attrs, - unsigned paramIndex, bool allowSRet, - bool noCapture = true) { + unsigned paramIndex, bool allowSRet) { llvm::AttrBuilder b; b.addAttribute(llvm::Attribute::NoAlias); - if (noCapture) - b.addAttribute(llvm::Attribute::NoCapture); + b.addAttribute(llvm::Attribute::NoCapture); if (allowSRet) b.addAttribute(llvm::Attribute::StructRet); attrs = attrs.addAttributes(IGM.getLLVMContext(), @@ -1429,18 +1427,6 @@ void SignatureExpansion::expandExternalSignatureTypes() { } } - if (formalIndirectResult) { - // If the result is a formal indirect result in SIL, that means that the - // Clang function has an explicit output parameter (e.g. it's a C++ - // constructor). This means: - // - Don't mark it `sret`, as this should only be used for C++ return - // values. - // - The Clang function might capture the pointer, so don't specify - // `nocapture`. - addIndirectResultAttributes(IGM, Attrs, 0, /* allowSRet = */ false, - /* noCapture = */ false); - } - if (returnInfo.isIndirect() || returnInfo.isIgnore()) { ResultIRType = IGM.VoidTy; } else { diff --git a/test/Interop/Cxx/class/constructors-irgen.swift b/test/Interop/Cxx/class/constructors-irgen.swift index 75b1c015c403a..4c4999bac484e 100644 --- a/test/Interop/Cxx/class/constructors-irgen.swift +++ b/test/Interop/Cxx/class/constructors-irgen.swift @@ -12,23 +12,9 @@ struct UnsafePointer { } struct UnsafeMutablePointer { } public func createHasVirtualBase() -> HasVirtualBase { - // - The `this` parameter should carry a `noalias` attribute, as it is - // guaranteed that nothing will alias the object before it has been fully - // constructed. Note that this doesn't apply on ABIs (Itanium ARM, - // Microsoft x64) where we insert an (inlined) thunk; in this case, we're - // getting the attributes of the constructor that was generated by Clang, - // which doesn't insert these attributes. - // - // - The `this` parameter should _not_ carry an `sret` attribute because the - // constructor doesn't return the constructed object as a return value. - // - // - The `this` parameter should _not_ carry a `nocapture` attribute (unlike - // Swift constructors that return their result indirectly) because the C++ - // constructor has explicit access to `this` and may capture it. - // // ITANIUM_X64: define swiftcc void @"$ss20createHasVirtualBaseSo0bcD0VyF"(%TSo14HasVirtualBaseV* noalias nocapture sret %0) // ITANIUM_X64-NOT: define - // ITANIUM_X64: call void @_ZN14HasVirtualBaseC1E7ArgType(%struct.HasVirtualBase* noalias %{{[0-9]+}}, i32 %{{[0-9]+}}) + // ITANIUM_X64: call void @_ZN14HasVirtualBaseC1E7ArgType(%struct.HasVirtualBase* %{{[0-9]+}}, i32 %{{[0-9]+}}) // // ITANIUM_ARM: define protected swiftcc void @"$ss20createHasVirtualBaseSo0bcD0VyF"(%TSo14HasVirtualBaseV* noalias nocapture sret %0) // To verify that the thunk is inlined, make sure there's no intervening @@ -48,7 +34,7 @@ public func createHasVirtualBase() -> HasVirtualBase { public func createImplicitDefaultConstructor() -> ImplicitDefaultConstructor { // ITANIUM_X64: define swiftcc i32 @"$ss32createImplicitDefaultConstructorSo0bcD0VyF"() // ITANIUM_X64-NOT: define - // ITANIUM_X64: call void @_ZN26ImplicitDefaultConstructorC1Ev(%struct.ImplicitDefaultConstructor* noalias %{{[0-9]+}}) + // ITANIUM_X64: call void @_ZN26ImplicitDefaultConstructorC1Ev(%struct.ImplicitDefaultConstructor* %{{[0-9]+}}) // // ITANIUM_ARM: define protected swiftcc i32 @"$ss32createImplicitDefaultConstructorSo0bcD0VyF"() // ITANIUM_ARM-NOT: define @@ -66,7 +52,7 @@ public func createStructWithSubobjectCopyConstructorAndValue() { // ITANIUM_X64-LABEL: define swiftcc void @"$ss48createStructWithSubobjectCopyConstructorAndValueyyF"() // ITANIUM_X64: [[MEMBER:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV // ITANIUM_X64: [[MEMBER_AS_STRUCT:%.*]] = bitcast %TSo33StructWithCopyConstructorAndValueV* %member to %struct.StructWithCopyConstructorAndValue* - // ITANIUM_X64: void @_ZN33StructWithCopyConstructorAndValueC1Ev(%struct.StructWithCopyConstructorAndValue* noalias [[MEMBER_AS_STRUCT]]) + // ITANIUM_X64: void @_ZN33StructWithCopyConstructorAndValueC1Ev(%struct.StructWithCopyConstructorAndValue* [[MEMBER_AS_STRUCT]]) // ITANIUM_X64: call %TSo33StructWithCopyConstructorAndValueV* @"$sSo33StructWithCopyConstructorAndValueVWOc"(%TSo33StructWithCopyConstructorAndValueV* [[MEMBER]], %TSo33StructWithCopyConstructorAndValueV* [[TMP:%.*]]) // ITANIUM_X64: [[OBJ_MEMBER:%.*]] = getelementptr inbounds %TSo42StructWithSubobjectCopyConstructorAndValueV, %TSo42StructWithSubobjectCopyConstructorAndValueV* %obj, i32 0, i32 0 // ITANIUM_X64: call %TSo33StructWithCopyConstructorAndValueV* @"$sSo33StructWithCopyConstructorAndValueVWOb"(%TSo33StructWithCopyConstructorAndValueV* [[TMP]], %TSo33StructWithCopyConstructorAndValueV* [[OBJ_MEMBER]]) diff --git a/test/Interop/Cxx/class/constructors-objc-irgen.swift b/test/Interop/Cxx/class/constructors-objc-irgen.swift index a5e8d4d162f87..9b9a0c939708f 100644 --- a/test/Interop/Cxx/class/constructors-objc-irgen.swift +++ b/test/Interop/Cxx/class/constructors-objc-irgen.swift @@ -11,6 +11,6 @@ public func createConstructorWithNSArrayParam() -> ConstructorWithNSArrayParam { // CHECK: [[VAR:%[0-9]+]] = alloca %TSo27ConstructorWithNSArrayParamV, align 1 // CHECK: %{{[0-9]+}} = call swiftcc %TSo7NSArrayC* @"$sSa10FoundationE19_bridgeToObjectiveCSo7NSArrayCyF"(%swift.bridge* %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sypN", i32 0, i32 1)) // CHECK: [[CAST_VAR:%[0-9]+]] = bitcast %TSo27ConstructorWithNSArrayParamV* [[VAR]] to %struct.ConstructorWithNSArrayParam* - // CHECK: call void @_ZN27ConstructorWithNSArrayParamC1EP7NSArray(%struct.ConstructorWithNSArrayParam* noalias [[CAST_VAR]], [[VAR]]* %{{[0-9]+}}) + // CHECK: call void @_ZN27ConstructorWithNSArrayParamC1EP7NSArray(%struct.ConstructorWithNSArrayParam* [[CAST_VAR]], [[VAR]]* %{{[0-9]+}}) return ConstructorWithNSArrayParam([]) } From d9acaf353e5a25a4b5c01c46702407925ab5e2c7 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Wed, 14 Oct 2020 21:12:22 -0700 Subject: [PATCH 480/745] [cxx-interop] Merge synthesized initializer tests into "constructors-silgen". We no longer synthesize initializers for C++ types so this name no longer makes sense. --- test/Interop/Cxx/class/Inputs/constructors.h | 6 +++ .../class/Inputs/synthesized-initializers.h | 5 -- .../Cxx/class/constructors-silgen.swift | 54 +++++++++++++++++-- .../synthesized-initializers-silgen.swift | 44 --------------- 4 files changed, 56 insertions(+), 53 deletions(-) delete mode 100644 test/Interop/Cxx/class/Inputs/synthesized-initializers.h delete mode 100644 test/Interop/Cxx/class/synthesized-initializers-silgen.swift diff --git a/test/Interop/Cxx/class/Inputs/constructors.h b/test/Interop/Cxx/class/Inputs/constructors.h index 7959be4e0a4fe..738b13472f70a 100644 --- a/test/Interop/Cxx/class/Inputs/constructors.h +++ b/test/Interop/Cxx/class/Inputs/constructors.h @@ -37,3 +37,9 @@ struct HasVirtualBase : public virtual Base { HasVirtualBase(ArgType Arg) {} int i; }; + +struct EmptyStruct {}; + +struct IntWrapper { + int x; +}; diff --git a/test/Interop/Cxx/class/Inputs/synthesized-initializers.h b/test/Interop/Cxx/class/Inputs/synthesized-initializers.h deleted file mode 100644 index da20bf036bd10..0000000000000 --- a/test/Interop/Cxx/class/Inputs/synthesized-initializers.h +++ /dev/null @@ -1,5 +0,0 @@ -struct EmptyStruct {}; - -struct IntBox { - int x; -}; diff --git a/test/Interop/Cxx/class/constructors-silgen.swift b/test/Interop/Cxx/class/constructors-silgen.swift index 9472ff3cc08aa..3e256b4746e7e 100644 --- a/test/Interop/Cxx/class/constructors-silgen.swift +++ b/test/Interop/Cxx/class/constructors-silgen.swift @@ -1,12 +1,58 @@ -// RUN: %target-swift-frontend -I %S/Inputs -enable-cxx-interop -emit-sil %s | %FileCheck %s +// RUN: %target-swift-frontend -I %S/Inputs -enable-cxx-interop -emit-silgen %s | %FileCheck %s import Constructors // The most important thing to test here is that the constructor result is // returned with an @out attribute. +// CHECK-LABEL: sil [ossa] @$s4main24testConstructorWithParamyyF : $@convention(thin) () -> () // CHECK: [[VAR:%[0-9]+]] = alloc_stack $ConstructorWithParam -// CHECK: [[LITERAL:%[0-9]+]] = integer_literal $Builtin.Int32, 42 -// CHECK: [[INT:%[0-9]+]] = struct $Int32 ([[LITERAL]] : $Builtin.Int32) +// CHECK: [[LITERAL:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 42 +// CHECK: [[INT:%[0-9]+]] = apply %{{[0-9]+}}([[LITERAL]], %{{[0-9]+}}) // CHECK: [[FUNC:%[0-9]+]] = function_ref @{{_ZN20ConstructorWithParamC1Ei|\?\?0ConstructorWithParam@@QEAA@H@Z}} : $@convention(c) (Int32) -> @out ConstructorWithParam // CHECK: %{{[0-9]+}} = apply [[FUNC]]([[VAR]], [[INT]]) : $@convention(c) (Int32) -> @out ConstructorWithParam -let _ = ConstructorWithParam(42) +// CHECK-LABEL: end sil function '$s4main24testConstructorWithParamyyF' + +// CHECK-LABEL: sil [clang ConstructorWithParam.init] @{{_ZN20ConstructorWithParamC1Ei|\?\?0ConstructorWithParam@@QEAA@H@Z}} : $@convention(c) (Int32) -> @out ConstructorWithParam +public func testConstructorWithParam() { + let c = ConstructorWithParam(42) +} + +// CHECK-LABEL: sil [ossa] @$s4main18emptyTypeNoArgInityyF : $@convention(thin) () -> () +// CHECK: [[AS:%.*]] = alloc_stack $EmptyStruct +// CHECK: [[FN:%.*]] = function_ref @{{_ZN11EmptyStructC1Ev|\?\?0EmptyStruct@@QEAA@XZ}} : $@convention(c) () -> @out EmptyStruct +// CHECK: apply [[FN]]([[AS]]) : $@convention(c) () -> @out EmptyStruct +// CHECK-LABEL: end sil function '$s4main18emptyTypeNoArgInityyF' + +// CHECL-LABEL: sil [clang EmptyStruct.init] @{{_ZN11EmptyStructC1Ev|\?\?0EmptyStruct@@QEAA@XZ}} : $@convention(c) () -> @out EmptyStruct +public func emptyTypeNoArgInit() { + let e = EmptyStruct() +} + +// CHECK-LABEL: sil [ossa] @$s4main25singleMemberTypeNoArgInityyF : $@convention(thin) () -> () +// CHECK: [[AS:%.*]] = alloc_stack $IntWrapper +// CHECK: [[FN:%.*]] = function_ref @{{_ZN10IntWrapperC1Ev|\?\?0IntWrapper@@QEAA@XZ}} : $@convention(c) () -> @out IntWrapper +// CHECK: apply [[FN]]([[AS]]) : $@convention(c) () -> @out IntWrapper +// CHECK-LABEL: end sil function '$s4main25singleMemberTypeNoArgInityyF' + +//CHECK-LABEL: sil [clang IntWrapper.init] @{{_ZN10IntWrapperC1Ev|\?\?0IntWrapper@@QEAA@XZ}} : $@convention(c) () -> @out IntWrapper +public func singleMemberTypeNoArgInit() { + let i = IntWrapper() +} + +// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo10IntWrapperV1xABs5Int32V_tcfC : $@convention(method) (Int32, @thin IntWrapper.Type) -> IntWrapper +// CHECK: bb0([[I:%[0-9]+]] : $Int32, %{{[0-9]+}} : $@thin IntWrapper.Type): +// CHECK-NEXT: [[S:%.*]] = struct $IntWrapper ([[I]] : $Int32) +// CHECK-NEXT: return [[S]] +// CHECK-LABEL: end sil function '$sSo10IntWrapperV1xABs5Int32V_tcfC' +public func singleMemberTypeValueInit() { + let i = IntWrapper(x: 42) +} + +// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo25DefaultConstructorDeletedV1aABSpys5Int32VG_tcfC : $@convention(method) (UnsafeMutablePointer, @thin DefaultConstructorDeleted.Type) -> DefaultConstructorDeleted +// CHECK: bb0([[A:%.*]] : $UnsafeMutablePointer +// CHECK-NEXT: [[OUT:%.*]] = struct $DefaultConstructorDeleted ([[A]] : $UnsafeMutablePointer) +// CHECK-NEXT: return [[OUT]] : $DefaultConstructorDeleted +// CHECK-LABEL: end sil function '$sSo25DefaultConstructorDeletedV1aABSpys5Int32VG_tcfC' +public func deletedConstructor(a: UnsafeMutablePointer) { + let deletedExplicitly = DefaultConstructorDeleted(a: a) +} diff --git a/test/Interop/Cxx/class/synthesized-initializers-silgen.swift b/test/Interop/Cxx/class/synthesized-initializers-silgen.swift deleted file mode 100644 index dac14a687a652..0000000000000 --- a/test/Interop/Cxx/class/synthesized-initializers-silgen.swift +++ /dev/null @@ -1,44 +0,0 @@ -// RUN: %target-swift-frontend -I %S/Inputs -enable-cxx-interop -emit-silgen %s | %FileCheck %s - -import SynthesizedInitializers -import Constructors - -// CHECK-LABEL: sil [ossa] @$s4main18emptyTypeNoArgInityyF : $@convention(thin) () -> () -// CHECK: [[AS:%.*]] = alloc_stack $EmptyStruct -// CHECK: [[FN:%.*]] = function_ref @{{_ZN11EmptyStructC1Ev|\?\?0EmptyStruct@@QEAA@XZ}} : $@convention(c) () -> @out EmptyStruct -// CHECK: apply [[FN]]([[AS]]) : $@convention(c) () -> @out EmptyStruct -// CHECK-LABEL: end sil function '$s4main18emptyTypeNoArgInityyF' - -// CHECL-LABEL: sil [clang EmptyStruct.init] @{{_ZN11EmptyStructC1Ev|\?\?0EmptyStruct@@QEAA@XZ}} : $@convention(c) () -> @out EmptyStruct -public func emptyTypeNoArgInit() { - let e = EmptyStruct() -} - -// CHECK-LABEL: sil [ossa] @$s4main25singleMemberTypeNoArgInityyF : $@convention(thin) () -> () -// CHECK: [[AS:%.*]] = alloc_stack $IntBox -// CHECK: [[FN:%.*]] = function_ref @{{_ZN6IntBoxC1Ev|\?\?0IntBox@@QEAA@XZ}} : $@convention(c) () -> @out IntBox -// CHECK: apply [[FN]]([[AS]]) : $@convention(c) () -> @out IntBox -// CHECK-LABEL: end sil function '$s4main25singleMemberTypeNoArgInityyF' - -//CHECK-LABEL: sil [clang IntBox.init] @{{_ZN6IntBoxC1Ev|\?\?0IntBox@@QEAA@XZ}} : $@convention(c) () -> @out IntBox -public func singleMemberTypeNoArgInit() { - let i = IntBox() -} - -// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo6IntBoxV1xABs5Int32V_tcfC : $@convention(method) (Int32, @thin IntBox.Type) -> IntBox -// CHECK: bb0([[I:%[0-9]+]] : $Int32, %{{[0-9]+}} : $@thin IntBox.Type): -// CHECK-NEXT: [[S:%.*]] = struct $IntBox ([[I]] : $Int32) -// CHECK-NEXT: return [[S]] -// CHECK-LABEL: end sil function '$sSo6IntBoxV1xABs5Int32V_tcfC' -public func singleMemberTypeValueInit() { - let i = IntBox(x: 42) -} - -// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo25DefaultConstructorDeletedV1aABSpys5Int32VG_tcfC : $@convention(method) (UnsafeMutablePointer, @thin DefaultConstructorDeleted.Type) -> DefaultConstructorDeleted -// CHECK: bb0([[A:%.*]] : $UnsafeMutablePointer -// CHECK-NEXT: [[OUT:%.*]] = struct $DefaultConstructorDeleted ([[A]] : $UnsafeMutablePointer) -// CHECK-NEXT: return [[OUT]] : $DefaultConstructorDeleted -// CHECK-LABEL: end sil function '$sSo25DefaultConstructorDeletedV1aABSpys5Int32VG_tcfC' -public func deletedConstructor(a: UnsafeMutablePointer) { - let deletedExplicitly = DefaultConstructorDeleted(a: a) -} From 18777bfa94a2eece5ab8e935d5bc539ad3483a24 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Thu, 15 Oct 2020 11:34:31 -0700 Subject: [PATCH 481/745] LoadableByAddress: Make sure that indirect return arguments are at the right type expansion rdar://70220886 --- lib/IRGen/LoadableByAddress.cpp | 3 ++- test/IRGen/big_types_generic.swift | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/IRGen/LoadableByAddress.cpp b/lib/IRGen/LoadableByAddress.cpp index 2a509cbfeeb89..1d74b82059c7a 100644 --- a/lib/IRGen/LoadableByAddress.cpp +++ b/lib/IRGen/LoadableByAddress.cpp @@ -1407,7 +1407,8 @@ void LoadableStorageAllocation::insertIndirectReturnArgs() { canType = genEnv->mapTypeIntoContext(canType)->getCanonicalType(); } resultStorageType = SILType::getPrimitiveObjectType(canType); - auto newResultStorageType = pass.getNewSILType(loweredTy, resultStorageType); + auto newResultStorageType = + pass.F->getLoweredType(pass.getNewSILType(loweredTy, resultStorageType)); auto &ctx = pass.F->getModule().getASTContext(); auto var = new (ctx) ParamDecl( diff --git a/test/IRGen/big_types_generic.swift b/test/IRGen/big_types_generic.swift index d1c648fd3e465..3d3b9194d19f6 100644 --- a/test/IRGen/big_types_generic.swift +++ b/test/IRGen/big_types_generic.swift @@ -67,3 +67,29 @@ func useStuff() { print(generic2(1).0) print(generic2(1).1) } + + +public struct BigThing { + var x: (Int64, Int64, Int64, Int64) = (0, 0, 0, 0) + var y: (Int64, Int64, Int64, Int64) = (0, 0, 0, 0) + var z: (Int64, Int64, Int64, Int64) = (0, 0, 0, 0) +} + +public protocol P {} + +public protocol Assoc { + associatedtype A + func foo() -> A +} + +extension Int : P {} + +public struct DefineSome : Assoc { + public func foo() -> some P { + return 5 + } +} + +public func abiIndirect() -> BigThing { + return BigThing() +} From 2fc5cbdc14dfee3b27070d3f0c761e89f23b01d0 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Tue, 13 Oct 2020 20:20:30 -0700 Subject: [PATCH 482/745] stdlib: remove `swiftMSVCRT`, replace with `swiftCRT` on Windows This replaces swiftMSVCRT with swiftCRT. The big difference here is that the `visualc` module is no longer imported nor exported. The `visualc` module remains in use for a singular test wrt availability, but this should effectively remove the need for the `visualc` module. The difference between the MSVCRT and ucrt module was not well understood by most. MSVCRT provided ucrt AND visualc, combining pieces of the old MSVCRT and the newer ucrt. The ucrt module is what you really wanted most of the time, however, would need to use MSVCRT for the convenience aliases for type-generic math and the deprecated math constants. Unfortunately, we cannot shadow the `ucrt` module and create a Swift SDK overlay for ucrt as that seems to result in circular dependencies when processing the `_Concurrency` module. Although this makes using the C library easier for most people, it has a more important subtle change: it cleaves the dependency on visualc. This means that this enables use of Swift without Visual Studio for the singular purpose of providing 3 header files. Additionally, it removes the need for the installation of 2 of the 4 support files. This greatly simplifies the deployment process on Windows. --- stdlib/private/RuntimeUnittest/CMakeLists.txt | 2 +- stdlib/private/StdlibCollectionUnittest/CMakeLists.txt | 2 +- stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt | 2 +- stdlib/private/StdlibUnittest/CMakeLists.txt | 2 +- stdlib/private/StdlibUnittest/RaceTest.swift | 2 +- stdlib/private/StdlibUnittest/StdlibCoreExtras.swift | 2 +- stdlib/private/StdlibUnittest/StdlibUnittest.swift | 2 +- stdlib/private/StdlibUnittest/SymbolLookup.swift | 2 +- stdlib/private/SwiftPrivate/CMakeLists.txt | 2 +- stdlib/private/SwiftPrivate/IO.swift | 2 +- stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt | 2 +- stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift | 2 +- .../SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift | 2 +- stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt | 2 +- .../SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift | 2 +- stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift | 2 +- stdlib/private/SwiftReflectionTest/CMakeLists.txt | 2 +- stdlib/public/Concurrency/CMakeLists.txt | 2 +- stdlib/public/Differentiation/CMakeLists.txt | 2 +- stdlib/public/Differentiation/TgmathDerivatives.swift.gyb | 2 +- stdlib/public/Platform/CMakeLists.txt | 4 ++-- stdlib/public/Platform/Platform.swift | 4 ---- stdlib/public/Platform/{msvcrt.swift => ucrt.swift} | 1 - test/AutoDiff/stdlib/tgmath_derivatives.swift.gyb | 2 +- test/AutoDiff/validation-test/custom_derivatives.swift | 2 +- test/AutoDiff/validation-test/separate_tangent_type.swift | 2 +- test/ClangImporter/availability_returns_twice-msvc.swift | 2 +- test/ClangImporter/clang_builtins.swift | 2 +- test/IRGen/builtin_math.swift | 2 +- test/IRGen/sanitize_coverage.swift | 2 +- test/Interpreter/SDK/libc.swift | 2 +- test/Interpreter/dynamicReplacement_property_observer.swift | 2 +- test/Interpreter/dynamic_replacement.swift | 2 +- test/Interpreter/dynamic_replacement_chaining.swift | 2 +- .../dynamic_replacement_without_previous_calls.swift | 2 +- test/Prototypes/BigInt.swift | 2 +- test/Sanitizers/tsan.swift | 2 +- test/stdlib/FloatConstants.swift | 2 +- test/stdlib/MathConstants.swift | 2 +- test/stdlib/PrintFloat.swift.gyb | 2 +- test/stdlib/Runtime.swift.gyb | 2 +- test/stdlib/VarArgs.swift | 2 +- test/stdlib/tgmath.swift.gyb | 2 +- test/stdlib/tgmath_optimized.swift | 2 +- 44 files changed, 43 insertions(+), 48 deletions(-) rename stdlib/public/Platform/{msvcrt.swift => ucrt.swift} (99%) diff --git a/stdlib/private/RuntimeUnittest/CMakeLists.txt b/stdlib/private/RuntimeUnittest/CMakeLists.txt index 3f042f605613a..538ff3c4cf2e5 100644 --- a/stdlib/private/RuntimeUnittest/CMakeLists.txt +++ b/stdlib/private/RuntimeUnittest/CMakeLists.txt @@ -13,7 +13,7 @@ add_swift_target_library(swiftRuntimeUnittest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT + SWIFT_MODULE_DEPENDS_WINDOWS CRT SWIFT_COMPILE_FLAGS ${swift_stdlib_unittest_compile_flags} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/StdlibCollectionUnittest/CMakeLists.txt b/stdlib/private/StdlibCollectionUnittest/CMakeLists.txt index 0efaf5ab37f0b..f0eb6fe467cff 100644 --- a/stdlib/private/StdlibCollectionUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibCollectionUnittest/CMakeLists.txt @@ -24,7 +24,7 @@ add_swift_target_library(swiftStdlibCollectionUnittest ${SWIFT_STDLIB_LIBRARY_BU SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT + SWIFT_MODULE_DEPENDS_WINDOWS CRT SWIFT_COMPILE_FLAGS ${swift_stdlib_unittest_compile_flags} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt b/stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt index 20aaa0a81bfa8..979cc9dfccb69 100644 --- a/stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt @@ -12,7 +12,7 @@ add_swift_target_library(swiftStdlibUnicodeUnittest ${SWIFT_STDLIB_LIBRARY_BUILD SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT + SWIFT_MODULE_DEPENDS_WINDOWS CRT SWIFT_COMPILE_FLAGS ${swift_stdlib_unittest_compile_flags} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/StdlibUnittest/CMakeLists.txt b/stdlib/private/StdlibUnittest/CMakeLists.txt index d1f61270a89b6..634d34b7198ed 100644 --- a/stdlib/private/StdlibUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibUnittest/CMakeLists.txt @@ -41,7 +41,7 @@ add_swift_target_library(swiftStdlibUnittest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK + SWIFT_MODULE_DEPENDS_WINDOWS CRT WinSDK SWIFT_COMPILE_FLAGS ${swift_stdlib_unittest_compile_flags} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/StdlibUnittest/RaceTest.swift b/stdlib/private/StdlibUnittest/RaceTest.swift index ef25992e8c54d..58b415965b6d8 100644 --- a/stdlib/private/StdlibUnittest/RaceTest.swift +++ b/stdlib/private/StdlibUnittest/RaceTest.swift @@ -44,7 +44,7 @@ import Darwin #elseif canImport(Glibc) import Glibc #elseif os(Windows) -import MSVCRT +import CRT import WinSDK #endif diff --git a/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift b/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift index a734f9fc0e047..b76c1140f6d7b 100644 --- a/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift +++ b/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift @@ -17,7 +17,7 @@ import Darwin #elseif canImport(Glibc) import Glibc #elseif os(Windows) -import MSVCRT +import CRT #endif #if _runtime(_ObjC) diff --git a/stdlib/private/StdlibUnittest/StdlibUnittest.swift b/stdlib/private/StdlibUnittest/StdlibUnittest.swift index f8841795eee92..4a1f89ddec2c1 100644 --- a/stdlib/private/StdlibUnittest/StdlibUnittest.swift +++ b/stdlib/private/StdlibUnittest/StdlibUnittest.swift @@ -21,7 +21,7 @@ import Darwin #elseif canImport(Glibc) import Glibc #elseif os(Windows) -import MSVCRT +import CRT import WinSDK #endif diff --git a/stdlib/private/StdlibUnittest/SymbolLookup.swift b/stdlib/private/StdlibUnittest/SymbolLookup.swift index 2d99cc4f6f3da..5cab6a1ac5c1b 100644 --- a/stdlib/private/StdlibUnittest/SymbolLookup.swift +++ b/stdlib/private/StdlibUnittest/SymbolLookup.swift @@ -15,7 +15,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT import WinSDK #else #error("Unsupported platform") diff --git a/stdlib/private/SwiftPrivate/CMakeLists.txt b/stdlib/private/SwiftPrivate/CMakeLists.txt index e0ab051ba48a2..c68670bce1814 100644 --- a/stdlib/private/SwiftPrivate/CMakeLists.txt +++ b/stdlib/private/SwiftPrivate/CMakeLists.txt @@ -15,7 +15,7 @@ add_swift_target_library(swiftSwiftPrivate ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I GYB_SOURCES AtomicInt.swift.gyb - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK + SWIFT_MODULE_DEPENDS_WINDOWS CRT WinSDK SWIFT_COMPILE_FLAGS ${swift_swiftprivate_compile_flags} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/SwiftPrivate/IO.swift b/stdlib/private/SwiftPrivate/IO.swift index 152ae9a0bc1a8..f973261d4c920 100644 --- a/stdlib/private/SwiftPrivate/IO.swift +++ b/stdlib/private/SwiftPrivate/IO.swift @@ -14,7 +14,7 @@ import Swift import SwiftShims #if os(Windows) -import MSVCRT +import CRT import WinSDK #endif diff --git a/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt b/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt index b923d3f1d5e99..887b4bab6f64f 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt +++ b/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt @@ -18,6 +18,6 @@ add_swift_target_library(swiftSwiftPrivateLibcExtras ${SWIFT_STDLIB_LIBRARY_BUIL SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK + SWIFT_MODULE_DEPENDS_WINDOWS CRT WinSDK INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift index 0b9aeb8466b65..e28c46872ff42 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift +++ b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift @@ -16,7 +16,7 @@ import Darwin #elseif canImport(Glibc) import Glibc #elseif os(Windows) -import MSVCRT +import CRT import WinSDK #endif diff --git a/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift b/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift index d9dd93277e471..b5e76d6be64f8 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift +++ b/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift @@ -16,7 +16,7 @@ import Darwin #elseif canImport(Glibc) import Glibc #elseif os(Windows) -import MSVCRT +import CRT #endif public func _stdlib_mkstemps(_ template: inout String, _ suffixlen: CInt) -> CInt { diff --git a/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt b/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt index 76487eb500314..0c9fefb71d6b7 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt +++ b/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt @@ -15,7 +15,7 @@ add_swift_target_library(swiftSwiftPrivateThreadExtras ${SWIFT_STDLIB_LIBRARY_BU SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK + SWIFT_MODULE_DEPENDS_WINDOWS CRT WinSDK SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} TARGET_SDKS ALL_APPLE_PLATFORMS CYGWIN FREEBSD OPENBSD HAIKU LINUX WINDOWS ANDROID INSTALL_IN_COMPONENT stdlib-experimental diff --git a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift index 9b044cbd55457..fa802a44cc49c 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift @@ -20,7 +20,7 @@ import Darwin #elseif canImport(Glibc) import Glibc #elseif os(Windows) -import MSVCRT +import CRT import WinSDK #endif diff --git a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift index 73b989377c578..4138ddc7bfa84 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift @@ -15,7 +15,7 @@ import Darwin #elseif canImport(Glibc) import Glibc #elseif os(Windows) -import MSVCRT +import CRT import WinSDK #endif diff --git a/stdlib/private/SwiftReflectionTest/CMakeLists.txt b/stdlib/private/SwiftReflectionTest/CMakeLists.txt index 7c33850c96a2c..f07e50f13e2b6 100644 --- a/stdlib/private/SwiftReflectionTest/CMakeLists.txt +++ b/stdlib/private/SwiftReflectionTest/CMakeLists.txt @@ -9,7 +9,7 @@ if (SWIFT_INCLUDE_TESTS) SWIFT_MODULE_DEPENDS_TVOS Darwin SWIFT_MODULE_DEPENDS_WATCHOS Darwin SWIFT_MODULE_DEPENDS_LINUX Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT + SWIFT_MODULE_DEPENDS_WINDOWS CRT INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index c7addf55947ec..32541e85a27cd 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -25,7 +25,7 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT + SWIFT_MODULE_DEPENDS_WINDOWS CRT LINK_LIBRARIES swiftCore diff --git a/stdlib/public/Differentiation/CMakeLists.txt b/stdlib/public/Differentiation/CMakeLists.txt index ecd1b8637f3fd..48ab091a260c2 100644 --- a/stdlib/public/Differentiation/CMakeLists.txt +++ b/stdlib/public/Differentiation/CMakeLists.txt @@ -32,7 +32,7 @@ add_swift_target_library(swift_Differentiation ${SWIFT_STDLIB_LIBRARY_BUILD_TYPE SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT + SWIFT_MODULE_DEPENDS_WINDOWS CRT SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} diff --git a/stdlib/public/Differentiation/TgmathDerivatives.swift.gyb b/stdlib/public/Differentiation/TgmathDerivatives.swift.gyb index 1a770907f5a55..d5ef6502aed4c 100644 --- a/stdlib/public/Differentiation/TgmathDerivatives.swift.gyb +++ b/stdlib/public/Differentiation/TgmathDerivatives.swift.gyb @@ -19,7 +19,7 @@ import Swift #elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif diff --git a/stdlib/public/Platform/CMakeLists.txt b/stdlib/public/Platform/CMakeLists.txt index ae972bc2e5cbb..0d7adcd3b3e42 100644 --- a/stdlib/public/Platform/CMakeLists.txt +++ b/stdlib/public/Platform/CMakeLists.txt @@ -48,8 +48,8 @@ add_swift_target_library(swiftGlibc ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_O INSTALL_IN_COMPONENT sdk-overlay DEPENDS glibc_modulemap) -add_swift_target_library(swiftMSVCRT ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY - msvcrt.swift +add_swift_target_library(swiftCRT ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY + ucrt.swift ${swift_platform_sources} POSIXError.swift diff --git a/stdlib/public/Platform/Platform.swift b/stdlib/public/Platform/Platform.swift index 5243b516379f9..eebde99006153 100644 --- a/stdlib/public/Platform/Platform.swift +++ b/stdlib/public/Platform/Platform.swift @@ -13,10 +13,6 @@ import SwiftShims import SwiftOverlayShims -#if os(Windows) -import ucrt -#endif - #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) //===----------------------------------------------------------------------===// // MacTypes.h diff --git a/stdlib/public/Platform/msvcrt.swift b/stdlib/public/Platform/ucrt.swift similarity index 99% rename from stdlib/public/Platform/msvcrt.swift rename to stdlib/public/Platform/ucrt.swift index 5030fc35061b7..80181db44d5cf 100644 --- a/stdlib/public/Platform/msvcrt.swift +++ b/stdlib/public/Platform/ucrt.swift @@ -11,7 +11,6 @@ //===----------------------------------------------------------------------===// @_exported import ucrt // Clang module -@_exported import visualc // Clang module @available(swift, deprecated: 3.0, message: "Please use 'Double.pi' or '.pi' to get the value of correct type and avoid casting.") public let M_PI = Double.pi diff --git a/test/AutoDiff/stdlib/tgmath_derivatives.swift.gyb b/test/AutoDiff/stdlib/tgmath_derivatives.swift.gyb index 6a7573f51e4a4..b0699fdb6721e 100644 --- a/test/AutoDiff/stdlib/tgmath_derivatives.swift.gyb +++ b/test/AutoDiff/stdlib/tgmath_derivatives.swift.gyb @@ -6,7 +6,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif diff --git a/test/AutoDiff/validation-test/custom_derivatives.swift b/test/AutoDiff/validation-test/custom_derivatives.swift index bd3fda6f560a7..bb32e01a66ea6 100644 --- a/test/AutoDiff/validation-test/custom_derivatives.swift +++ b/test/AutoDiff/validation-test/custom_derivatives.swift @@ -7,7 +7,7 @@ import StdlibUnittest #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import ucrt + import CRT #else #error("Unsupported platform") #endif diff --git a/test/AutoDiff/validation-test/separate_tangent_type.swift b/test/AutoDiff/validation-test/separate_tangent_type.swift index a39a3d39c14a6..0d38d0ca753f4 100644 --- a/test/AutoDiff/validation-test/separate_tangent_type.swift +++ b/test/AutoDiff/validation-test/separate_tangent_type.swift @@ -7,7 +7,7 @@ import StdlibUnittest #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import ucrt + import CRT #else #error("Unsupported platform") #endif diff --git a/test/ClangImporter/availability_returns_twice-msvc.swift b/test/ClangImporter/availability_returns_twice-msvc.swift index 36bee9412138b..904ea8de42031 100644 --- a/test/ClangImporter/availability_returns_twice-msvc.swift +++ b/test/ClangImporter/availability_returns_twice-msvc.swift @@ -1,7 +1,7 @@ // RUN: %target-typecheck-verify-swift // REQUIRES: OS=windows-msvc -import MSVCRT +import visualc typealias JumpBuffer = _JBTYPE func test_unavailable_returns_twice_function() { diff --git a/test/ClangImporter/clang_builtins.swift b/test/ClangImporter/clang_builtins.swift index 07557df68b723..50d05084d2403 100644 --- a/test/ClangImporter/clang_builtins.swift +++ b/test/ClangImporter/clang_builtins.swift @@ -5,7 +5,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif diff --git a/test/IRGen/builtin_math.swift b/test/IRGen/builtin_math.swift index 3602af70075cf..cb8eb439a154e 100644 --- a/test/IRGen/builtin_math.swift +++ b/test/IRGen/builtin_math.swift @@ -5,7 +5,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif diff --git a/test/IRGen/sanitize_coverage.swift b/test/IRGen/sanitize_coverage.swift index 3114e5a7c7040..b7444d2d1a57d 100644 --- a/test/IRGen/sanitize_coverage.swift +++ b/test/IRGen/sanitize_coverage.swift @@ -14,7 +14,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif diff --git a/test/Interpreter/SDK/libc.swift b/test/Interpreter/SDK/libc.swift index 9e91f9833befe..c68e1f5be7db2 100644 --- a/test/Interpreter/SDK/libc.swift +++ b/test/Interpreter/SDK/libc.swift @@ -14,7 +14,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT let S_IRUSR: Int32 = ucrt._S_IREAD let S_IWUSR: Int32 = 0 diff --git a/test/Interpreter/dynamicReplacement_property_observer.swift b/test/Interpreter/dynamicReplacement_property_observer.swift index ae1f435c81bcd..613f89b9c7181 100644 --- a/test/Interpreter/dynamicReplacement_property_observer.swift +++ b/test/Interpreter/dynamicReplacement_property_observer.swift @@ -16,7 +16,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT import WinSDK #else #error("Unsupported platform") diff --git a/test/Interpreter/dynamic_replacement.swift b/test/Interpreter/dynamic_replacement.swift index 8518de95e7c80..1e027dcc8fcc7 100644 --- a/test/Interpreter/dynamic_replacement.swift +++ b/test/Interpreter/dynamic_replacement.swift @@ -109,7 +109,7 @@ import StdlibUnittest #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT import WinSDK #else #error("Unsupported platform") diff --git a/test/Interpreter/dynamic_replacement_chaining.swift b/test/Interpreter/dynamic_replacement_chaining.swift index 421ed96996d0d..9a4c251c309e1 100644 --- a/test/Interpreter/dynamic_replacement_chaining.swift +++ b/test/Interpreter/dynamic_replacement_chaining.swift @@ -30,7 +30,7 @@ import StdlibUnittest #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT import WinSDK #else #error("Unsupported platform") diff --git a/test/Interpreter/dynamic_replacement_without_previous_calls.swift b/test/Interpreter/dynamic_replacement_without_previous_calls.swift index bc7301e816f60..dd5dadc617f9d 100644 --- a/test/Interpreter/dynamic_replacement_without_previous_calls.swift +++ b/test/Interpreter/dynamic_replacement_without_previous_calls.swift @@ -16,7 +16,7 @@ import StdlibUnittest #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT import WinSDK #else #error("Unsupported platform") diff --git a/test/Prototypes/BigInt.swift b/test/Prototypes/BigInt.swift index f6c96eaa7a045..2399f9fbef5fd 100644 --- a/test/Prototypes/BigInt.swift +++ b/test/Prototypes/BigInt.swift @@ -27,7 +27,7 @@ import StdlibUnittest #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif diff --git a/test/Sanitizers/tsan.swift b/test/Sanitizers/tsan.swift index 3fbc8dd5defe1..6b51cf07034a0 100644 --- a/test/Sanitizers/tsan.swift +++ b/test/Sanitizers/tsan.swift @@ -15,7 +15,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif diff --git a/test/stdlib/FloatConstants.swift b/test/stdlib/FloatConstants.swift index 24e3fea8dc6d0..3a3d4e98235f7 100644 --- a/test/stdlib/FloatConstants.swift +++ b/test/stdlib/FloatConstants.swift @@ -5,7 +5,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif diff --git a/test/stdlib/MathConstants.swift b/test/stdlib/MathConstants.swift index 61fb01b584645..3fdb5a7dcbc61 100644 --- a/test/stdlib/MathConstants.swift +++ b/test/stdlib/MathConstants.swift @@ -5,7 +5,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif diff --git a/test/stdlib/PrintFloat.swift.gyb b/test/stdlib/PrintFloat.swift.gyb index 2795a1c8a3473..ec24b7ba11113 100644 --- a/test/stdlib/PrintFloat.swift.gyb +++ b/test/stdlib/PrintFloat.swift.gyb @@ -15,7 +15,7 @@ import StdlibUnittest #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif diff --git a/test/stdlib/Runtime.swift.gyb b/test/stdlib/Runtime.swift.gyb index ae05de0b0b8c6..ca9e81d3e6a41 100644 --- a/test/stdlib/Runtime.swift.gyb +++ b/test/stdlib/Runtime.swift.gyb @@ -15,7 +15,7 @@ import SwiftShims #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT import WinSDK #else #error("Unsupported platform") diff --git a/test/stdlib/VarArgs.swift b/test/stdlib/VarArgs.swift index 90dff40f81e3a..feda99a9d95b5 100644 --- a/test/stdlib/VarArgs.swift +++ b/test/stdlib/VarArgs.swift @@ -10,7 +10,7 @@ import Swift import Glibc typealias CGFloat = Double #elseif os(Windows) - import MSVCRT + import CRT #if arch(x86_64) || arch(arm64) typealias CGFloat = Double #else diff --git a/test/stdlib/tgmath.swift.gyb b/test/stdlib/tgmath.swift.gyb index 3a1e526ab30f0..f4ad32701e42e 100644 --- a/test/stdlib/tgmath.swift.gyb +++ b/test/stdlib/tgmath.swift.gyb @@ -22,7 +22,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif diff --git a/test/stdlib/tgmath_optimized.swift b/test/stdlib/tgmath_optimized.swift index b1628b922e707..209e68277e915 100644 --- a/test/stdlib/tgmath_optimized.swift +++ b/test/stdlib/tgmath_optimized.swift @@ -9,7 +9,7 @@ #elseif canImport(Glibc) import Glibc #elseif os(Windows) - import MSVCRT + import CRT #else #error("Unsupported platform") #endif From 2a3f4f6665df9ca62381faf13b0d50e8b06acf7b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 15 Oct 2020 16:15:47 -0700 Subject: [PATCH 483/745] [AST] Allow resolution of the @_typeEraser type when printing. --- lib/AST/Attr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index fd9ecb13d9116..8920fd7c6c59c 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -986,8 +986,8 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, auto *attr = cast(this); if (auto *repr = attr->getParsedTypeEraserTypeRepr()) repr->print(Printer, Options); - else - attr->getTypeWithoutResolving()->print(Printer, Options); + else if (auto proto = dyn_cast(D)) + attr->getResolvedType(proto)->print(Printer, Options); Printer.printNamePost(PrintNameContext::Attribute); Printer << ")"; break; From 17f7ad82cac717a0be4e8417a136348fb0365ca9 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 1 Oct 2020 16:13:41 -0700 Subject: [PATCH 484/745] [CSBindings] Record all of the protocol requirements (not just literal) --- include/swift/Sema/ConstraintSystem.h | 2 +- lib/Sema/CSBindings.cpp | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 93da7c8bafa45..cb60ec3655fc2 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -4706,7 +4706,7 @@ class ConstraintSystem { SmallVector Bindings; /// The set of protocol requirements placed on this type variable. - llvm::TinyPtrVector Protocols; + llvm::SmallVector Protocols; /// The set of constraints which would be used to infer default types. llvm::TinyPtrVector Defaults; diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 16afaf1834f19..c8cf886771c60 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -986,8 +986,13 @@ bool ConstraintSystem::PotentialBindings::infer( break; case ConstraintKind::ConformsTo: - case ConstraintKind::SelfObjectOfProtocol: - return false; + case ConstraintKind::SelfObjectOfProtocol: { + auto protocolTy = constraint->getSecondType(); + if (!protocolTy->is()) + return false; + + LLVM_FALLTHROUGH; + } case ConstraintKind::LiteralConformsTo: { // Record constraint where protocol requirement originated From 0c51159228992019f029530844d7c482256b2ed2 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 1 Oct 2020 23:03:55 -0700 Subject: [PATCH 485/745] [CSBindings] Record constraint associated with a subtype relationship While inferring bindings, let's record not only the fact that current type variable is a subtype of some other type variable but track constraint which establishes this relationship. --- include/swift/Sema/ConstraintSystem.h | 15 ++++++++++++--- lib/Sema/CSBindings.cpp | 25 ++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index cb60ec3655fc2..31a6421909e6c 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -4749,7 +4749,7 @@ class ConstraintSystem { /// chain of subtypes because binding inference algorithm can't, /// at the moment, determine bindings transitively through supertype /// type variables. - llvm::SmallPtrSet SubtypeOf; + llvm::SmallDenseMap SubtypeOf; PotentialBindings(TypeVariableType *typeVar) : TypeVar(typeVar), PotentiallyIncomplete(isGenericParameter()) {} @@ -4794,10 +4794,10 @@ class ConstraintSystem { // This is required because algorithm can't currently infer // bindings for subtype transitively through superclass ones. if (!(x.IsHole && y.IsHole)) { - if (x.SubtypeOf.count(y.TypeVar)) + if (x.isSubtypeOf(y.TypeVar)) return false; - if (y.SubtypeOf.count(x.TypeVar)) + if (y.isSubtypeOf(x.TypeVar)) return true; } @@ -4843,6 +4843,15 @@ class ConstraintSystem { return false; } + bool isSubtypeOf(TypeVariableType *typeVar) const { + auto result = SubtypeOf.find(typeVar); + if (result == SubtypeOf.end()) + return false; + + auto *constraint = result->second; + return constraint->getKind() == ConstraintKind::Subtype; + } + /// Check if this binding is favored over a disjunction e.g. /// if it has only concrete types or would resolve a closure. bool favoredOverDisjunction(Constraint *disjunction) const; diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index c8cf886771c60..80cdda22986c3 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -790,9 +790,28 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint( } } - if (constraint->getKind() == ConstraintKind::Subtype && - kind == AllowedBindingKind::Subtypes) { - result.SubtypeOf.insert(bindingTypeVar); + switch (constraint->getKind()) { + case ConstraintKind::Subtype: + case ConstraintKind::Conversion: + case ConstraintKind::ArgumentConversion: + case ConstraintKind::OperatorArgumentConversion: { + if (kind == AllowedBindingKind::Subtypes) { + result.SubtypeOf.insert({bindingTypeVar, constraint}); + } else { + // TODO: record this type variable as a `supertypeOf` + } + break; + } + + case ConstraintKind::Bind: + case ConstraintKind::BindParam: + case ConstraintKind::Equal: { + // TODO: record this type variable as being equal to other type variable. + break; + } + + default: + break; } return None; From a9cce605a4a4062366ce7c4ef2afedd055e79f2c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 2 Oct 2020 13:39:07 -0700 Subject: [PATCH 486/745] [CSBindings] Shrink binding "sources" to supertype and equivalence only Instead of recording all of the binding "sources" let's only record subtype, supertype and equivalence relationships which didn't materialize as bindings (because other side is a type variable). This is the only information necessary to infer transitive bindings and protocol requirements. --- include/swift/Sema/ConstraintSystem.h | 14 +++++------ lib/Sema/CSBindings.cpp | 36 +++++---------------------- 2 files changed, 12 insertions(+), 38 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 31a6421909e6c..99c160c3f2b93 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -4741,15 +4741,13 @@ class ConstraintSystem { /// Tracks the position of the last known supertype in the group. Optional lastSupertypeIndex; - /// A set of all constraints which contribute to pontential bindings. - llvm::SmallPtrSet Sources; - /// A set of all not-yet-resolved type variables this type variable - /// is a subtype of. This is used to determine ordering inside a - /// chain of subtypes because binding inference algorithm can't, - /// at the moment, determine bindings transitively through supertype - /// type variables. - llvm::SmallDenseMap SubtypeOf; + /// is a subtype of, supertype of or is equivalent to. This is used + /// to determine ordering inside of a chain of subtypes to help infer + /// transitive bindings and protocol requirements. + llvm::SmallMapVector SubtypeOf; + llvm::SmallMapVector SupertypeOf; + llvm::SmallMapVector EquivalentTo; PotentialBindings(TypeVariableType *typeVar) : TypeVar(typeVar), PotentiallyIncomplete(isGenericParameter()) {} diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 80cdda22986c3..940fed3b06d8f 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -29,29 +29,8 @@ void ConstraintSystem::PotentialBindings::inferTransitiveBindings( &inferredBindings) { using BindingKind = ConstraintSystem::AllowedBindingKind; - llvm::SmallVector conversions; - // First, let's collect all of the conversions associated - // with this type variable. - llvm::copy_if( - Sources, std::back_inserter(conversions), - [&](const Constraint *constraint) -> bool { - if (constraint->getKind() != ConstraintKind::Subtype && - constraint->getKind() != ConstraintKind::Conversion && - constraint->getKind() != ConstraintKind::ArgumentConversion && - constraint->getKind() != ConstraintKind::OperatorArgumentConversion) - return false; - - auto rhs = cs.simplifyType(constraint->getSecondType()); - return rhs->getAs() == TypeVar; - }); - - for (auto *constraint : conversions) { - auto *tv = - cs.simplifyType(constraint->getFirstType())->getAs(); - if (!tv || tv == TypeVar) - continue; - - auto relatedBindings = inferredBindings.find(tv); + for (const auto &entry : SupertypeOf) { + auto relatedBindings = inferredBindings.find(entry.first); if (relatedBindings == inferredBindings.end()) continue; @@ -89,7 +68,7 @@ void ConstraintSystem::PotentialBindings::inferTransitiveBindings( llvm::copy(bindings.Defaults, std::back_inserter(Defaults)); // TODO: We shouldn't need this in the future. - if (constraint->getKind() != ConstraintKind::Subtype) + if (entry.second->getKind() != ConstraintKind::Subtype) continue; for (auto &binding : bindings.Bindings) { @@ -670,10 +649,6 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint( auto *typeVar = result.TypeVar; - // Record constraint which contributes to the - // finding of potential bindings. - result.Sources.insert(constraint); - auto first = simplifyType(constraint->getFirstType()); auto second = simplifyType(constraint->getSecondType()); @@ -798,7 +773,8 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint( if (kind == AllowedBindingKind::Subtypes) { result.SubtypeOf.insert({bindingTypeVar, constraint}); } else { - // TODO: record this type variable as a `supertypeOf` + assert(kind == AllowedBindingKind::Supertypes); + result.SupertypeOf.insert({bindingTypeVar, constraint}); } break; } @@ -806,7 +782,7 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint( case ConstraintKind::Bind: case ConstraintKind::BindParam: case ConstraintKind::Equal: { - // TODO: record this type variable as being equal to other type variable. + result.EquivalentTo.insert({bindingTypeVar, constraint}); break; } From d011bf3d7d72adc8235d336a202241f8cda62971 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 2 Oct 2020 17:09:40 -0700 Subject: [PATCH 487/745] [CSBindings] Implement transtive protocol requirement inference Implements iterative protocol requirement inference through subtype, conversion and equivalence relationships. This algorithm doesn't depend on a type variable finalization order (which is currently the order of type variable introduction). If a given type variable doesn't yet have its transitive protocol requirements inferred, algorithm would use iterative depth-first walk through its supertypes and equivalences and incrementally infer transitive protocols for each type variable involved, transferring new information down the chain e.g. T1 T3 \ / T4 T5 \ / T2 Here `T1`, `T3` are supertypes of `T4`, `T4` and `T5` are supertypes of `T2`. Let's assume that algorithm starts at `T2` and none of the involved type variables have their protocol requirements inferred yet. First, it would consider supertypes of `T2` which are `T4` and `T5`, since `T5` is the last in the chain algorithm would transfer its direct protocol requirements to `T2`. `T4` has supertypes `T1` and `T3` - they transfer their direct protocol requirements to `T4` and `T4` transfers its direct and transitive (from `T1` and `T3`) protocol requirements to `T2`. At this point all the type variables in subtype chain have their transitive protocol requirements resolved and cached so they don't have to be re-inferred later. --- include/swift/Sema/ConstraintSystem.h | 17 ++++- lib/Sema/CSBindings.cpp | 89 ++++++++++++++++++++++++++- 2 files changed, 101 insertions(+), 5 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 99c160c3f2b93..e6db184b1bd29 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -4708,6 +4708,10 @@ class ConstraintSystem { /// The set of protocol requirements placed on this type variable. llvm::SmallVector Protocols; + /// The set of transitive protocol requirements inferred through + /// subtype/conversion/equivalence relations with other type variables. + Optional> TransitiveProtocols; + /// The set of constraints which would be used to infer default types. llvm::TinyPtrVector Defaults; @@ -4873,6 +4877,15 @@ class ConstraintSystem { ConstraintSystem::PotentialBindings> &inferredBindings); + /// Detect subtype, conversion or equivalence relationship + /// between two type variables and attempt to propagate protocol + /// requirements down the subtype or equivalence chain. + void inferTransitiveProtocolRequirements( + const ConstraintSystem &cs, + llvm::SmallDenseMap + &inferredBindings); + /// Infer bindings based on any protocol conformances that have default /// types. void inferDefaultTypes(ConstraintSystem &cs, @@ -4886,8 +4899,8 @@ class ConstraintSystem { /// Finalize binding computation for this type variable by /// inferring bindings from context e.g. transitive bindings. void finalize(ConstraintSystem &cs, - const llvm::SmallDenseMap + llvm::SmallDenseMap &inferredBindings); void dump(llvm::raw_ostream &out, diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 940fed3b06d8f..0bdf2c2e2a9ad 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -22,6 +22,90 @@ using namespace swift; using namespace constraints; +void ConstraintSystem::PotentialBindings::inferTransitiveProtocolRequirements( + const ConstraintSystem &cs, + llvm::SmallDenseMap + &inferredBindings) { + if (TransitiveProtocols) + return; + + llvm::SmallVector, 4> + workList; + llvm::SmallPtrSet visitedRelations; + + llvm::SmallDenseMap, 4> + protocols; + + auto addToWorkList = [&](TypeVariableType *parent, + TypeVariableType *typeVar) { + if (visitedRelations.insert(typeVar).second) + workList.push_back({parent, typeVar}); + }; + + auto propagateProtocolsTo = + [&protocols](TypeVariableType *dstVar, + const SmallVectorImpl &direct, + const SmallPtrSetImpl &transitive) { + auto &destination = protocols[dstVar]; + + for (auto *protocol : direct) + destination.insert(protocol); + + for (auto *protocol : transitive) + destination.insert(protocol); + }; + + addToWorkList(nullptr, TypeVar); + + do { + auto *currentVar = workList.back().second; + + auto cachedBindings = inferredBindings.find(currentVar); + if (cachedBindings == inferredBindings.end()) { + workList.pop_back(); + continue; + } + + auto &bindings = cachedBindings->getSecond(); + + // If current variable already has transitive protocol + // conformances inferred, there is no need to look deeper + // into subtype/equivalence chain. + if (bindings.TransitiveProtocols) { + TypeVariableType *parent = nullptr; + std::tie(parent, currentVar) = workList.pop_back_val(); + assert(parent); + propagateProtocolsTo(parent, bindings.Protocols, + *bindings.TransitiveProtocols); + continue; + } + + for (const auto &entry : bindings.SubtypeOf) + addToWorkList(currentVar, entry.first); + + for (const auto &entry : bindings.EquivalentTo) + addToWorkList(currentVar, entry.first); + + // More subtype/equivalences relations have been added. + if (workList.back().second != currentVar) + continue; + + TypeVariableType *parent = nullptr; + std::tie(parent, currentVar) = workList.pop_back_val(); + + // At all of the protocols associated with current type variable + // are transitive to its parent, propogate them down the subtype/equivalence + // chain. + if (parent) { + propagateProtocolsTo(parent, bindings.Protocols, protocols[currentVar]); + } + + // Update the bindings associated with current type variable, + // to avoid repeating this inference process. + bindings.TransitiveProtocols.emplace(std::move(protocols[currentVar])); + } while (!workList.empty()); +} + void ConstraintSystem::PotentialBindings::inferTransitiveBindings( ConstraintSystem &cs, llvm::SmallPtrSetImpl &existingTypes, const llvm::SmallDenseMap + llvm::SmallDenseMap &inferredBindings) { // We need to make sure that there are no duplicate bindings in the // set, otherwise solver would produce multiple identical solutions. @@ -290,8 +373,8 @@ void ConstraintSystem::PotentialBindings::finalize( for (const auto &binding : Bindings) existingTypes.insert(binding.BindingType->getCanonicalType()); + inferTransitiveProtocolRequirements(cs, inferredBindings); inferTransitiveBindings(cs, existingTypes, inferredBindings); - inferDefaultTypes(cs, existingTypes); // Adjust optionality of existing bindings based on presence of From 5dc9919aff788fc71ab69603db997f0a45ca258e Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 14 Oct 2020 15:59:27 -0700 Subject: [PATCH 488/745] [ConstraintSystem] NFC: Make `SemaTest` a friend of `ConstraintSystem` This is necessary in order to have access to private members of a `ConstraintSystem` for testing purposes, such as logic related to potential binding computation. --- include/swift/Sema/ConstraintSystem.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index e6db184b1bd29..06e8c6635e56d 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -66,6 +66,12 @@ class SolutionApplicationTarget; } // end namespace constraints +namespace unittest { + +class SemaTest; + +} // end namespace unittest + // Forward declare some TypeChecker related functions // so they could be made friends of ConstraintSystem. namespace TypeChecker { @@ -2018,6 +2024,8 @@ enum class SolutionApplicationToFunctionResult { class ConstraintSystem { ASTContext &Context; + friend class swift::unittest::SemaTest; + public: DeclContext *DC; ConstraintSystemOptions Options; From 1aecea17e41bde3508326c9ae72c47b432ed5c15 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 14 Oct 2020 16:01:25 -0700 Subject: [PATCH 489/745] [unittest/Sema] Add a helper method to infer bindings for a given type variable This mimics what `determineBestBindings` does but without sorting. --- unittests/Sema/SemaFixture.cpp | 25 +++++++++++++++++++++++++ unittests/Sema/SemaFixture.h | 8 +++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/unittests/Sema/SemaFixture.cpp b/unittests/Sema/SemaFixture.cpp index d260b1227ca73..15abc54536453 100644 --- a/unittests/Sema/SemaFixture.cpp +++ b/unittests/Sema/SemaFixture.cpp @@ -22,6 +22,7 @@ #include "swift/ClangImporter/ClangImporter.h" #include "swift/Serialization/SerializedModuleLoader.h" #include "swift/Subsystems.h" +#include "llvm/ADT/DenseMap.h" using namespace swift; using namespace swift::unittest; @@ -74,3 +75,27 @@ Type SemaTest::getStdlibType(StringRef name) const { return Type(); } + +ConstraintSystem::PotentialBindings +SemaTest::inferBindings(ConstraintSystem &cs, TypeVariableType *typeVar) { + llvm::SmallDenseMap + cache; + + for (auto *typeVar : cs.getTypeVariables()) { + if (!typeVar->getImpl().hasRepresentativeOrFixed()) + cache.insert({typeVar, cs.inferBindingsFor(typeVar, /*finalize=*/false)}); + } + + for (auto *typeVar : cs.getTypeVariables()) { + auto cachedBindings = cache.find(typeVar); + if (cachedBindings == cache.end()) + continue; + + auto &bindings = cachedBindings->getSecond(); + bindings.finalize(cs, cache); + } + + auto result = cache.find(typeVar); + assert(result != cache.end()); + return result->second; +} diff --git a/unittests/Sema/SemaFixture.h b/unittests/Sema/SemaFixture.h index adb0ab40d5039..4956595596c40 100644 --- a/unittests/Sema/SemaFixture.h +++ b/unittests/Sema/SemaFixture.h @@ -18,13 +18,16 @@ #include "swift/Basic/LangOptions.h" #include "swift/Basic/Platform.h" #include "swift/Basic/SourceManager.h" -#include "llvm/ADT/StringRef.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/Host.h" #include "llvm/Support/Path.h" #include "gtest/gtest.h" #include +using namespace swift::constraints; + namespace swift { namespace unittest { @@ -62,6 +65,9 @@ class SemaTest : public SemaTestBase { protected: Type getStdlibType(StringRef name) const; + + static ConstraintSystem::PotentialBindings + inferBindings(ConstraintSystem &cs, TypeVariableType *typeVar); }; } // end namespace unittest From a24383112214c2a23a2008f5b704a0acb513b9fe Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 14 Oct 2020 16:15:58 -0700 Subject: [PATCH 490/745] [unittest/Sema] Add a test-case for a single hop protocol requirement inference In situations like: `$T0 $T1` `$T1 P` `$T0` should know about `P` as a transitive protocol requirement. --- unittests/Sema/BindingInferenceTests.cpp | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/unittests/Sema/BindingInferenceTests.cpp b/unittests/Sema/BindingInferenceTests.cpp index f4b71d921a28c..dbd08ee8cb5cf 100644 --- a/unittests/Sema/BindingInferenceTests.cpp +++ b/unittests/Sema/BindingInferenceTests.cpp @@ -13,6 +13,7 @@ #include "SemaFixture.h" #include "swift/AST/Expr.h" #include "swift/Sema/ConstraintSystem.h" +#include "llvm/ADT/SmallPtrSet.h" using namespace swift; using namespace swift::unittest; @@ -44,3 +45,44 @@ TEST_F(SemaTest, TestIntLiteralBindingInference) { ASSERT_TRUE(binding.BindingType->isEqual(getStdlibType("Int"))); ASSERT_TRUE(binding.hasDefaultedLiteralProtocol()); } + +TEST_F(SemaTest, TestTransitiveProtocolInference) { + ConstraintSystemOptions options; + ConstraintSystem cs(DC, options); + + auto *PD1 = + new (Context) ProtocolDecl(DC, SourceLoc(), SourceLoc(), + Context.getIdentifier("P1"), /*Inherited=*/{}, + /*trailingWhere=*/nullptr); + PD1->setImplicit(); + + auto *protocolTy1 = ProtocolType::get(PD1, Type(), Context); + + auto *GPT = cs.createTypeVariable(cs.getConstraintLocator({}), + /*options=*/TVO_CanBindToNoEscape); + + cs.addConstraint( + ConstraintKind::ConformsTo, GPT, protocolTy1, + cs.getConstraintLocator({}, LocatorPathElt::TypeParameterRequirement( + 0, RequirementKind::Conformance))); + + // First, let's try inferring through a single conversion + // relationship. + { + auto *typeVar = cs.createTypeVariable(cs.getConstraintLocator({}), + /*options=*/0); + + cs.addConstraint( + ConstraintKind::Conversion, typeVar, GPT, + cs.getConstraintLocator({}, LocatorPathElt::ContextualType())); + + auto bindings = inferBindings(cs, typeVar); + ASSERT_TRUE(bindings.Protocols.empty()); + + const auto &inferredProtocols = bindings.TransitiveProtocols; + ASSERT_TRUE(bool(inferredProtocols)); + ASSERT_EQ(inferredProtocols->size(), (unsigned)1); + ASSERT_TRUE( + (*inferredProtocols->begin())->getSecondType()->isEqual(protocolTy1)); + } +} From 8024b7bef3a3907299656eab5c0dc9ac844a5d05 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 15 Oct 2020 14:38:10 -0700 Subject: [PATCH 491/745] [CSBindings] Improve equivalence class handling in transitive protocol inference Let all of the members of the equivalence class be represented by the first type variable encountered during the depth-first walk. This means that supertypes are inferred from the members and all of the transitive protocol requirements are distributed among the members upon return back to the "representative". --- lib/Sema/CSBindings.cpp | 53 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 0bdf2c2e2a9ad..0c107fed76ef5 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -83,8 +83,28 @@ void ConstraintSystem::PotentialBindings::inferTransitiveProtocolRequirements( for (const auto &entry : bindings.SubtypeOf) addToWorkList(currentVar, entry.first); - for (const auto &entry : bindings.EquivalentTo) - addToWorkList(currentVar, entry.first); + // If current type variable is part of an equivalence + // class, make it a "representative" and let's it infer + // supertypes and direct protocol requirements from + // other members. + for (const auto &entry : bindings.EquivalentTo) { + auto eqBindings = inferredBindings.find(entry.first); + if (eqBindings != inferredBindings.end()) { + const auto &bindings = eqBindings->getSecond(); + + llvm::SmallPtrSet placeholder; + // Add any direct protocols from members of the + // equivalence class, so they could be propagated + // to all of the members. + propagateProtocolsTo(currentVar, bindings.Protocols, placeholder); + + // Since type variables are equal, current type variable + // becomes a subtype to any supertype found in the current + // equivalence class. + for (const auto &eqEntry : bindings.SubtypeOf) + addToWorkList(currentVar, eqEntry.first); + } + } // More subtype/equivalences relations have been added. if (workList.back().second != currentVar) @@ -100,9 +120,36 @@ void ConstraintSystem::PotentialBindings::inferTransitiveProtocolRequirements( propagateProtocolsTo(parent, bindings.Protocols, protocols[currentVar]); } + auto inferredProtocols = std::move(protocols[currentVar]); + + llvm::SmallPtrSet protocolsForEquivalence; + + // Equivalence class should contain both: + // - direct protocol requirements of the current type + // variable; + // - all of the transitive protocols inferred through + // the members of the equivalence class. + { + protocolsForEquivalence.insert(bindings.Protocols.begin(), + bindings.Protocols.end()); + + protocolsForEquivalence.insert(inferredProtocols.begin(), + inferredProtocols.end()); + } + + // Propogate inferred protocols to all of the members of the + // equivalence class. + for (const auto &equivalence : bindings.EquivalentTo) { + auto eqBindings = inferredBindings.find(equivalence.first); + if (eqBindings != inferredBindings.end()) { + auto &bindings = eqBindings->getSecond(); + bindings.TransitiveProtocols.emplace(protocolsForEquivalence); + } + } + // Update the bindings associated with current type variable, // to avoid repeating this inference process. - bindings.TransitiveProtocols.emplace(std::move(protocols[currentVar])); + bindings.TransitiveProtocols.emplace(std::move(inferredProtocols)); } while (!workList.empty()); } From a3c3981a68f9672e48823fda0661c06df5c9fbc3 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 15 Oct 2020 15:13:00 -0700 Subject: [PATCH 492/745] [unittest/Sema] NFC: Add a way to create a protocol with a given name and parent type --- unittests/Sema/SemaFixture.cpp | 11 +++++++++++ unittests/Sema/SemaFixture.h | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/unittests/Sema/SemaFixture.cpp b/unittests/Sema/SemaFixture.cpp index 15abc54536453..94bf7437d3fe6 100644 --- a/unittests/Sema/SemaFixture.cpp +++ b/unittests/Sema/SemaFixture.cpp @@ -76,6 +76,17 @@ Type SemaTest::getStdlibType(StringRef name) const { return Type(); } +ProtocolType *SemaTest::createProtocol(llvm::StringRef protocolName, + Type parent) { + auto *PD = new (Context) + ProtocolDecl(DC, SourceLoc(), SourceLoc(), + Context.getIdentifier(protocolName), /*Inherited=*/{}, + /*trailingWhere=*/nullptr); + PD->setImplicit(); + + return ProtocolType::get(PD, parent, Context); +} + ConstraintSystem::PotentialBindings SemaTest::inferBindings(ConstraintSystem &cs, TypeVariableType *typeVar) { llvm::SmallDenseMap diff --git a/unittests/Sema/SemaFixture.h b/unittests/Sema/SemaFixture.h index 4956595596c40..88741394e025e 100644 --- a/unittests/Sema/SemaFixture.h +++ b/unittests/Sema/SemaFixture.h @@ -15,6 +15,7 @@ #include "swift/AST/Module.h" #include "swift/AST/SourceFile.h" #include "swift/AST/Type.h" +#include "swift/AST/Types.h" #include "swift/Basic/LangOptions.h" #include "swift/Basic/Platform.h" #include "swift/Basic/SourceManager.h" @@ -66,6 +67,9 @@ class SemaTest : public SemaTestBase { protected: Type getStdlibType(StringRef name) const; + ProtocolType *createProtocol(llvm::StringRef protocolName, + Type parent = Type()); + static ConstraintSystem::PotentialBindings inferBindings(ConstraintSystem &cs, TypeVariableType *typeVar); }; From 9598f1984833a76aaa38cdb080bf997e28dacd1b Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 15 Oct 2020 16:27:10 -0700 Subject: [PATCH 493/745] [unittest/Sema] Cover transitive protocol inference with unit tests --- unittests/Sema/BindingInferenceTests.cpp | 146 ++++++++++++++++++++--- 1 file changed, 131 insertions(+), 15 deletions(-) diff --git a/unittests/Sema/BindingInferenceTests.cpp b/unittests/Sema/BindingInferenceTests.cpp index dbd08ee8cb5cf..4f94d2a6fa03b 100644 --- a/unittests/Sema/BindingInferenceTests.cpp +++ b/unittests/Sema/BindingInferenceTests.cpp @@ -46,23 +46,41 @@ TEST_F(SemaTest, TestIntLiteralBindingInference) { ASSERT_TRUE(binding.hasDefaultedLiteralProtocol()); } +// Given a set of inferred protocol requirements, make sure that +// all of the expected types are present. +static void verifyProtocolInferenceResults( + const llvm::SmallPtrSetImpl &protocols, + ArrayRef expectedTypes) { + ASSERT_TRUE(protocols.size() >= expectedTypes.size()); + + llvm::SmallPtrSet inferredProtocolTypes; + for (auto *protocol : protocols) + inferredProtocolTypes.insert(protocol->getSecondType()); + + for (auto expectedTy : expectedTypes) { + ASSERT_TRUE(inferredProtocolTypes.count(expectedTy)); + } +} + TEST_F(SemaTest, TestTransitiveProtocolInference) { ConstraintSystemOptions options; ConstraintSystem cs(DC, options); - auto *PD1 = - new (Context) ProtocolDecl(DC, SourceLoc(), SourceLoc(), - Context.getIdentifier("P1"), /*Inherited=*/{}, - /*trailingWhere=*/nullptr); - PD1->setImplicit(); + auto *protocolTy1 = createProtocol("P1"); + auto *protocolTy2 = createProtocol("P2"); - auto *protocolTy1 = ProtocolType::get(PD1, Type(), Context); + auto *GPT1 = cs.createTypeVariable(cs.getConstraintLocator({}), + /*options=*/TVO_CanBindToNoEscape); + auto *GPT2 = cs.createTypeVariable(cs.getConstraintLocator({}), + /*options=*/TVO_CanBindToNoEscape); - auto *GPT = cs.createTypeVariable(cs.getConstraintLocator({}), - /*options=*/TVO_CanBindToNoEscape); + cs.addConstraint( + ConstraintKind::ConformsTo, GPT1, protocolTy1, + cs.getConstraintLocator({}, LocatorPathElt::TypeParameterRequirement( + 0, RequirementKind::Conformance))); cs.addConstraint( - ConstraintKind::ConformsTo, GPT, protocolTy1, + ConstraintKind::ConformsTo, GPT2, protocolTy2, cs.getConstraintLocator({}, LocatorPathElt::TypeParameterRequirement( 0, RequirementKind::Conformance))); @@ -73,16 +91,114 @@ TEST_F(SemaTest, TestTransitiveProtocolInference) { /*options=*/0); cs.addConstraint( - ConstraintKind::Conversion, typeVar, GPT, + ConstraintKind::Conversion, typeVar, GPT1, cs.getConstraintLocator({}, LocatorPathElt::ContextualType())); auto bindings = inferBindings(cs, typeVar); ASSERT_TRUE(bindings.Protocols.empty()); + ASSERT_TRUE(bool(bindings.TransitiveProtocols)); + verifyProtocolInferenceResults(*bindings.TransitiveProtocols, + {protocolTy1}); + } + + // Now, let's make sure that protocol requirements could be propagated + // down conversion/equality chains through multiple hops. + { + // GPT1 is a subtype of GPT2 and GPT2 is convertible to a target type + // variable, target should get both protocols inferred - P1 & P2. - const auto &inferredProtocols = bindings.TransitiveProtocols; - ASSERT_TRUE(bool(inferredProtocols)); - ASSERT_EQ(inferredProtocols->size(), (unsigned)1); - ASSERT_TRUE( - (*inferredProtocols->begin())->getSecondType()->isEqual(protocolTy1)); + auto *typeVar = cs.createTypeVariable(cs.getConstraintLocator({}), + /*options=*/0); + + cs.addConstraint(ConstraintKind::Subtype, GPT1, GPT2, + cs.getConstraintLocator({})); + + cs.addConstraint(ConstraintKind::Conversion, typeVar, GPT1, + cs.getConstraintLocator({})); + + auto bindings = inferBindings(cs, typeVar); + ASSERT_TRUE(bindings.Protocols.empty()); + ASSERT_TRUE(bool(bindings.TransitiveProtocols)); + verifyProtocolInferenceResults(*bindings.TransitiveProtocols, + {protocolTy1, protocolTy2}); } } + +/// Let's try a more complicated situation where there protocols +/// are inferred from multiple sources on different levels of +/// convertion chain. +/// +/// (P1) T0 T4 (T3) T6 (P4) +/// \ / / +/// T3 = T1 (P2) = T5 +/// \ / +/// T2 + +TEST_F(SemaTest, TestComplexTransitiveProtocolInference) { + ConstraintSystemOptions options; + ConstraintSystem cs(DC, options); + + auto *protocolTy1 = createProtocol("P1"); + auto *protocolTy2 = createProtocol("P2"); + auto *protocolTy3 = createProtocol("P3"); + auto *protocolTy4 = createProtocol("P4"); + + auto *nilLocator = cs.getConstraintLocator({}); + + auto typeVar0 = cs.createTypeVariable(nilLocator, /*options=*/0); + auto typeVar1 = cs.createTypeVariable(nilLocator, /*options=*/0); + auto typeVar2 = cs.createTypeVariable(nilLocator, /*options=*/0); + // Allow this type variable to be bound to l-value type to prevent + // it from being merged with the rest of the type variables. + auto typeVar3 = + cs.createTypeVariable(nilLocator, /*options=*/TVO_CanBindToLValue); + auto typeVar4 = cs.createTypeVariable(nilLocator, /*options=*/0); + auto typeVar5 = + cs.createTypeVariable(nilLocator, /*options=*/TVO_CanBindToLValue); + auto typeVar6 = cs.createTypeVariable(nilLocator, /*options=*/0); + + cs.addConstraint(ConstraintKind::ConformsTo, typeVar0, protocolTy1, + nilLocator); + cs.addConstraint(ConstraintKind::ConformsTo, typeVar1, protocolTy2, + nilLocator); + cs.addConstraint(ConstraintKind::ConformsTo, typeVar4, protocolTy3, + nilLocator); + cs.addConstraint(ConstraintKind::ConformsTo, typeVar6, protocolTy4, + nilLocator); + + // T3 <: T0, T3 <: T4 + cs.addConstraint(ConstraintKind::Conversion, typeVar3, typeVar0, nilLocator); + cs.addConstraint(ConstraintKind::Conversion, typeVar3, typeVar4, nilLocator); + + // T2 <: T3, T2 <: T1, T3 == T1 + cs.addConstraint(ConstraintKind::Subtype, typeVar2, typeVar3, nilLocator); + cs.addConstraint(ConstraintKind::Conversion, typeVar2, typeVar1, nilLocator); + cs.addConstraint(ConstraintKind::Equal, typeVar3, typeVar1, nilLocator); + // T1 == T5, T <: T6 + cs.addConstraint(ConstraintKind::Equal, typeVar1, typeVar5, nilLocator); + cs.addConstraint(ConstraintKind::Conversion, typeVar5, typeVar6, nilLocator); + + auto bindingsForT1 = inferBindings(cs, typeVar1); + auto bindingsForT2 = inferBindings(cs, typeVar2); + auto bindingsForT3 = inferBindings(cs, typeVar3); + auto bindingsForT5 = inferBindings(cs, typeVar5); + + ASSERT_TRUE(bool(bindingsForT1.TransitiveProtocols)); + verifyProtocolInferenceResults(*bindingsForT1.TransitiveProtocols, + {protocolTy1, protocolTy3, protocolTy4}); + + ASSERT_TRUE(bool(bindingsForT2.TransitiveProtocols)); + verifyProtocolInferenceResults( + *bindingsForT2.TransitiveProtocols, + {protocolTy1, protocolTy2, protocolTy3, protocolTy4}); + + ASSERT_TRUE(bool(bindingsForT3.TransitiveProtocols)); + verifyProtocolInferenceResults( + *bindingsForT3.TransitiveProtocols, + {protocolTy1, protocolTy2, protocolTy3, protocolTy4}); + + ASSERT_TRUE(bool(bindingsForT5.TransitiveProtocols)); + verifyProtocolInferenceResults( + *bindingsForT5.TransitiveProtocols, + {protocolTy1, protocolTy2, protocolTy3, protocolTy4}); +} From 1888724166059af496287d0272c21468926a3350 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 15 Oct 2020 15:04:39 -0400 Subject: [PATCH 494/745] Sema: Disallow protocols from refining less available protocols Concrete types can conform to unavailable protocols because the witness table for the conformance is not required for use with the concrete type itself. However, protocols cannot have unavailable base protocols. I believe this was an oversight of the original implementation here. --- lib/Sema/TypeCheckAccess.cpp | 2 +- test/Sema/availability_versions.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 3ce68a68e315e..eff60ef4e3e76 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -2232,7 +2232,7 @@ class DeclAvailabilityChecker : public DeclVisitor { llvm::for_each(proto->getInherited(), [&](TypeLoc requirement) { checkType(requirement.getType(), requirement.getTypeRepr(), proto, - /*allowUnavailableProtocol=*/true); + /*allowUnavailableProtocol=*/false); }); if (proto->getTrailingWhereClause()) { diff --git a/test/Sema/availability_versions.swift b/test/Sema/availability_versions.swift index ad4159215becf..b8550d1ccfa5c 100644 --- a/test/Sema/availability_versions.swift +++ b/test/Sema/availability_versions.swift @@ -883,7 +883,7 @@ protocol ProtocolAvailableOn10_51 { } @available(OSX, introduced: 10.9) -protocol ProtocolAvailableOn10_9InheritingFromProtocolAvailableOn10_51 : ProtocolAvailableOn10_51 { +protocol ProtocolAvailableOn10_9InheritingFromProtocolAvailableOn10_51 : ProtocolAvailableOn10_51 { // expected-error {{'ProtocolAvailableOn10_51' is only available in macOS 10.51 or newer}} } @available(OSX, introduced: 10.51) From 8fd29a57cc8e829587596465ced7de6a88f7d289 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 15 Oct 2020 17:56:26 -0400 Subject: [PATCH 495/745] Sema: Add some missing checks for where clauses on non-generic declarations Access control, availability and exportability checking missed these because of a baked-in assumption that getGenericParams() == nullptr rules out the presence of a trailing where clause. --- lib/Sema/TypeCheckAccess.cpp | 91 ++++++++++--------- test/Sema/accessibility_where.swift | 9 ++ test/Sema/availability_versions.swift | 8 ++ .../implementation-only-import-in-decls.swift | 5 + test/attr/attr_usableFromInline.swift | 8 ++ 5 files changed, 80 insertions(+), 41 deletions(-) diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index eff60ef4e3e76..d1d1cdf79227b 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -323,11 +323,10 @@ void AccessControlCheckerBase::checkGenericParamAccess( const Decl *ownerDecl, AccessScope accessScope, AccessLevel contextAccess) { - auto params = ownerCtx->getGenericParams(); - if (!params) + if (!ownerCtx->isGenericContext()) return; - // This must stay in sync with diag::generic_param_access. + // This must stay in sync with diag::generic_param_access. enum class ACEK { Parameter = 0, Requirement @@ -355,20 +354,25 @@ void AccessControlCheckerBase::checkGenericParamAccess( auto *DC = ownerDecl->getDeclContext(); - for (auto param : *params) { - if (param->getInherited().empty()) - continue; - assert(param->getInherited().size() == 1); - checkTypeAccessImpl(param->getInherited().front().getType(), - param->getInherited().front().getTypeRepr(), - accessScope, DC, /*mayBeInferred*/false, - callback); + if (auto params = ownerCtx->getGenericParams()) { + for (auto param : *params) { + if (param->getInherited().empty()) + continue; + assert(param->getInherited().size() == 1); + checkTypeAccessImpl(param->getInherited().front().getType(), + param->getInherited().front().getTypeRepr(), + accessScope, DC, /*mayBeInferred*/false, + callback); + } } + callbackACEK = ACEK::Requirement; - checkRequirementAccess(WhereClauseOwner( - const_cast(ownerCtx)), - accessScope, DC, callback); + if (ownerCtx->getTrailingWhereClause()) { + checkRequirementAccess(WhereClauseOwner( + const_cast(ownerCtx)), + accessScope, DC, callback); + } if (minAccessScope.isPublic()) return; @@ -1624,23 +1628,26 @@ class ExportabilityChecker : public DeclVisitor { void checkGenericParams(const GenericContext *ownerCtx, const ValueDecl *ownerDecl) { - const auto params = ownerCtx->getGenericParams(); - if (!params) + if (!ownerCtx->isGenericContext()) return; - for (auto param : *params) { - if (param->getInherited().empty()) - continue; - assert(param->getInherited().size() == 1); - checkType(param->getInherited().front(), ownerDecl, - getDiagnoser(ownerDecl)); + if (auto params = ownerCtx->getGenericParams()) { + for (auto param : *params) { + if (param->getInherited().empty()) + continue; + assert(param->getInherited().size() == 1); + checkType(param->getInherited().front(), ownerDecl, + getDiagnoser(ownerDecl)); + } } - forAllRequirementTypes(WhereClauseOwner( - const_cast(ownerCtx)), - [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, ownerDecl, getDiagnoser(ownerDecl)); - }); + if (ownerCtx->getTrailingWhereClause()) { + forAllRequirementTypes(WhereClauseOwner( + const_cast(ownerCtx)), + [&](Type type, TypeRepr *typeRepr) { + checkType(type, typeRepr, ownerDecl, getDiagnoser(ownerDecl)); + }); + } } // This enum must be kept in sync with @@ -2076,24 +2083,26 @@ class DeclAvailabilityChecker : public DeclVisitor { void checkGenericParams(const GenericContext *ownerCtx, const ValueDecl *ownerDecl) { - // FIXME: What if we have a where clause and no generic params? - const auto params = ownerCtx->getGenericParams(); - if (!params) + if (!ownerCtx->isGenericContext()) return; - for (auto param : *params) { - if (param->getInherited().empty()) - continue; - assert(param->getInherited().size() == 1); - auto inherited = param->getInherited().front(); - checkType(inherited.getType(), inherited.getTypeRepr(), ownerDecl); + if (auto params = ownerCtx->getGenericParams()) { + for (auto param : *params) { + if (param->getInherited().empty()) + continue; + assert(param->getInherited().size() == 1); + auto inherited = param->getInherited().front(); + checkType(inherited.getType(), inherited.getTypeRepr(), ownerDecl); + } } - forAllRequirementTypes(WhereClauseOwner( - const_cast(ownerCtx)), - [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, ownerDecl); - }); + if (ownerCtx->getTrailingWhereClause()) { + forAllRequirementTypes(WhereClauseOwner( + const_cast(ownerCtx)), + [&](Type type, TypeRepr *typeRepr) { + checkType(type, typeRepr, ownerDecl); + }); + } } public: diff --git a/test/Sema/accessibility_where.swift b/test/Sema/accessibility_where.swift index 8144fcc860d87..cb1f6540ae1df 100644 --- a/test/Sema/accessibility_where.swift +++ b/test/Sema/accessibility_where.swift @@ -87,3 +87,12 @@ protocol Protocol7 : BaseProtocol where T == (PrivateStruct) -> Void { associatedtype X : BaseProtocol where X.T == (PrivateStruct) -> Void // expected-error@-1 {{associated type in an internal protocol uses a private type in its requirement}} } + +private protocol PrivateProtocol {} // expected-note 2{{type declared here}} + +struct GenericStruct { + struct Inner where T : PrivateProtocol {} + // expected-error@-1 {{struct must be declared private because its generic requirement uses a private type}} + func nonGenericWhereClause() where T : PrivateProtocol {} + // expected-error@-1 {{instance method must be declared private because its generic requirement uses a private type}} +} diff --git a/test/Sema/availability_versions.swift b/test/Sema/availability_versions.swift index b8550d1ccfa5c..b47c459a2489f 100644 --- a/test/Sema/availability_versions.swift +++ b/test/Sema/availability_versions.swift @@ -928,6 +928,14 @@ func GenericSignature(_ t: T) { // expected-error // expected-note@-1 * {{add @available attribute to enclosing global function}} } +struct GenericType { // expected-note {{add @available attribute to enclosing generic struct}} + func nonGenericWhereClause() where T : ProtocolAvailableOn10_51 {} // expected-error {{'ProtocolAvailableOn10_51' is only available in macOS 10.51 or newer}} + // expected-note@-1 {{add @available attribute to enclosing instance method}} + + struct NestedType where T : ProtocolAvailableOn10_51 {} // expected-error {{'ProtocolAvailableOn10_51' is only available in macOS 10.51 or newer}} + // expected-note@-1 2{{add @available attribute to enclosing struct}} +} + // Extensions extension ClassAvailableOn10_51 { } // expected-error {{'ClassAvailableOn10_51' is only available in macOS 10.51 or newer}} diff --git a/test/Sema/implementation-only-import-in-decls.swift b/test/Sema/implementation-only-import-in-decls.swift index 60372ddae6532..818a62facc47c 100644 --- a/test/Sema/implementation-only-import-in-decls.swift +++ b/test/Sema/implementation-only-import-in-decls.swift @@ -21,6 +21,11 @@ public struct TestGenericParams {} // expected-error {{cannot use p public struct TestGenericParamsWhereClause where T: BadProto {} // expected-error {{cannot use protocol 'BadProto' here; 'BADLibrary' has been imported as implementation-only}} +public struct TestGenericParamsWithOuter { + public func nonGenericWhereClause() where T : BadProto {} // expected-error {{cannot use protocol 'BadProto' here; 'BADLibrary' has been imported as implementation-only}} + public struct Inner where T : BadProto {} // expected-error {{cannot use protocol 'BadProto' here; 'BADLibrary' has been imported as implementation-only}} +} + public enum TestCase { case x(BadStruct) // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} case y(Int, BadStruct) // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} diff --git a/test/attr/attr_usableFromInline.swift b/test/attr/attr_usableFromInline.swift index 9673353e10c54..de0729324d131 100644 --- a/test/attr/attr_usableFromInline.swift +++ b/test/attr/attr_usableFromInline.swift @@ -155,3 +155,11 @@ public struct TestGenericSubscripts { @usableFromInline typealias TestGenericAlias = T // expected-warning {{type referenced from a generic parameter of a '@usableFromInline' type alias should be '@usableFromInline' or public}} @usableFromInline typealias TestGenericAliasWhereClause = T where T: InternalProtocol // expected-warning {{type referenced from a generic requirement of a '@usableFromInline' type alias should be '@usableFromInline' or public}} + +@usableFromInline struct GenericStruct { + @usableFromInline struct Nested where T : InternalProtocol {} + // expected-error@-1 {{type referenced from a generic requirement of a '@usableFromInline' struct must be '@usableFromInline' or public}} + + @usableFromInline func nonGenericWhereClause() where T : InternalProtocol {} + // expected-error@-1 {{type referenced from a generic requirement of a '@usableFromInline' instance method must be '@usableFromInline' or public}} +} \ No newline at end of file From 027563733ad68846cea15034032d9d182e8a1154 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 15 Oct 2020 17:59:49 -0400 Subject: [PATCH 496/745] AST: Disallow @available attributes on generic parameters This was a no-op and it doesn't make sense in the current model. --- include/swift/AST/Attr.def | 3 +-- test/attr/attr_availability.swift | 8 +++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index e174d143c82cc..20427b356c81b 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -120,8 +120,7 @@ DECL_ATTR(_silgen_name, SILGenName, 0) DECL_ATTR(available, Available, OnAbstractFunction | OnGenericType | OnVar | OnSubscript | OnEnumElement | - OnExtension | OnGenericTypeParam | - AllowMultipleAttributes | LongAttribute | + OnExtension | AllowMultipleAttributes | LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, 1) CONTEXTUAL_SIMPLE_DECL_ATTR(final, Final, diff --git a/test/attr/attr_availability.swift b/test/attr/attr_availability.swift index 5a498da129905..5d77f4a2a6354 100644 --- a/test/attr/attr_availability.swift +++ b/test/attr/attr_availability.swift @@ -257,11 +257,6 @@ func TextOutputStreamTest(message: String, to: inout TextOutputStream) { print(message, &to) // expected-error {{'print' is unavailable: Please use the 'to' label for the target stream: 'print((...), to: &...)'}} } -// expected-note@+1{{'T' has been explicitly marked unavailable here}} -struct UnavailableGenericParam<@available(*, unavailable, message: "nope") T> { - func f(t: T) { } // expected-error{{'T' is unavailable: nope}} -} - struct DummyType {} @@ -1119,3 +1114,6 @@ func testBadRename() { _ = BadRename(from: 5, to: 17) // expected-warning{{'init(from:to:step:)' is deprecated: replaced by 'init(range:step:)'}} // expected-note@-1{{use 'init(range:step:)' instead}} } + +struct AvailableGenericParam<@available(*, deprecated) T> {} +// expected-error@-1 {{'@available' attribute cannot be applied to this declaration}} From 007351223eb96a1a0132953a219b12d9b06ec0e7 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 14 Oct 2020 17:07:42 +0200 Subject: [PATCH 497/745] MemBehavior: handle begin_access when checking for apply side-effects. --- lib/SILOptimizer/Analysis/MemoryBehavior.cpp | 2 ++ test/SILOptimizer/mem-behavior.sil | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp index b867c18b6300a..bad7bc7ad72e0 100644 --- a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp +++ b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp @@ -364,6 +364,7 @@ static bool hasEscapingUses(SILValue address, int &numChecks) { case SILInstructionKind::CopyAddrInst: case SILInstructionKind::DestroyAddrInst: case SILInstructionKind::DeallocStackInst: + case SILInstructionKind::EndAccessInst: // Those instructions have no result and cannot escape the address. break; case SILInstructionKind::ApplyInst: @@ -373,6 +374,7 @@ static bool hasEscapingUses(SILValue address, int &numChecks) { // possible that an address, passed as an indirect parameter, escapes // the function in any way (which is not unsafe and undefined behavior). break; + case SILInstructionKind::BeginAccessInst: case SILInstructionKind::OpenExistentialAddrInst: case SILInstructionKind::UncheckedTakeEnumDataAddrInst: case SILInstructionKind::StructElementAddrInst: diff --git a/test/SILOptimizer/mem-behavior.sil b/test/SILOptimizer/mem-behavior.sil index c33643578c3c7..7cc35e4061631 100644 --- a/test/SILOptimizer/mem-behavior.sil +++ b/test/SILOptimizer/mem-behavior.sil @@ -269,7 +269,6 @@ bb2(%8 : $Error): unreachable } - // CHECK-LABEL: @beginapply_allocstack_and_copyaddr // CHECK: PAIR #0. // CHECK-NEXT: (%4, %5) = begin_apply %3(%0) : $@yield_once @convention(thin) (@in Int32) -> @yields Int32 @@ -324,6 +323,23 @@ bb0(%0 : $X): return %3 : $Int32 } +// CHECK-LABEL: @apply_and_begin_access +// CHECK: PAIR #0. +// CHECK-NEXT: %6 = apply %5(%1) : $@convention(thin) (@in Int32) -> Int32 +// CHECK-NEXT: %0 = argument of bb0 : $*Int32 +// CHECK-NEXT: r=0,w=0 +sil @apply_and_begin_access : $@convention(thin) (@in Int32) -> Int32 { +bb0(%0 : $*Int32): + %1 = alloc_stack $Int32 + %2 = begin_access [read] [static] %0 : $*Int32 + copy_addr %2 to %1 : $*Int32 + end_access %2 : $*Int32 + %5 = function_ref @single_indirect_arg : $@convention(thin) (@in Int32) -> Int32 + %6 = apply %5(%1) : $@convention(thin) (@in Int32) -> Int32 + dealloc_stack %1 : $*Int32 + return %6 : $Int32 +} + sil @load_from_in : $@convention(thin) (@in X) -> () { bb0(%0 : $*X): %1 = load %0 : $*X From 68db2e7c6ce6f2bf9cdce1add556ef69971bc6a1 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 14 Oct 2020 18:54:27 +0200 Subject: [PATCH 498/745] TempRValueOpt: don't use collectLoads in tryOptimizeStoreIntoTemp This refactoring removes a lot of special-casing in collectLoads and also makes tryOptimizeStoreIntoTemp simpler. It's a NFC. --- .../Transforms/TempRValueElimination.cpp | 101 +++++++----------- 1 file changed, 36 insertions(+), 65 deletions(-) diff --git a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp index 7c80311fd4fe1..fc9b472dc5a46 100644 --- a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp +++ b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp @@ -61,6 +61,17 @@ namespace { /// it finds cases in which it is easy to determine that the source is /// unmodified during the copy destination's lifetime. Thus, the destination can /// be viewed as a short-lived "rvalue". +/// +/// As a second optimization, also stores into temporaries are handled. This is +/// a simple form of redundant-load-elimination (RLE). +/// +/// %temp = alloc_stack $T +/// store %src to [initialization] %temp : $*T +/// // no writes to %temp +/// %v = load [take] %temp : $*T +/// dealloc_stack %temp : $*T +/// +/// TODO: Check if we still need to handle stores when RLE supports OSSA. class TempRValueOptPass : public SILFunctionTransform { AliasAnalysis *aa = nullptr; @@ -94,14 +105,6 @@ class TempRValueOptPass : public SILFunctionTransform { bool TempRValueOptPass::collectLoadsFromProjection( SingleValueInstruction *projection, SILValue srcAddr, SmallPtrSetImpl &loadInsts) { - if (!srcAddr) { - LLVM_DEBUG( - llvm::dbgs() - << " Temp has addr_projection use?! Can not yet promote to value" - << *projection); - return false; - } - // Transitively look through projections on stack addresses. for (auto *projUseOper : projection->getUses()) { auto *user = projUseOper->getUser(); @@ -127,28 +130,15 @@ bool TempRValueOptPass::canApplyBeTreatedAsLoad( return false; } - if (srcAddr) { - // If the function may write to the source of the copy_addr, the apply - // cannot be treated as a load: all (potential) writes of the source must - // appear _after_ all loads of the temporary. But in case of a function call - // we don't know in which order the writes and loads are executed inside the - // called function. The source may be written before the temporary is - // loaded, which would make the optization invalid. - if (aa->mayWriteToMemory(apply.getInstruction(), srcAddr)) - return false; - } else { - // If we do not have an src address, but are indirect, bail. We would need - // to perform function signature specialization to change the functions - // signature to pass something direct. - if (convention.isIndirectConvention()) { - LLVM_DEBUG( - llvm::dbgs() - << " Temp used to materialize value for indirect convention?! Can " - "not remove temporary without func sig opts" - << *apply.getInstruction()); - return false; - } - } + // If the function may write to the source of the copy_addr, the apply + // cannot be treated as a load: all (potential) writes of the source must + // appear _after_ all loads of the temporary. But in case of a function call + // we don't know in which order the writes and loads are executed inside the + // called function. The source may be written before the temporary is + // loaded, which would make the optization invalid. + if (aa->mayWriteToMemory(apply.getInstruction(), srcAddr)) + return false; + return true; } @@ -237,14 +227,6 @@ bool TempRValueOptPass::collectLoads( return true; } case SILInstructionKind::OpenExistentialAddrInst: { - // If we do not have an srcAddr, bail. We do not support promoting this yet. - if (!srcAddr) { - LLVM_DEBUG(llvm::dbgs() << " Temp has open_existential_addr use?! Can " - "not yet promote to value" - << *user); - return false; - } - // We only support open existential addr if the access is immutable. auto *oeai = cast(user); if (oeai->getAccessKind() != OpenedExistentialAccess::Immutable) { @@ -294,11 +276,6 @@ bool TempRValueOptPass::collectLoads( return true; case SILInstructionKind::LoadBorrowInst: - // If we do not have a source addr, we must be trying to eliminate a - // store. Until we check that the source object is not destroyed within the - // given range, we need bail. - if (!srcAddr) - return false; loadInsts.insert(user); return true; case SILInstructionKind::FixLifetimeInst: @@ -598,29 +575,21 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) { if (user == si) continue; - // Destroys and deallocations are allowed to be in a different block. - if (isa(user) || isa(user)) - continue; - - // Same for load [take] on the top level temp object. SILGen always takes - // whole values from temporaries. If we have load [take] on projections from - // our base, we fail since those would be re-initializations. - if (auto *li = dyn_cast(user)) { - if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) { - continue; - } - } - - // We pass in SILValue() since we do not have a source address. - if (!collectLoads(useOper, user, tempObj, SILValue(), loadInsts)) - return {std::next(si->getIterator()), false}; - // Bail if there is any kind of user which is not handled in the code below. switch (user->getKind()) { - case SILInstructionKind::CopyAddrInst: + case SILInstructionKind::DestroyAddrInst: + case SILInstructionKind::DeallocStackInst: + case SILInstructionKind::LoadInst: case SILInstructionKind::FixLifetimeInst: + break; + case SILInstructionKind::CopyAddrInst: + if (cast(user)->getDest() == tempObj) + return {std::next(si->getIterator()), false}; + break; case SILInstructionKind::MarkDependenceInst: - continue; + if (cast(user)->getValue() == tempObj) + return {std::next(si->getIterator()), false}; + break; default: return {std::next(si->getIterator()), false}; } @@ -690,11 +659,13 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) { break; } case SILInstructionKind::MarkDependenceInst: { - SILBuilderWithScope builder(user); auto mdi = cast(user); - auto newInst = builder.createMarkDependence(user->getLoc(), mdi->getValue(), si->getSrc()); + assert(mdi->getBase() == tempObj); + SILBuilderWithScope builder(user); + auto newInst = builder.createMarkDependence(user->getLoc(), + mdi->getValue(), si->getSrc()); mdi->replaceAllUsesWith(newInst); - toDelete.push_back(user); + toDelete.push_back(mdi); break; } // ASSUMPTION: no operations that may be handled by this default clause can From 673b8873ab2930c0497944411a28070877883591 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 14 Oct 2020 19:09:01 +0200 Subject: [PATCH 499/745] TempRValueOpt: refactoring: simplify the signature of collectLoads It's sufficient to pass the operand instead of the operand, the user and the operand value. NFC. --- .../Transforms/TempRValueElimination.cpp | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp index fc9b472dc5a46..6dc7f3e4d5114 100644 --- a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp +++ b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp @@ -75,11 +75,10 @@ namespace { class TempRValueOptPass : public SILFunctionTransform { AliasAnalysis *aa = nullptr; - bool collectLoads(Operand *userOp, SILInstruction *userInst, - SingleValueInstruction *addr, SILValue srcObject, + bool collectLoads(Operand *addressUse, CopyAddrInst *originalCopy, SmallPtrSetImpl &loadInsts); bool collectLoadsFromProjection(SingleValueInstruction *projection, - SILValue srcAddr, + CopyAddrInst *originalCopy, SmallPtrSetImpl &loadInsts); bool @@ -103,7 +102,7 @@ class TempRValueOptPass : public SILFunctionTransform { } // anonymous namespace bool TempRValueOptPass::collectLoadsFromProjection( - SingleValueInstruction *projection, SILValue srcAddr, + SingleValueInstruction *projection, CopyAddrInst *originalCopy, SmallPtrSetImpl &loadInsts) { // Transitively look through projections on stack addresses. for (auto *projUseOper : projection->getUses()) { @@ -111,7 +110,7 @@ bool TempRValueOptPass::collectLoadsFromProjection( if (user->isTypeDependentOperand(*projUseOper)) continue; - if (!collectLoads(projUseOper, user, projection, srcAddr, loadInsts)) + if (!collectLoads(projUseOper, originalCopy, loadInsts)) return false; } return true; @@ -155,12 +154,15 @@ bool TempRValueOptPass::canApplyBeTreatedAsLoad( /// location at \address. The temporary must be initialized by the original copy /// and never written to again. Therefore, collectLoads disallows any operation /// that may write to memory at \p address. -bool TempRValueOptPass::collectLoads( - Operand *userOp, SILInstruction *user, SingleValueInstruction *address, - SILValue srcAddr, SmallPtrSetImpl &loadInsts) { +bool TempRValueOptPass:: +collectLoads(Operand *addressUse, CopyAddrInst *originalCopy, + SmallPtrSetImpl &loadInsts) { + SILInstruction *user = addressUse->getUser(); + SILValue address = addressUse->get(); + // All normal uses (loads) must be in the initialization block. // (The destroy and dealloc are commonly in a different block though.) - SILBasicBlock *block = address->getParent(); + SILBasicBlock *block = originalCopy->getParent(); if (user->getParent() != block) return false; @@ -192,8 +194,7 @@ bool TempRValueOptPass::collectLoads( // If the user is the value operand of the MarkDependenceInst we have to // transitively explore its uses until we reach a load or return false for (auto *mdiUseOper : mdi->getUses()) { - if (!collectLoads(mdiUseOper, mdiUseOper->getUser(), mdi, srcAddr, - loadInsts)) + if (!collectLoads(mdiUseOper, originalCopy, loadInsts)) return false; } return true; @@ -204,14 +205,16 @@ bool TempRValueOptPass::collectLoads( LLVM_FALLTHROUGH; case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: { - if (!canApplyBeTreatedAsLoad(userOp, ApplySite(user), srcAddr)) + if (!canApplyBeTreatedAsLoad(addressUse, ApplySite(user), + originalCopy->getSrc())) return false; // Everything is okay with the function call. Register it as a "load". loadInsts.insert(user); return true; } case SILInstructionKind::BeginApplyInst: { - if (!canApplyBeTreatedAsLoad(userOp, ApplySite(user), srcAddr)) + if (!canApplyBeTreatedAsLoad(addressUse, ApplySite(user), + originalCopy->getSrc())) return false; auto beginApply = cast(user); @@ -235,7 +238,7 @@ bool TempRValueOptPass::collectLoads( << *user); return false; } - return collectLoadsFromProjection(oeai, srcAddr, loadInsts); + return collectLoadsFromProjection(oeai, originalCopy, loadInsts); } case SILInstructionKind::UncheckedTakeEnumDataAddrInst: { // In certain cases, unchecked_take_enum_data_addr invalidates the @@ -249,14 +252,13 @@ bool TempRValueOptPass::collectLoads( return false; } - return collectLoadsFromProjection(utedai, srcAddr, loadInsts); + return collectLoadsFromProjection(utedai, originalCopy, loadInsts); } case SILInstructionKind::StructElementAddrInst: case SILInstructionKind::TupleElementAddrInst: - case SILInstructionKind::UncheckedAddrCastInst: { + case SILInstructionKind::UncheckedAddrCastInst: return collectLoadsFromProjection(cast(user), - srcAddr, loadInsts); - } + originalCopy, loadInsts); case SILInstructionKind::LoadInst: // Loads are the end of the data flow chain. The users of the load can't // access the temporary storage. @@ -448,7 +450,7 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { } } - if (!collectLoads(useOper, user, tempObj, copySrc, loadInsts)) + if (!collectLoads(useOper, copyInst, loadInsts)) return false; } From d569031f1a367281871ba18c92f385ffa0cafa7d Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 14 Oct 2020 19:21:53 +0200 Subject: [PATCH 500/745] TempRValueOpt: move the mayWrite-check for applies from collectLoads to checkNoSourceModification ... where it belongs. This is mostly refactoring, but it also fixes a bug: we don't recurse into a begin_access in collectLoads. If there is an apply in such a scope, the mayWrite-check wouldn't be done. In checkNoSourceModification all instructions are visited, so the check is always done. --- .../Transforms/TempRValueElimination.cpp | 72 +++++++------------ 1 file changed, 24 insertions(+), 48 deletions(-) diff --git a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp index 6dc7f3e4d5114..a3f1a1ee5ae16 100644 --- a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp +++ b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp @@ -89,9 +89,6 @@ class TempRValueOptPass : public SILFunctionTransform { checkTempObjectDestroy(AllocStackInst *tempObj, CopyAddrInst *copyInst, ValueLifetimeAnalysis::Frontier &tempAddressFrontier); - bool canApplyBeTreatedAsLoad(Operand *tempObjUser, ApplySite apply, - SILValue srcAddr); - bool tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst); std::pair tryOptimizeStoreIntoTemp(StoreInst *si); @@ -116,31 +113,6 @@ bool TempRValueOptPass::collectLoadsFromProjection( return true; } -/// Check if \p tempObjUser, passed to the apply instruction, is only loaded, -/// but not modified and if \p srcAddr is not modified as well. -bool TempRValueOptPass::canApplyBeTreatedAsLoad( - Operand *tempObjUser, ApplySite apply, SILValue srcAddr) { - // Check if the function can just read from tempObjUser. - auto convention = apply.getArgumentConvention(*tempObjUser); - if (!convention.isGuaranteedConvention()) { - LLVM_DEBUG(llvm::dbgs() << " Temp consuming use may write/destroy " - "its source" - << *apply.getInstruction()); - return false; - } - - // If the function may write to the source of the copy_addr, the apply - // cannot be treated as a load: all (potential) writes of the source must - // appear _after_ all loads of the temporary. But in case of a function call - // we don't know in which order the writes and loads are executed inside the - // called function. The source may be written before the temporary is - // loaded, which would make the optization invalid. - if (aa->mayWriteToMemory(apply.getInstruction(), srcAddr)) - return false; - - return true; -} - /// Transitively explore all data flow uses of the given \p address until /// reaching a load or returning false. /// @@ -204,28 +176,23 @@ collectLoads(Operand *addressUse, CopyAddrInst *originalCopy, return false; LLVM_FALLTHROUGH; case SILInstructionKind::ApplyInst: - case SILInstructionKind::TryApplyInst: { - if (!canApplyBeTreatedAsLoad(addressUse, ApplySite(user), - originalCopy->getSrc())) - return false; - // Everything is okay with the function call. Register it as a "load". - loadInsts.insert(user); - return true; - } + case SILInstructionKind::TryApplyInst: case SILInstructionKind::BeginApplyInst: { - if (!canApplyBeTreatedAsLoad(addressUse, ApplySite(user), - originalCopy->getSrc())) + auto convention = ApplySite(user).getArgumentConvention(*addressUse); + if (!convention.isGuaranteedConvention()) return false; - auto beginApply = cast(user); - // Register 'end_apply'/'abort_apply' as loads as well - // 'checkNoSourceModification' should check instructions until - // 'end_apply'/'abort_apply'. - for (auto tokenUse : beginApply->getTokenResult()->getUses()) { - SILInstruction *user = tokenUse->getUser(); - if (user->getParent() != block) - return false; - loadInsts.insert(tokenUse->getUser()); + loadInsts.insert(user); + if (auto *beginApply = dyn_cast(user)) { + // Register 'end_apply'/'abort_apply' as loads as well + // 'checkNoSourceModification' should check instructions until + // 'end_apply'/'abort_apply'. + for (auto tokenUse : beginApply->getTokenResult()->getUses()) { + SILInstruction *tokenUser = tokenUse->getUser(); + if (tokenUser->getParent() != block) + return false; + loadInsts.insert(tokenUser); + } } return true; } @@ -321,8 +288,17 @@ bool TempRValueOptPass::checkNoSourceModification( // If this is the last use of the temp we are ok. After this point, // modifications to the source don't matter anymore. - if (numLoadsFound == useInsts.size()) + // Note that we are assuming here that if an instruction loads and writes + // to copySrc at the same time (like a copy_addr could do), the write + // takes effect after the load. + if (numLoadsFound == useInsts.size()) { + // Function calls are an exception: in a called function a potential + // modification of copySrc could occur _before_ the read of the temporary. + if (FullApplySite::isa(inst) && aa->mayWriteToMemory(inst, copySrc)) + return false; + return true; + } if (aa->mayWriteToMemory(inst, copySrc)) { LLVM_DEBUG(llvm::dbgs() << " Source modified by" << *iter); From 7c293d8de960045fbe2d1717aa21481d801cd036 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 14 Oct 2020 19:31:50 +0200 Subject: [PATCH 501/745] TempRValueOpt: fix the handling of begin_access Consider the related end_access instructions as uses to correctly mark the end of the lifetime of the temporary. This fixes a miscompile in case there is a modification of the copy-source between an begin_access and end_access. --- .../Transforms/TempRValueElimination.cpp | 21 ++++++++++++++++--- test/SILOptimizer/temp_rvalue_opt_ossa.sil | 21 +++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp index a3f1a1ee5ae16..7e32ac2459250 100644 --- a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp +++ b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp @@ -153,9 +153,24 @@ collectLoads(Operand *addressUse, CopyAddrInst *originalCopy, LLVM_DEBUG(llvm::dbgs() << " Temp use may write/destroy its source" << *user); return false; - case SILInstructionKind::BeginAccessInst: - return cast(user)->getAccessKind() == SILAccessKind::Read; - + case SILInstructionKind::BeginAccessInst: { + auto *beginAccess = cast(user); + if (beginAccess->getAccessKind() != SILAccessKind::Read) + return false; + // We don't have to recursively call collectLoads for the beginAccess + // result, because a SILAccessKind::Read already guarantees that there are + // no writes to the beginAccess result address (or any projection from it). + // But we have to register the end-accesses as loads to correctly mark the + // end-of-lifetime of the temporary object. + for (Operand *accessUse : beginAccess->getUses()) { + if (auto *endAccess = dyn_cast(accessUse->getUser())) { + if (endAccess->getParent() != block) + return false; + loadInsts.insert(endAccess); + } + } + return true; + } case SILInstructionKind::MarkDependenceInst: { auto mdi = cast(user); // If the user is the base operand of the MarkDependenceInst we can return diff --git a/test/SILOptimizer/temp_rvalue_opt_ossa.sil b/test/SILOptimizer/temp_rvalue_opt_ossa.sil index 27db90c284f2f..f925d6745429d 100644 --- a/test/SILOptimizer/temp_rvalue_opt_ossa.sil +++ b/test/SILOptimizer/temp_rvalue_opt_ossa.sil @@ -759,6 +759,27 @@ bb0(%0 : $*Klass): return %9999 : $() } + +// CHECK-LABEL: sil [ossa] @dont_optimize_with_modify_inside_access +// CHECK: [[STK:%[0-9]+]] = alloc_stack $Klass +// CHECK: copy_addr %0 to [initialization] [[STK]] +// CHECK: begin_access [read] [static] [[STK]] +// CHECK-LABEL: } // end sil function 'dont_optimize_with_modify_inside_access' +sil [ossa] @dont_optimize_with_modify_inside_access : $@convention(thin) (@inout Klass, @owned Klass) -> () { +bb0(%0 : $*Klass, %1 : @owned $Klass): + %stack = alloc_stack $Klass + copy_addr %0 to [initialization] %stack : $*Klass + %f = function_ref @inguaranteed_user_without_result : $@convention(thin) (@in_guaranteed Klass) -> () + %access = begin_access [read] [static] %stack : $*Klass + store %1 to [assign] %0 : $*Klass // This store prevents the optimization + %call = apply %f(%access) : $@convention(thin) (@in_guaranteed Klass) -> () + end_access %access : $*Klass + destroy_addr %stack : $*Klass + dealloc_stack %stack : $*Klass + %9999 = tuple() + return %9999 : $() +} + ///////////////// // Store Tests // ///////////////// From 6310dfcc939d88664d1fb63a908f1139e9cf6b47 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 14 Oct 2020 19:48:23 +0200 Subject: [PATCH 502/745] TempRValueOpt: fix the handling of load [take] load [take] was not considered as a use and it was not detected if it's in a different basic block. This fixes a miscompile in case there is a modification of the copy-source before a load [take]. rdar://problem/69757314 --- .../Transforms/TempRValueElimination.cpp | 25 ++++------ test/SILOptimizer/temp_rvalue_opt_ossa.sil | 46 +++++++++++-------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp index 7e32ac2459250..2262f487c3538 100644 --- a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp +++ b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp @@ -241,7 +241,8 @@ collectLoads(Operand *addressUse, CopyAddrInst *originalCopy, case SILInstructionKind::UncheckedAddrCastInst: return collectLoadsFromProjection(cast(user), originalCopy, loadInsts); - case SILInstructionKind::LoadInst: + + case SILInstructionKind::LoadInst: { // Loads are the end of the data flow chain. The users of the load can't // access the temporary storage. // @@ -251,14 +252,17 @@ collectLoads(Operand *addressUse, CopyAddrInst *originalCopy, // function. So if we have such a load [take], we /must/ have a // reinitialization or an alloc_stack that does not fit the pattern we are // expecting from SILGen. Be conservative and return false. - if (auto *li = dyn_cast(user)) { - if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) { - return false; - } + auto *li = cast(user); + if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take && + // Only accept load [take] if it takes the whole temporary object. + // load [take] from a projection would destroy only a part of the + // temporary and we don't handle this. + address != originalCopy->getDest()) { + return false; } loadInsts.insert(user); return true; - + } case SILInstructionKind::LoadBorrowInst: loadInsts.insert(user); return true; @@ -432,15 +436,6 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { if (isa(user) || isa(user)) continue; - // Same for load [take] on the top level temp object. SILGen always takes - // whole values from temporaries. If we have load [take] on projections from - // our base, we fail since those would be re-initializations. - if (auto *li = dyn_cast(user)) { - if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) { - continue; - } - } - if (!collectLoads(useOper, copyInst, loadInsts)) return false; } diff --git a/test/SILOptimizer/temp_rvalue_opt_ossa.sil b/test/SILOptimizer/temp_rvalue_opt_ossa.sil index f925d6745429d..03ad97cade07d 100644 --- a/test/SILOptimizer/temp_rvalue_opt_ossa.sil +++ b/test/SILOptimizer/temp_rvalue_opt_ossa.sil @@ -128,6 +128,22 @@ bb0(%0 : $*B, %1 : $*GS): return %9999 : $() } +// CHECK-LABEL: sil [ossa] @store_before_load_take +// CHECK: [[STK:%[0-9]+]] = alloc_stack +// CHECK: copy_addr [take] %0 to [initialization] [[STK]] +// CHECK: store +// CHECK: load [take] [[STK]] +// CHECK: return +// CHECK: } // end sil function 'store_before_load_take' +sil [ossa] @store_before_load_take : $@convention(thin) (@inout Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned Builtin.NativeObject { +bb0(%0 : $*Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): + %stk = alloc_stack $Builtin.NativeObject + copy_addr [take] %0 to [initialization] %stk : $*Builtin.NativeObject + store %1 to [init] %0 : $*Builtin.NativeObject + %obj = load [take] %stk : $*Builtin.NativeObject + dealloc_stack %stk : $*Builtin.NativeObject + return %obj : $Builtin.NativeObject +} // CHECK-LABEL: sil [ossa] @load_in_wrong_block // CHECK: bb0(%0 : $*GS): // CHECK-NEXT: alloc_stack @@ -693,31 +709,20 @@ bb0(%0 : @owned $GS): return %obj : $GS } -// CHECK-LABEL: sil [ossa] @hoist_load_copy_to_src_copy_addr_site_two_takes : $@convention(thin) (@in GS) -> @owned GS { -// CHECK: bb0([[ARG:%.*]] : -// CHECK: [[COPY_1:%.*]] = load [copy] [[ARG]] -// CHECK: [[COPY_2:%.*]] = load [copy] [[ARG]] -// CHECK: destroy_addr [[ARG]] -// CHECK: cond_br undef, bb1, bb2 -// +// CHECK-LABEL: sil [ossa] @dont_optimize_with_load_in_different_block +// CHECK: [[STK:%[0-9]+]] = alloc_stack +// CHECK: copy_addr %0 to [initialization] [[STK]] // CHECK: bb1: -// CHECK: destroy_value [[COPY_1]] -// CHECK: br bb3([[COPY_2]] : -// +// CHECK: load [take] [[STK]] // CHECK: bb2: -// CHECK: destroy_value [[COPY_2]] -// CHECK: br bb3([[COPY_1]] : -// -// CHECK: bb3([[RESULT:%.*]] : @owned -// CHECK: apply {{%.*}}([[RESULT]]) -// CHECK: return [[RESULT]] -// CHECK: } // end sil function 'hoist_load_copy_to_src_copy_addr_site_two_takes' -sil [ossa] @hoist_load_copy_to_src_copy_addr_site_two_takes : $@convention(thin) (@in GS) -> @owned GS { -bb0(%0 : $*GS): +// CHECK: copy_addr %1 to %0 +// CHECK: load [take] [[STK]] +// CHECK: } // end sil function 'dont_optimize_with_load_in_different_block' +sil [ossa] @dont_optimize_with_load_in_different_block : $@convention(thin) (@inout GS, @in_guaranteed GS) -> @owned GS { +bb0(%0 : $*GS, %1 : $*GS): %f = function_ref @use_gsbase_builtinnativeobject : $@convention(thin) (@guaranteed GS) -> () %stk = alloc_stack $GS copy_addr %0 to [initialization] %stk : $*GS - destroy_addr %0 : $*GS cond_br undef, bb1, bb2 bb1: @@ -725,6 +730,7 @@ bb1: br bb3(%obj : $GS) bb2: + copy_addr %1 to %0 : $*GS %obj2 = load [take] %stk : $*GS br bb3(%obj2 : $GS) From 9a10ec7d581345cbfcd15990c54848ca1b25dc8c Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 16 Oct 2020 16:23:13 +0200 Subject: [PATCH 503/745] SILBuilder: add an API to insert _after_ an instruction. ... and use that API in FullApplySite::insertAfterInvocation. Also change FullApplySite::insertAfterInvocation/insertAfterFullEvaluation to directly pass a SILBuilder instead of just an insertion point to the callback. This makes more sense (given the function names) and simplifies the usages. It's a NFC. --- include/swift/SIL/ApplySite.h | 50 +++---------------- include/swift/SIL/SILBuilder.h | 14 ++++++ lib/SIL/IR/ApplySite.cpp | 46 +++++++++++++++++ lib/SIL/IR/CMakeLists.txt | 1 + lib/SIL/IR/SILBuilder.cpp | 16 ++++++ .../Mandatory/MandatoryInlining.cpp | 20 ++++---- lib/SILOptimizer/Utils/Generics.cpp | 12 ++--- .../Utils/PartialApplyCombiner.cpp | 6 +-- 8 files changed, 101 insertions(+), 64 deletions(-) create mode 100644 lib/SIL/IR/ApplySite.cpp diff --git a/include/swift/SIL/ApplySite.h b/include/swift/SIL/ApplySite.h index e11ca87b047b6..ce7ac40e608db 100644 --- a/include/swift/SIL/ApplySite.h +++ b/include/swift/SIL/ApplySite.h @@ -585,62 +585,28 @@ class FullApplySite : public ApplySite { llvm_unreachable("Covered switch isn't covered?!"); } - /// If this is a terminator apply site, then pass the first instruction of - /// each successor to fun. Otherwise, pass std::next(Inst). + /// If this is a terminator apply site, then pass a builder to insert at the + /// first instruction of each successor to \p func. Otherwise, pass a builder + /// to insert at std::next(Inst). /// /// The intention is that this abstraction will enable the compiler writer to /// ignore whether or not an apply site is a terminator when inserting /// instructions after an apply site. This results in eliminating unnecessary /// if-else code otherwise required to handle such situations. /// - /// NOTE: We return std::next() for begin_apply. If one wishes to insert code + /// NOTE: We pass std::next() for begin_apply. If one wishes to insert code /// /after/ the end_apply/abort_apply, please use instead /// insertAfterFullEvaluation. - void insertAfterInvocation( - function_ref func) const { - switch (getKind()) { - case FullApplySiteKind::ApplyInst: - case FullApplySiteKind::BeginApplyInst: - return func(std::next(getInstruction()->getIterator())); - case FullApplySiteKind::TryApplyInst: - for (auto *succBlock : - cast(getInstruction())->getSuccessorBlocks()) { - func(succBlock->begin()); - } - return; - } - llvm_unreachable("Covered switch isn't covered"); - } + void insertAfterInvocation(function_ref func) const; - /// Pass to func insertion points that are guaranteed to be immediately after - /// this full apply site has completely finished executing. + /// Pass a builder with insertion points that are guaranteed to be immediately + /// after this full apply site has completely finished executing. /// /// This is just like insertAfterInvocation except that if the full apply site /// is a begin_apply, we pass the insertion points after the end_apply, /// abort_apply rather than an insertion point right after the /// begin_apply. For such functionality, please invoke insertAfterInvocation. - void insertAfterFullEvaluation( - function_ref func) const { - switch (getKind()) { - case FullApplySiteKind::ApplyInst: - case FullApplySiteKind::TryApplyInst: - return insertAfterInvocation(func); - case FullApplySiteKind::BeginApplyInst: - SmallVector endApplies; - SmallVector abortApplies; - auto *bai = cast(getInstruction()); - bai->getCoroutineEndPoints(endApplies, abortApplies); - for (auto *eai : endApplies) { - func(std::next(eai->getIterator())); - } - for (auto *aai : abortApplies) { - func(std::next(aai->getIterator())); - } - return; - } - - llvm_unreachable("covered switch isn't covered"); - } + void insertAfterFullEvaluation(function_ref func) const; /// Returns true if \p op is an operand that passes an indirect /// result argument to the apply site. diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 83d93da9059c1..0292cf2cbae91 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -2499,6 +2499,20 @@ class SILBuilderWithScope : public SILBuilder { assert(DS && "Instruction without debug scope associated!"); setCurrentDebugScope(DS); } + + /// If \p inst is a terminator apply site, then pass a builder to insert at + /// the first instruction of each successor to \p func. Otherwise, pass a + /// builder to insert at std::next(inst). + /// + /// The intention is that this abstraction will enable the compiler writer to + /// ignore whether or not \p inst is a terminator when inserting instructions + /// after \p inst. + /// + /// Precondition: It's the responsibility of the caller to ensure that if + /// \p inst is a terminator, all successor blocks have only a single + /// predecessor block: the parent of \p inst. + static void insertAfter(SILInstruction *inst, + function_ref func); }; class SavedInsertionPointRAII { diff --git a/lib/SIL/IR/ApplySite.cpp b/lib/SIL/IR/ApplySite.cpp new file mode 100644 index 0000000000000..2e6aa2bd2077c --- /dev/null +++ b/lib/SIL/IR/ApplySite.cpp @@ -0,0 +1,46 @@ +//===--- ApplySite.cpp - Wrapper around apply instructions ----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/SIL/ApplySite.h" +#include "swift/SIL/SILBuilder.h" + + +using namespace swift; + +void FullApplySite::insertAfterInvocation(function_ref func) const { + SILBuilderWithScope::insertAfter(getInstruction(), func); +} + +void FullApplySite::insertAfterFullEvaluation( + function_ref func) const { + switch (getKind()) { + case FullApplySiteKind::ApplyInst: + case FullApplySiteKind::TryApplyInst: + return insertAfterInvocation(func); + case FullApplySiteKind::BeginApplyInst: + SmallVector endApplies; + SmallVector abortApplies; + auto *bai = cast(getInstruction()); + bai->getCoroutineEndPoints(endApplies, abortApplies); + for (auto *eai : endApplies) { + SILBuilderWithScope builder(std::next(eai->getIterator())); + func(builder); + } + for (auto *aai : abortApplies) { + SILBuilderWithScope builder(std::next(aai->getIterator())); + func(builder); + } + return; + } + llvm_unreachable("covered switch isn't covered"); +} + diff --git a/lib/SIL/IR/CMakeLists.txt b/lib/SIL/IR/CMakeLists.txt index d5b426ed47358..394ceb88544bf 100644 --- a/lib/SIL/IR/CMakeLists.txt +++ b/lib/SIL/IR/CMakeLists.txt @@ -1,5 +1,6 @@ target_sources(swiftSIL PRIVATE AbstractionPattern.cpp + ApplySite.cpp Bridging.cpp Linker.cpp Notifications.cpp diff --git a/lib/SIL/IR/SILBuilder.cpp b/lib/SIL/IR/SILBuilder.cpp index c925cc83bb737..ff246686d696d 100644 --- a/lib/SIL/IR/SILBuilder.cpp +++ b/lib/SIL/IR/SILBuilder.cpp @@ -661,3 +661,19 @@ CheckedCastBranchInst *SILBuilder::createCheckedCastBranch( destLoweredTy, destFormalTy, successBB, failureBB, getFunction(), C.OpenedArchetypes, target1Count, target2Count)); } + +void SILBuilderWithScope::insertAfter(SILInstruction *inst, + function_ref func) { + if (isa(inst)) { + for (const SILSuccessor &succ : inst->getParent()->getSuccessors()) { + SILBasicBlock *succBlock = succ; + assert(succBlock->getSinglePredecessorBlock() == inst->getParent() && + "the terminator instruction must not have critical successors"); + SILBuilderWithScope builder(succBlock->begin()); + func(builder); + } + } else { + SILBuilderWithScope builder(std::next(inst->getIterator())); + func(builder); + } +} diff --git a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp index c69f739cf3bb9..e829648d5ffd0 100644 --- a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp +++ b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp @@ -123,9 +123,9 @@ static bool fixupReferenceCounts( }); if (!consumedInLoop) { - applySite.insertAfterInvocation([&](SILBasicBlock::iterator iter) { - SILBuilderWithScope(iter).createDestroyAddr(loc, stackLoc); - SILBuilderWithScope(iter).createDeallocStack(loc, stackLoc); + applySite.insertAfterInvocation([&](SILBuilder &builder) { + builder.createDestroyAddr(loc, stackLoc); + builder.createDeallocStack(loc, stackLoc); }); } v = stackLoc; @@ -176,11 +176,11 @@ static bool fixupReferenceCounts( // uses in the top of a diamond and need to insert a destroy after the // apply since the leak will just cover the other path. if (!consumedInLoop) { - applySite.insertAfterInvocation([&](SILBasicBlock::iterator iter) { + applySite.insertAfterInvocation([&](SILBuilder &builder) { if (hasOwnership) { - SILBuilderWithScope(iter).createEndBorrow(loc, argument); + builder.createEndBorrow(loc, argument); } - SILBuilderWithScope(iter).emitDestroyValueOperation(loc, copy); + builder.emitDestroyValueOperation(loc, copy); }); } v = argument; @@ -217,8 +217,8 @@ static bool fixupReferenceCounts( // Then insert destroys after the apply site since our value is not being // consumed as part of the actual apply. - applySite.insertAfterInvocation([&](SILBasicBlock::iterator iter) { - SILBuilderWithScope(iter).emitDestroyValueOperation(loc, v); + applySite.insertAfterInvocation([&](SILBuilder &builder) { + builder.emitDestroyValueOperation(loc, v); }); break; } @@ -263,8 +263,8 @@ static bool fixupReferenceCounts( // Destroy the callee as the apply would have done if our function is not // callee guaranteed. if (!isCalleeGuaranteed) { - applySite.insertAfterInvocation([&](SILBasicBlock::iterator iter) { - SILBuilderWithScope(iter).emitDestroyValueOperation(loc, calleeValue); + applySite.insertAfterInvocation([&](SILBuilder &builder) { + builder.emitDestroyValueOperation(loc, calleeValue); }); } return invalidatedStackNesting; diff --git a/lib/SILOptimizer/Utils/Generics.cpp b/lib/SILOptimizer/Utils/Generics.cpp index 389d06b559cfb..694d3dc14b18a 100644 --- a/lib/SILOptimizer/Utils/Generics.cpp +++ b/lib/SILOptimizer/Utils/Generics.cpp @@ -2027,8 +2027,7 @@ static ApplySite replaceWithSpecializedCallee(ApplySite applySite, assert(resultBlock->getSinglePredecessorBlock() == tai->getParent()); // First insert the cleanups for our arguments int he appropriate spot. FullApplySite(tai).insertAfterFullEvaluation( - [&](SILBasicBlock::iterator insertPt) { - SILBuilderWithScope argBuilder(insertPt); + [&](SILBuilder &argBuilder) { cleanupCallArguments(argBuilder, loc, arguments, argsNeedingEndBorrow); }); @@ -2052,8 +2051,7 @@ static ApplySite replaceWithSpecializedCallee(ApplySite applySite, case ApplySiteKind::ApplyInst: { auto *ai = cast(applySite); FullApplySite(ai).insertAfterFullEvaluation( - [&](SILBasicBlock::iterator insertPt) { - SILBuilderWithScope argBuilder(insertPt); + [&](SILBuilder &argBuilder) { cleanupCallArguments(argBuilder, loc, arguments, argsNeedingEndBorrow); }); @@ -2082,8 +2080,7 @@ static ApplySite replaceWithSpecializedCallee(ApplySite applySite, auto *bai = cast(applySite); assert(!resultOut); FullApplySite(bai).insertAfterFullEvaluation( - [&](SILBasicBlock::iterator insertPt) { - SILBuilderWithScope argBuilder(insertPt); + [&](SILBuilder &argBuilder) { cleanupCallArguments(argBuilder, loc, arguments, argsNeedingEndBorrow); }); @@ -2227,8 +2224,7 @@ SILFunction *ReabstractionThunkGenerator::createThunk() { // Now that we have finished constructing our CFG (note the return above), // insert any compensating end borrows that we need. - ApplySite.insertAfterFullEvaluation([&](SILBasicBlock::iterator insertPt) { - SILBuilderWithScope argBuilder(insertPt); + ApplySite.insertAfterFullEvaluation([&](SILBuilder &argBuilder) { cleanupCallArguments(argBuilder, Loc, Arguments, ArgsThatNeedEndBorrow); }); diff --git a/lib/SILOptimizer/Utils/PartialApplyCombiner.cpp b/lib/SILOptimizer/Utils/PartialApplyCombiner.cpp index 4d662e974d1b8..d3161d892b6d1 100644 --- a/lib/SILOptimizer/Utils/PartialApplyCombiner.cpp +++ b/lib/SILOptimizer/Utils/PartialApplyCombiner.cpp @@ -153,8 +153,7 @@ void PartialApplyCombiner::processSingleApply(FullApplySite paiAI) { auto *ASI = builder.createAllocStack(pai->getLoc(), arg->getType()); builder.createCopyAddr(pai->getLoc(), arg, ASI, IsTake_t::IsNotTake, IsInitialization_t::IsInitialization); - paiAI.insertAfterFullEvaluation([&](SILBasicBlock::iterator insertPt) { - SILBuilderWithScope builder(insertPt); + paiAI.insertAfterFullEvaluation([&](SILBuilder &builder) { builder.createDeallocStack(destroyloc, ASI); }); arg = ASI; @@ -184,8 +183,7 @@ void PartialApplyCombiner::processSingleApply(FullApplySite paiAI) { // We also need to destroy the partial_apply instruction itself because it is // consumed by the apply_instruction. if (!pai->hasCalleeGuaranteedContext()) { - paiAI.insertAfterFullEvaluation([&](SILBasicBlock::iterator insertPt) { - SILBuilderWithScope builder(insertPt); + paiAI.insertAfterFullEvaluation([&](SILBuilder &builder) { builder.emitDestroyValueOperation(destroyloc, pai); }); } From 4557f151b1e90b874f77736bdf4ab20df8affe69 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 14 Oct 2020 20:16:21 +0200 Subject: [PATCH 504/745] TempRValueOpt: fix the placement of destroy_addr in case of a copy_addr [take] Instead of reusing the existing destroy_addr (or load/copy_addr [take]) of the temporary, insert a new destroy_addr - at the correct location. This fixes a memory lifetime failure in case the copy-source is modified (actually: re-initialized) after the last use of the temporary: E.g. (before copy elimination): copy_addr [take] %src to [initialization] %temp %x = load %temp // last use of %temp store %y to [init] %src destroy_addr %temp The correct location for the destroy_addr is after the last use (after copy elimination): %x = load %src destroy_addr %src store %y to [init] %src rdar://problem/69757314 --- .../Transforms/TempRValueElimination.cpp | 164 +++++++++--------- test/SILOptimizer/temp_rvalue_opt.sil | 29 +++- test/SILOptimizer/temp_rvalue_opt_ossa.sil | 67 ++++++- 3 files changed, 176 insertions(+), 84 deletions(-) diff --git a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp index 2262f487c3538..f07c66b8a8c5c 100644 --- a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp +++ b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp @@ -81,9 +81,8 @@ class TempRValueOptPass : public SILFunctionTransform { CopyAddrInst *originalCopy, SmallPtrSetImpl &loadInsts); - bool - checkNoSourceModification(CopyAddrInst *copyInst, SILValue copySrc, - const SmallPtrSetImpl &useInsts); + SILInstruction *getLastUseWhileSourceIsNotModified( + CopyAddrInst *copyInst, const SmallPtrSetImpl &useInsts); bool checkTempObjectDestroy(AllocStackInst *tempObj, CopyAddrInst *copyInst, @@ -157,11 +156,16 @@ collectLoads(Operand *addressUse, CopyAddrInst *originalCopy, auto *beginAccess = cast(user); if (beginAccess->getAccessKind() != SILAccessKind::Read) return false; + // We don't have to recursively call collectLoads for the beginAccess // result, because a SILAccessKind::Read already guarantees that there are // no writes to the beginAccess result address (or any projection from it). // But we have to register the end-accesses as loads to correctly mark the - // end-of-lifetime of the temporary object. + // end-of-lifetime of the tempObj. + // + // %addr = begin_access [read] + // ... // there can be no writes to %addr here + // end_acess %addr // <- This is where the use actually ends. for (Operand *accessUse : beginAccess->getUses()) { if (auto *endAccess = dyn_cast(accessUse->getUser())) { if (endAccess->getParent() != block) @@ -284,20 +288,29 @@ collectLoads(Operand *addressUse, CopyAddrInst *originalCopy, } } -/// Checks if the copy's source can be modified within the temporary's lifetime. +/// Checks if the source of \p copyInst is not modified within the temporary's +/// lifetime, i.e. is not modified before the last use of \p useInsts. +/// +/// If there are no source modifications with the lifetime, returns the last +/// user (or copyInst if there are no uses at all). +/// Otherwise, returns a nullptr. /// /// Unfortunately, we cannot simply use the destroy points as the lifetime end, /// because they can be in a different basic block (that's what SILGen /// generates). Instead we guarantee that all normal uses are within the block /// of the temporary and look for the last use, which effectively ends the /// lifetime. -bool TempRValueOptPass::checkNoSourceModification( - CopyAddrInst *copyInst, SILValue copySrc, - const SmallPtrSetImpl &useInsts) { +SILInstruction *TempRValueOptPass::getLastUseWhileSourceIsNotModified( + CopyAddrInst *copyInst, const SmallPtrSetImpl &useInsts) { + if (useInsts.empty()) + return copyInst; unsigned numLoadsFound = 0; - auto iter = std::next(copyInst->getIterator()); + SILValue copySrc = copyInst->getSrc(); + // We already checked that the useful lifetime of the temporary ends in - // the initialization block. + // the initialization block. Iterate over the instructions of the block, + // starting at copyInst, until we get to the last user. + auto iter = std::next(copyInst->getIterator()); auto iterEnd = copyInst->getParent()->end(); for (; iter != iterEnd; ++iter) { SILInstruction *inst = &*iter; @@ -314,19 +327,19 @@ bool TempRValueOptPass::checkNoSourceModification( // Function calls are an exception: in a called function a potential // modification of copySrc could occur _before_ the read of the temporary. if (FullApplySite::isa(inst) && aa->mayWriteToMemory(inst, copySrc)) - return false; + return nullptr; - return true; + return inst; } if (aa->mayWriteToMemory(inst, copySrc)) { LLVM_DEBUG(llvm::dbgs() << " Source modified by" << *iter); - return false; + return nullptr; } } // For some reason, not all normal uses have been seen between the copy and // the end of the initialization block. We should never reach here. - return false; + return nullptr; } /// Return true if the \p tempObj, which is initialized by \p copyInst, is @@ -342,11 +355,6 @@ bool TempRValueOptPass::checkNoSourceModification( bool TempRValueOptPass::checkTempObjectDestroy( AllocStackInst *tempObj, CopyAddrInst *copyInst, ValueLifetimeAnalysis::Frontier &tempAddressFrontier) { - // If the original copy was a take, then replacing all uses cannot affect - // the lifetime. - if (copyInst->isTakeOfSrc()) - return true; - // ValueLifetimeAnalysis is not normally used for address types. It does not // reason about the lifetime of the in-memory object. However the utility can // be abused here to check that the address is directly destroyed on all @@ -385,15 +393,8 @@ bool TempRValueOptPass::checkTempObjectDestroy( if (isa(lastUser)) continue; - if (auto *li = dyn_cast(lastUser)) { - if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) { - continue; - } - } - if (auto *cai = dyn_cast(lastUser)) { assert(cai->getSrc() == tempObj && "collectLoads checks for writes"); - assert(!copyInst->isTakeOfSrc() && "checked above"); if (cai->isTakeOfSrc()) continue; } @@ -411,6 +412,8 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { if (!tempObj) return false; + bool isOSSA = copyInst->getFunction()->hasOwnership(); + // The copy's source address must not be a scoped instruction, like // begin_borrow. When the temporary object is eliminated, it's uses are // replaced with the copy's source. Therefore, the source address must be @@ -418,9 +421,13 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { // source. End-of-scope markers, such as end_borrow, do not write to or // destroy memory, so scoped addresses are not valid replacements. SILValue copySrc = stripAccessMarkers(copyInst->getSrc()); - assert(tempObj != copySrc && "can't initialize temporary with itself"); + // If the source of the copyInst is taken, we must insert a compensating + // destroy_addr. This must be done at the right spot: after the last use + // tempObj, but before any (potential) re-initialization of the source. + bool needToInsertDestroy = copyInst->isTakeOfSrc(); + // Scan all uses of the temporary storage (tempObj) to verify they all refer // to the value initialized by this copy. It is sufficient to check that the // only users that modify memory are the copy_addr [initialization] and @@ -432,16 +439,44 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { if (user == copyInst) continue; - // Destroys and deallocations are allowed to be in a different block. - if (isa(user) || isa(user)) + // Deallocations are allowed to be in a different block. + if (isa(user)) + continue; + + // Also, destroys are allowed to be in a different block. + if (isa(user)) { + if (!isOSSA && needToInsertDestroy) { + // In non-OSSA mode, for the purpose of inserting the destroy of + // copySrc, we have to be conservative and assume that the lifetime of + // tempObj goes beyond it's last use - until the final destroy_addr. + // Otherwise we would risk of inserting the destroy too early. + // So we just treat the destroy_addr as any other use of tempObj. + if (user->getParent() != copyInst->getParent()) + return false; + loadInsts.insert(user); + } continue; + } if (!collectLoads(useOper, copyInst, loadInsts)) return false; } // Check if the source is modified within the lifetime of the temporary. - if (!checkNoSourceModification(copyInst, copySrc, loadInsts)) + SILInstruction *lastLoadInst = getLastUseWhileSourceIsNotModified(copyInst, + loadInsts); + if (!lastLoadInst) + return false; + + // We cannot insert the destroy of copySrc after lastLoadInst if copySrc is + // re-initialized by exactly this instruction. + // This is a corner case, but can happen if lastLoadInst is a copy_addr. + // Example: + // copy_addr [take] %copySrc to [initialization] %tempObj // copyInst + // copy_addr [take] %tempObj to [initialization] %copySrc // lastLoadInst + if (needToInsertDestroy && lastLoadInst != copyInst && + !isa(lastLoadInst) && + aa->mayWriteToMemory(lastLoadInst, copySrc)) return false; ValueLifetimeAnalysis::Frontier tempAddressFrontier; @@ -450,71 +485,45 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { LLVM_DEBUG(llvm::dbgs() << " Success: replace temp" << *tempObj); - // Do a "replaceAllUses" by either deleting the users or replacing them with - // the source address. Note: we must not delete the original copyInst because - // it would crash the instruction iteration in run(). Instead the copyInst - // gets identical Src and Dest operands. + if (needToInsertDestroy) { + // Compensate the [take] of the original copyInst. + SILBuilderWithScope::insertAfter(lastLoadInst, [&] (SILBuilder &builder) { + builder.createDestroyAddr(builder.getInsertionPoint()->getLoc(), copySrc); + }); + } + + // * Replace all uses of the tempObj with the copySrc. // - // NOTE: We delete instructions at the end to allow us to use - // tempAddressFrontier to insert compensating destroys for load [take]. - SmallVector toDelete; + // * Delete the destroy(s) of tempObj (to compensate the removal of the + // original copyInst): either by erasing the destroy_addr or by converting + // load/copy_addr [take] into copying instructions. + // + // Note: we must not delete the original copyInst because it would crash the + // instruction iteration in run(). Instead the copyInst gets identical Src and + // Dest operands. while (!tempObj->use_empty()) { Operand *use = *tempObj->use_begin(); SILInstruction *user = use->getUser(); switch (user->getKind()) { case SILInstructionKind::DestroyAddrInst: - if (copyInst->isTakeOfSrc()) { - use->set(copySrc); - } else { - user->dropAllReferences(); - toDelete.push_back(user); - } - break; case SILInstructionKind::DeallocStackInst: - user->dropAllReferences(); - toDelete.push_back(user); + user->eraseFromParent(); break; case SILInstructionKind::CopyAddrInst: { auto *cai = cast(user); if (cai != copyInst) { assert(cai->getSrc() == tempObj); - if (cai->isTakeOfSrc() && !copyInst->isTakeOfSrc()) + if (cai->isTakeOfSrc()) cai->setIsTakeOfSrc(IsNotTake); } use->set(copySrc); break; } case SILInstructionKind::LoadInst: { - // If we do not have a load [take] or we have a load [take] and our - // copy_addr takes the source, just do the normal thing of setting the - // load to use the copyInst's source. auto *li = cast(user); - if (li->getOwnershipQualifier() != LoadOwnershipQualifier::Take || - copyInst->isTakeOfSrc()) { - use->set(copyInst->getSrc()); - break; - } - - // Otherwise, since copy_addr is not taking src, we need to ensure that we - // insert a copy of our value. We do that by creating a load [copy] at the - // copy_addr inst and RAUWing the load [take] with that. We then insert - // destroy_value for the load [copy] at all points where we had destroys - // that are not the specific take that we were optimizing. - SILBuilderWithScope builder(copyInst); - SILValue newLoad = builder.emitLoadValueOperation( - copyInst->getLoc(), copyInst->getSrc(), LoadOwnershipQualifier::Copy); - for (auto *inst : tempAddressFrontier) { - assert(inst->getIterator() != inst->getParent()->begin() && - "Should have caught this when checking destructor"); - auto prevInst = std::prev(inst->getIterator()); - if (&*prevInst == li) - continue; - SILBuilderWithScope builder(prevInst); - builder.emitDestroyValueOperation(prevInst->getLoc(), newLoad); - } - li->replaceAllUsesWith(newLoad); - li->dropAllReferences(); - toDelete.push_back(li); + if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) + li->setOwnershipQualifier(LoadOwnershipQualifier::Copy); + use->set(copyInst->getSrc()); break; } @@ -527,9 +536,6 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { } } - while (!toDelete.empty()) { - toDelete.pop_back_val()->eraseFromParent(); - } tempObj->eraseFromParent(); return true; } diff --git a/test/SILOptimizer/temp_rvalue_opt.sil b/test/SILOptimizer/temp_rvalue_opt.sil index 3e503d5a7ae94..cf34d9dcb481d 100644 --- a/test/SILOptimizer/temp_rvalue_opt.sil +++ b/test/SILOptimizer/temp_rvalue_opt.sil @@ -25,6 +25,7 @@ struct Str { sil @unknown : $@convention(thin) () -> () sil @load_string : $@convention(thin) (@in_guaranteed String) -> String +sil @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> () sil @inguaranteed_user_without_result : $@convention(thin) (@in_guaranteed Klass) -> () { bb0(%0 : $*Klass): @@ -456,6 +457,25 @@ bb3: return %9 : $() } +// CHECK-LABEL: sil @lifetime_beyond_last_use : +// CHECK-NOT: copy_addr +// CHECK: [[V:%[0-9]+]] = load %0 +// CHECK: apply {{%[0-9]+}}([[V]]) +// CHECK: destroy_addr %0 +// CHECK: } // end sil function 'lifetime_beyond_last_use' +sil @lifetime_beyond_last_use : $@convention(thin) (@in Klass) -> () { +bb0(%0 : $*Klass): + %2 = alloc_stack $Klass + copy_addr [take] %0 to [initialization] %2 : $*Klass + %4 = load %2 : $*Klass + %5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> () + %6 = apply %5(%4) : $@convention(thin) (@guaranteed Klass) -> () + destroy_addr %2 : $*Klass + dealloc_stack %2 : $*Klass + %9 = tuple () + return %9 : $() +} + // Make sure that we can eliminate temporaries passed via a temporary rvalue to // an @in_guaranteed function. // @@ -683,12 +703,13 @@ bb0(%0 : $*Builtin.NativeObject): return %v : $() } -// Remove a copy that is released via a load as long as it was a copy [take]. // CHECK-LABEL: sil @takeWithLoadRelease : $@convention(thin) (@in Builtin.NativeObject) -> () { // CHECK: bb0(%0 : $*Builtin.NativeObject): -// CHECK: [[V:%.*]] = load %0 : $*Builtin.NativeObject -// CHECK: apply %{{.*}}([[V]]) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () -// CHECK: release_value [[V]] : $Builtin.NativeObject +// CHECK: [[STK:%.*]] = alloc_stack $Builtin.NativeObject +// CHECK: copy_addr [take] %0 to [initialization] [[STK]] : $*Builtin.NativeObject +// CHECK: [[VAL:%.*]] = load [[STK]] : $*Builtin.NativeObject +// CHECK: apply %{{.*}}([[VAL]]) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () +// CHECK: release_value [[VAL]] : $Builtin.NativeObject // CHECK-LABEL: } // end sil function 'takeWithLoadRelease' sil @takeWithLoadRelease : $@convention(thin) (@in Builtin.NativeObject) -> () { bb0(%0 : $*Builtin.NativeObject): diff --git a/test/SILOptimizer/temp_rvalue_opt_ossa.sil b/test/SILOptimizer/temp_rvalue_opt_ossa.sil index 03ad97cade07d..e8e6d8984283e 100644 --- a/test/SILOptimizer/temp_rvalue_opt_ossa.sil +++ b/test/SILOptimizer/temp_rvalue_opt_ossa.sil @@ -144,6 +144,70 @@ bb0(%0 : $*Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): dealloc_stack %stk : $*Builtin.NativeObject return %obj : $Builtin.NativeObject } + +// CHECK-LABEL: sil [ossa] @copy_with_take_and_copy_from_src +// CHECK: bb0({{.*}}): +// CHECK-NEXT: destroy_addr %0 +// CHECK-NEXT: copy_addr [take] %1 to [initialization] %0 +// CHECK-NEXT: tuple +// CHECK-NEXT: return +// CHECK: } // end sil function 'copy_with_take_and_copy_from_src' +sil [ossa] @copy_with_take_and_copy_from_src : $@convention(thin) (@inout Builtin.NativeObject, @in Builtin.NativeObject) -> () { +bb0(%0 : $*Builtin.NativeObject, %1 : $*Builtin.NativeObject): + %stk = alloc_stack $Builtin.NativeObject + copy_addr [take] %0 to [initialization] %stk : $*Builtin.NativeObject + copy_addr [take] %1 to [initialization] %0 : $*Builtin.NativeObject + destroy_addr %stk : $*Builtin.NativeObject + dealloc_stack %stk : $*Builtin.NativeObject + %v = tuple () + return %v : $() +} + +// CHECK-LABEL: sil [ossa] @copy_take_and_try_apply +// CHECK-NOT: copy_addr +// CHECK: try_apply {{%[0-9]+}}(%0) +// CHECK: bb1({{.*}}): +// CHECK: destroy_addr %0 +// CHECK: bb2({{.*}}): +// CHECK: destroy_addr %0 +// CHECK: bb3: +// CHECK: store %1 to [init] %0 +// CHECK: } // end sil function 'copy_take_and_try_apply' +sil [ossa] @copy_take_and_try_apply : $@convention(thin) (@inout Klass, @owned Klass) -> () { +bb0(%0 : $*Klass, %1 : @owned $Klass): + %2 = alloc_stack $Klass + copy_addr [take] %0 to [initialization] %2 : $*Klass + %5 = function_ref @throwing_function : $@convention(thin) (@in_guaranteed Klass) -> ((), @error Error) + try_apply %5(%2) : $@convention(thin) (@in_guaranteed Klass) -> ((), @error Error), normal bb1, error bb2 +bb1(%r : $()): + br bb3 +bb2(%e : $Error): + br bb3 +bb3: + store %1 to [init] %0 : $*Klass + destroy_addr %2 : $*Klass + dealloc_stack %2 : $*Klass + %9 = tuple () + return %9 : $() +} + +// Currently we cannot optimize this. But in theory it's possible to eliminate +// both copies. +// +// CHECK-LABEL: sil [ossa] @copy_and_copy_back +// CHECK: [[STK:%[0-9]+]] = alloc_stack +// CHECK: copy_addr [take] %0 to [initialization] [[STK]] +// CHECK: copy_addr [take] [[STK]] to [initialization] %0 +// CHECK: } // end sil function 'copy_and_copy_back' +sil [ossa] @copy_and_copy_back : $@convention(thin) (@inout Builtin.NativeObject) -> () { +bb0(%0 : $*Builtin.NativeObject): + %stk = alloc_stack $Builtin.NativeObject + copy_addr [take] %0 to [initialization] %stk : $*Builtin.NativeObject + copy_addr [take] %stk to [initialization] %0 : $*Builtin.NativeObject + dealloc_stack %stk : $*Builtin.NativeObject + %v = tuple () + return %v : $() +} // CHECK-LABEL: sil [ossa] @load_in_wrong_block // CHECK: bb0(%0 : $*GS): // CHECK-NEXT: alloc_stack @@ -654,7 +718,8 @@ bb0(%0 : $*Builtin.NativeObject): // // CHECK-LABEL: sil [ossa] @takeWithLoadRelease : $@convention(thin) (@in Builtin.NativeObject) -> () { // CHECK: bb0(%0 : $*Builtin.NativeObject): -// CHECK: [[V:%.*]] = load [take] %0 : $*Builtin.NativeObject +// CHECK: [[V:%.*]] = load [copy] %0 : $*Builtin.NativeObject +// CHECK: destroy_addr %0 : $*Builtin.NativeObject // CHECK: apply %{{.*}}([[V]]) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () // CHECK: destroy_value [[V]] : $Builtin.NativeObject // CHECK-LABEL: } // end sil function 'takeWithLoadRelease' From 262c7b251f9d650231a394bedcd5d12ca527cc92 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 14 Oct 2020 20:55:13 +0200 Subject: [PATCH 505/745] TempRValueOpt: don't allow copy_addr [take] from a projection of the temporary. This fixes a memory lifetime failure. --- .../Transforms/TempRValueElimination.cpp | 4 +++ test/SILOptimizer/temp_rvalue_opt_ossa.sil | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp index f07c66b8a8c5c..5e86f2330c7fe 100644 --- a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp +++ b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp @@ -282,6 +282,10 @@ collectLoads(Operand *addressUse, CopyAddrInst *originalCopy, LLVM_DEBUG(llvm::dbgs() << " Temp written or taken" << *user); return false; } + // As with load [take], only accept copy_addr [take] if it takes the whole + // temporary object. + if (copyFromTmp->isTakeOfSrc() && address != originalCopy->getDest()) + return false; loadInsts.insert(copyFromTmp); return true; } diff --git a/test/SILOptimizer/temp_rvalue_opt_ossa.sil b/test/SILOptimizer/temp_rvalue_opt_ossa.sil index e8e6d8984283e..594bca1701863 100644 --- a/test/SILOptimizer/temp_rvalue_opt_ossa.sil +++ b/test/SILOptimizer/temp_rvalue_opt_ossa.sil @@ -16,6 +16,11 @@ struct GS { class Klass {} +struct Two { + var a: Klass + var b: Klass +} + sil @unknown : $@convention(thin) () -> () sil [ossa] @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> () @@ -208,6 +213,26 @@ bb0(%0 : $*Builtin.NativeObject): %v = tuple () return %v : $() } + +// CHECK-LABEL: sil [ossa] @dont_allow_copy_take_from_projection +// CHECK: [[STK:%[0-9]+]] = alloc_stack +// CHECK: copy_addr [take] %1 to [initialization] [[STK]] +// CHECK: } // end sil function 'dont_allow_copy_take_from_projection' +sil [ossa] @dont_allow_copy_take_from_projection : $@convention(thin) (@in Two) -> @out Two { +bb0(%0 : $*Two, %1 : $*Two): + %a0 = struct_element_addr %0 : $*Two, #Two.a + %b0 = struct_element_addr %0 : $*Two, #Two.b + %s = alloc_stack $Two + copy_addr [take] %1 to [initialization] %s : $*Two + %as = struct_element_addr %s : $*Two, #Two.a + %bs = struct_element_addr %s : $*Two, #Two.b + copy_addr [take] %as to [initialization] %a0 : $*Klass + copy_addr [take] %bs to [initialization] %b0 : $*Klass + dealloc_stack %s : $*Two + %r = tuple () + return %r : $() +} + // CHECK-LABEL: sil [ossa] @load_in_wrong_block // CHECK: bb0(%0 : $*GS): // CHECK-NEXT: alloc_stack From 1e5f3bd253f0fbfab7f39f176f321b3dce94d11b Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 16 Oct 2020 16:29:18 +0200 Subject: [PATCH 506/745] TempRValueOpt: only do the check for "unusual" temp object destruction in non-OSSA mode. In OSSA, memory locations are always destroyed in a "visible" way, e.g. with destroy_addr or load [take]. --- lib/SILOptimizer/Transforms/TempRValueElimination.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp index 5e86f2330c7fe..6f060ec7d2db2 100644 --- a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp +++ b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp @@ -85,8 +85,7 @@ class TempRValueOptPass : public SILFunctionTransform { CopyAddrInst *copyInst, const SmallPtrSetImpl &useInsts); bool - checkTempObjectDestroy(AllocStackInst *tempObj, CopyAddrInst *copyInst, - ValueLifetimeAnalysis::Frontier &tempAddressFrontier); + checkTempObjectDestroy(AllocStackInst *tempObj, CopyAddrInst *copyInst); bool tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst); std::pair @@ -357,8 +356,7 @@ SILInstruction *TempRValueOptPass::getLastUseWhileSourceIsNotModified( /// releasing it. Rather than detecting unbalanced load releases, simply check /// that tempObj is destroyed directly on all paths. bool TempRValueOptPass::checkTempObjectDestroy( - AllocStackInst *tempObj, CopyAddrInst *copyInst, - ValueLifetimeAnalysis::Frontier &tempAddressFrontier) { + AllocStackInst *tempObj, CopyAddrInst *copyInst) { // ValueLifetimeAnalysis is not normally used for address types. It does not // reason about the lifetime of the in-memory object. However the utility can // be abused here to check that the address is directly destroyed on all @@ -377,6 +375,7 @@ bool TempRValueOptPass::checkTempObjectDestroy( } // Find the boundary of tempObj's address lifetime, starting at copyInst. ValueLifetimeAnalysis vla(copyInst, users); + ValueLifetimeAnalysis::Frontier tempAddressFrontier; if (!vla.computeFrontier(tempAddressFrontier, ValueLifetimeAnalysis::DontModifyCFG)) { return false; @@ -483,8 +482,7 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { aa->mayWriteToMemory(lastLoadInst, copySrc)) return false; - ValueLifetimeAnalysis::Frontier tempAddressFrontier; - if (!checkTempObjectDestroy(tempObj, copyInst, tempAddressFrontier)) + if (!isOSSA && !checkTempObjectDestroy(tempObj, copyInst)) return false; LLVM_DEBUG(llvm::dbgs() << " Success: replace temp" << *tempObj); From 545b44e9c14f7423ec1c881c1450b8b3a568bf75 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Fri, 16 Oct 2020 08:21:58 -0700 Subject: [PATCH 507/745] [cxx-interop] Look through template decl to find constructor when importing the function name. Sometimes, on windows, we get a function template wrapping the constructor decl. In this case, look through the function template to find the constructor decl. --- lib/ClangImporter/ImportName.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 79fcd57ccc141..9e22535c2fddf 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -1608,14 +1608,18 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, SmallString<16> selectorSplitScratch; ArrayRef params; switch (D->getDeclName().getNameKind()) { - case clang::DeclarationName::CXXConstructorName: + case clang::DeclarationName::CXXConstructorName: { isInitializer = true; isFunction = true; result.info.initKind = CtorInitializerKind::Designated; baseName = "init"; - addEmptyArgNamesForClangFunction(cast(D), - argumentNames); + auto ctor = dyn_cast(D); + if (auto templateCtor = dyn_cast(D)) + ctor = cast(templateCtor->getAsFunction()); + assert(ctor && "Unkown decl with CXXConstructorName."); + addEmptyArgNamesForClangFunction(ctor, argumentNames); break; + } case clang::DeclarationName::CXXConversionFunctionName: case clang::DeclarationName::CXXDestructorName: From 1599d3e8dc7ff8940de6f7089a8610e9096bdfbd Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Fri, 16 Oct 2020 12:32:27 -0700 Subject: [PATCH 508/745] Pass subst options down when substitution sil_box type's substitution map rdar://70262551 --- lib/AST/Type.cpp | 2 +- .../specialize_opaque_result_types.swift | 4 ++ .../specialize_opaque_result_types2.sil | 46 +++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 test/SILOptimizer/Inputs/specialize_opaque_result_types.swift create mode 100644 test/SILOptimizer/specialize_opaque_result_types2.sil diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 6d9dae653c108..7ab9e95bd1b7f 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -3759,7 +3759,7 @@ static Type substType(Type derivedType, // we want to structurally substitute the substitutions. if (auto boxTy = dyn_cast(type)) { auto subMap = boxTy->getSubstitutions(); - auto newSubMap = subMap.subst(substitutions, lookupConformances); + auto newSubMap = subMap.subst(substitutions, lookupConformances, options); return SILBoxType::get(boxTy->getASTContext(), boxTy->getLayout(), diff --git a/test/SILOptimizer/Inputs/specialize_opaque_result_types.swift b/test/SILOptimizer/Inputs/specialize_opaque_result_types.swift new file mode 100644 index 0000000000000..9b90b0f664ed3 --- /dev/null +++ b/test/SILOptimizer/Inputs/specialize_opaque_result_types.swift @@ -0,0 +1,4 @@ +public protocol Butt {} +extension Int: Butt {} + +public func exportsOpaqueReturn() -> some Butt { return 0 } diff --git a/test/SILOptimizer/specialize_opaque_result_types2.sil b/test/SILOptimizer/specialize_opaque_result_types2.sil new file mode 100644 index 0000000000000..43827bb12136c --- /dev/null +++ b/test/SILOptimizer/specialize_opaque_result_types2.sil @@ -0,0 +1,46 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -disable-availability-checking -primary-file %S/Inputs/specialize_opaque_result_types.swift -enable-library-evolution -module-name A -emit-sib -o %t/A.sib +// RUN: %target-swift-frontend -emit-sil -primary-file %s -enable-library-evolution -O -module-name A %t/A.sib -o - | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +typealias SomeButt = @_opaqueReturnTypeOf("$s1A19exportsOpaqueReturnQryF", 0) opaque + +sil @$foobar : $@convention(thin) () -> @out SomeButt { +bb0(%0 : $*Int): + %1 = integer_literal $Builtin.Int64, 0 // user: %2 + %2 = struct $Int (%1 : $Builtin.Int64) // user: %3 + store %2 to %0 : $*Int // id: %3 + %4 = tuple () // user: %5 + return %4 : $() // id: %5 +} + +sil @getGenericClosure_closure : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> @out T + +sil [noinline] @getGenericClosure : $@convention(thin) (@in T) -> @owned @callee_owned () -> @out T { +bb0(%0 : $*T): + debug_value_addr %0 : $*T, let, name "t" // id: %1 + %2 = function_ref @getGenericClosure_closure : $@convention(thin) <τ_0_0> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0 // user: %5 + %3 = alloc_box $<τ_0_0> { var τ_0_0 } // users: %4, %5, %5 + %3a = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 + copy_addr %0 to [initialization] %3a : $*T // id: %4 + %5 = partial_apply %2(%3) : $@convention(thin) <τ_0_0> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0 // user: %7 + destroy_addr %0 : $*T // id: %6 + return %5 : $@callee_owned () -> @out T // id: %7 +} + +// CHECK-LABEL: sil shared [noinline] @$s17getGenericClosure1A19exportsOpaqueReturnQryFQOyQo__Tg5 : $@convention(thin) (Int) -> @owned @callee_owned () -> @out Int { +// CHECK: alloc_box $<τ_0_0> { var τ_0_0 } +// CHECK: } // end sil function '$s17getGenericClosure1A19exportsOpaqueReturnQryFQOyQo__Tg5' + +sil [transparent] [serialized] @specializePartialApplies : $@convention(thin) (@in SomeButt) -> () { +bb0(%0 : $*SomeButt): + %5 = function_ref @getGenericClosure : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @owned @callee_owned () -> @out τ_0_0 + %8 = apply %5(%0) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @owned @callee_owned () -> @out τ_0_0 + %15 = tuple() + return %15 : $() +} From b9ade70c5d3f9973ba2358655912e970451b1d9b Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Fri, 16 Oct 2020 12:55:14 -0700 Subject: [PATCH 509/745] swift_build_sdk_interfaces.py: create directory under dry-run mode Users may install a different version of SDK from the one found in the originally downloaded toolchain. Thus, we may need to create a new directory structure instead of using the original one in the toolchain, especially if the SDK under use has a different version number. rdar://67951012 --- utils/swift_build_sdk_interfaces.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/utils/swift_build_sdk_interfaces.py b/utils/swift_build_sdk_interfaces.py index 140afabdb9d3d..7ffa954246cd2 100755 --- a/utils/swift_build_sdk_interfaces.py +++ b/utils/swift_build_sdk_interfaces.py @@ -112,9 +112,7 @@ def run_command(args, dry_run): raise -def make_dirs_if_needed(path, dry_run): - if dry_run: - return +def make_dirs_if_needed(path): try: os.makedirs(path) except OSError as e: @@ -232,7 +230,7 @@ def log_output_to_file(content, module_name, interface_base, label, log_path): return if not content: return - make_dirs_if_needed(log_path, dry_run=False) + make_dirs_if_needed(log_path) log_name = module_name + "-" + interface_base + "-" + label + ".txt" with open(os.path.join(log_path, log_name), "w") as output_file: output_file.write(content) @@ -296,7 +294,7 @@ def process_module(module_file): module_file.name + ".swiftmodule") if interface_base != module_file.name: - make_dirs_if_needed(output_path, args.dry_run) + make_dirs_if_needed(output_path) output_path = os.path.join(output_path, interface_base + ".swiftmodule") @@ -408,7 +406,7 @@ def main(): with open(args.xfails) as xfails_file: xfails = json.load(xfails_file) - make_dirs_if_needed(args.output_dir, args.dry_run) + make_dirs_if_needed(args.output_dir) # Copy a file containing SDK build version into the prebuilt module dir, # so we can keep track of the SDK version we built from. From 883d583a2fc6e5c74a58b379ba19a5d2907dabdd Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Fri, 16 Oct 2020 18:32:20 -0300 Subject: [PATCH 510/745] [CSSimplify] Make sure visit and record holes recursivelly for dependent member type --- include/swift/Sema/ConstraintSystem.h | 3 +-- lib/Sema/CSSimplify.cpp | 15 +++++---------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index bf0b921eac731..379bf149caa8a 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -3158,8 +3158,7 @@ class ConstraintSystem { /// subsequent solution would be worse than the best known solution. bool recordFix(ConstraintFix *fix, unsigned impact = 1); - void recordPotentialHole(TypeVariableType *typeVar); - void recordPotentialHole(FunctionType *fnType); + void recordPotentialHole(Type type); void recordTrailingClosureMatch( ConstraintLocator *locator, diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index f56558cdf3cc6..b6e5d7bd2a3aa 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -9913,16 +9913,11 @@ bool ConstraintSystem::recordFix(ConstraintFix *fix, unsigned impact) { return false; } -void ConstraintSystem::recordPotentialHole(TypeVariableType *typeVar) { - assert(typeVar); - typeVar->getImpl().enableCanBindToHole(getSavedBindings()); -} - -void ConstraintSystem::recordPotentialHole(FunctionType *fnType) { - assert(fnType); - Type(fnType).visit([&](Type type) { +void ConstraintSystem::recordPotentialHole(Type type) { + assert(type->hasTypeVariable()); + type.visit([&](Type type) { if (auto *typeVar = type->getAs()) - recordPotentialHole(typeVar); + typeVar->getImpl().enableCanBindToHole(getSavedBindings()); }); } @@ -10024,7 +10019,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( newTupleTypes.push_back(smallerElt); } else { if (largerElt.getType()->isTypeVariableOrMember()) - recordPotentialHole(largerElt.getType()->getAs()); + recordPotentialHole(largerElt.getType()); } } auto matchingType = From 885815227f6c1321e48fa2a2300171306719c6fc Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Fri, 16 Oct 2020 18:32:30 -0300 Subject: [PATCH 511/745] [tests] Add regression tests for SR-13732 --- test/Constraints/lvalues.swift | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/Constraints/lvalues.swift b/test/Constraints/lvalues.swift index 7c7d4069ec384..165c752f7db21 100644 --- a/test/Constraints/lvalues.swift +++ b/test/Constraints/lvalues.swift @@ -241,3 +241,28 @@ func wump(to: T, _ body: (G) -> ()) {} wump(to: 0, { $0[] = 0 }) // expected-error@-1 {{missing argument for parameter #1 in call}} + +// SR-13732 +extension MutableCollection { + public mutating func writePrefix(from source: inout I) + -> (writtenCount: Int, afterLastWritten: Index) + where I.Element == Element + { + fatalError() + } + + public mutating func writePrefix(from source: Source) + -> (writtenCount: Int, afterLastWritten: Index, afterLastRead: Source.Index) + where Source.Element == Element + { + fatalError() + } + +} + +func testWritePrefixIterator() { + var a = Array(0..<10) + + var underflow = (1..<10).makeIterator() + var (writtenCount, afterLastWritten) = a.writePrefix(from: underflow) // expected-error {{passing value of type 'IndexingIterator<(Range)>' to an inout parameter requires explicit '&'}} {{62-62=&}} +} From 23cde90b29bb2a0cf02dfee9fe45e7a5c36ec7b4 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Fri, 16 Oct 2020 14:41:14 -0700 Subject: [PATCH 512/745] Update the Xcode version in How to Guides --- docs/HowToGuides/FAQ.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/HowToGuides/FAQ.md b/docs/HowToGuides/FAQ.md index d5cc17a363f29..7889fa317c4bc 100644 --- a/docs/HowToGuides/FAQ.md +++ b/docs/HowToGuides/FAQ.md @@ -42,7 +42,7 @@ built compiler to compile both packages and Xcode projects. Create a build setting `SWIFT_EXEC` with the value set to `/path/to/swiftc`. If you now do a clean build, your locally built compiler will be used. - At the time of writing, in the latest Xcode 12 beta, `SWIFT_EXEC` does not + At the time of writing, in the latest Xcode 12.2 beta 3, `SWIFT_EXEC` does not work for SwiftPM integration inside Xcode, so this will not work for Xcode projects that depend on SwiftPM packages. From bd6cc586d5c8d826c798993e21e86477b70805db Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Fri, 16 Oct 2020 14:42:42 -0700 Subject: [PATCH 513/745] Update the Xcode version in Getting Started doc --- docs/HowToGuides/GettingStarted.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/HowToGuides/GettingStarted.md b/docs/HowToGuides/GettingStarted.md index 8db9a0db716d9..98eb50c093400 100644 --- a/docs/HowToGuides/GettingStarted.md +++ b/docs/HowToGuides/GettingStarted.md @@ -126,7 +126,7 @@ Double-check that running `pwd` prints a path ending with `swift`. ### macOS -1. Install [Xcode 12 beta 3][Xcode] or newer: +1. Install [Xcode 12.2 beta 3][Xcode] or newer: The required version of Xcode changes frequently and is often a beta release. Check this document or the host information on for the current required version. From 85ff15acd30f6b1b6e5ead370ec9d99e6a67b0ab Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Sun, 5 Apr 2020 18:53:24 -0700 Subject: [PATCH 514/745] Add indexTrieRoot to the SILModule to share across Analyses. ...and avoid reallocation. This is immediately necessary for LICM, in addition to its current uses. I suspect this could be used by many passes that work with addresses. RLE/DSE should absolutely migrate to it. --- .../{SILOptimizer/Utils => Basic}/IndexTrie.h | 1 + include/swift/SIL/SILModule.h | 7 +++++++ .../SILOptimizer/Analysis/AccessSummaryAnalysis.h | 15 +++------------ lib/SIL/IR/SILModule.cpp | 3 ++- .../Analysis/AccessSummaryAnalysis.cpp | 11 +++++------ .../Mandatory/DiagnoseStaticExclusivity.cpp | 2 +- lib/SILOptimizer/Transforms/CopyPropagation.cpp | 2 +- .../Transforms/DeadObjectElimination.cpp | 2 +- 8 files changed, 21 insertions(+), 22 deletions(-) rename include/swift/{SILOptimizer/Utils => Basic}/IndexTrie.h (98%) diff --git a/include/swift/SILOptimizer/Utils/IndexTrie.h b/include/swift/Basic/IndexTrie.h similarity index 98% rename from include/swift/SILOptimizer/Utils/IndexTrie.h rename to include/swift/Basic/IndexTrie.h index b7986cb4d2e22..9428ff2e87c8f 100644 --- a/include/swift/SILOptimizer/Utils/IndexTrie.h +++ b/include/swift/Basic/IndexTrie.h @@ -13,6 +13,7 @@ #ifndef SWIFT_SILOPTIMIZER_UTILS_INDEXTREE_H #define SWIFT_SILOPTIMIZER_UTILS_INDEXTREE_H +#include "swift/Basic/LLVM.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index 33f7b07ff875f..162606ab5a747 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -21,6 +21,7 @@ #include "swift/AST/Builtins.h" #include "swift/AST/SILLayout.h" #include "swift/AST/SILOptions.h" +#include "swift/Basic/IndexTrie.h" #include "swift/Basic/LangOptions.h" #include "swift/Basic/ProfileCounter.h" #include "swift/Basic/Range.h" @@ -256,6 +257,10 @@ class SILModule { /// The indexed profile data to be used for PGO, or nullptr. std::unique_ptr PGOReader; + /// A trie of integer indices that gives pointer identity to a path of + /// projections, shared between all functions in the module. + std::unique_ptr indexTrieRoot; + /// The options passed into this SILModule. const SILOptions &Options; @@ -655,6 +660,8 @@ class SILModule { PGOReader = std::move(IPR); } + IndexTrieNode *getIndexTrieRoot() { return indexTrieRoot.get(); } + /// Can value operations (copies and destroys) on the given lowered type /// be performed in this module? bool isTypeABIAccessible(SILType type, diff --git a/include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h b/include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h index e71b6c58fea36..d8a448e7ee85a 100644 --- a/include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h @@ -19,10 +19,10 @@ #ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ACCESS_SUMMARY_ANALYSIS_H_ #define SWIFT_SILOPTIMIZER_ANALYSIS_ACCESS_SUMMARY_ANALYSIS_H_ +#include "swift/Basic/IndexTrie.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" #include "swift/SILOptimizer/Analysis/BottomUpIPAnalysis.h" -#include "swift/SILOptimizer/Utils/IndexTrie.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" @@ -173,22 +173,13 @@ class AccessSummaryAnalysis : public BottomUpIPAnalysis { llvm::SpecificBumpPtrAllocator Allocator; - /// A trie of integer indices that gives pointer identity to a path of - /// projections. This is shared between all functions in the module. - std::unique_ptr SubPathTrie; - public: - AccessSummaryAnalysis() : BottomUpIPAnalysis(SILAnalysisKind::AccessSummary) { - SubPathTrie.reset(new IndexTrieNode()); - } + AccessSummaryAnalysis() + : BottomUpIPAnalysis(SILAnalysisKind::AccessSummary) {} /// Returns a summary of the accesses performed by the given function. const FunctionSummary &getOrCreateSummary(SILFunction *Fn); - IndexTrieNode *getSubPathTrieRoot() { - return SubPathTrie.get(); - } - /// Returns an IndexTrieNode that represents the single subpath accessed from /// BAI or the root if no such node exists. const IndexTrieNode *findSubPathAccessed(BeginAccessInst *BAI); diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 3c15b8f2ec1d3..cc30fc7f5d07b 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -94,7 +94,8 @@ class SILModule::SerializationCallback final SILModule::SILModule(llvm::PointerUnion context, Lowering::TypeConverter &TC, const SILOptions &Options) - : Stage(SILStage::Raw), Options(Options), serialized(false), + : Stage(SILStage::Raw), indexTrieRoot(new IndexTrieNode()), + Options(Options), serialized(false), regDeserializationNotificationHandlerForNonTransparentFuncOME(false), regDeserializationNotificationHandlerForAllFuncOME(false), SerializeSILAction(), Types(TC) { diff --git a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp index d3906e4af74d2..0d0a7de0fda58 100644 --- a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp @@ -49,9 +49,9 @@ void AccessSummaryAnalysis::processFunction(FunctionInfo *info, /// started by a begin_access and any flows of the arguments to other /// functions. void AccessSummaryAnalysis::processArgument(FunctionInfo *info, - SILFunctionArgument *argument, - ArgumentSummary &summary, - FunctionOrder &order) { + SILFunctionArgument *argument, + ArgumentSummary &summary, + FunctionOrder &order) { unsigned argumentIndex = argument->getIndex(); // Use a worklist to track argument uses to be processed. @@ -77,7 +77,7 @@ void AccessSummaryAnalysis::processArgument(FunctionInfo *info, // call. if (!callee || callee->empty()) { summary.mergeWith(SILAccessKind::Modify, apply.getLoc(), - getSubPathTrieRoot()); + apply.getModule().getIndexTrieRoot()); continue; } unsigned operandNumber = operand->getOperandNumber(); @@ -468,7 +468,6 @@ AccessSummaryAnalysis::getOrCreateSummary(SILFunction *fn) { void AccessSummaryAnalysis::AccessSummaryAnalysis::invalidate() { FunctionInfos.clear(); Allocator.DestroyAll(); - SubPathTrie.reset(new IndexTrieNode()); } void AccessSummaryAnalysis::invalidate(SILFunction *F, InvalidationKind K) { @@ -526,7 +525,7 @@ getSingleAddressProjectionUser(SingleValueInstruction *I) { const IndexTrieNode * AccessSummaryAnalysis::findSubPathAccessed(BeginAccessInst *BAI) { - IndexTrieNode *SubPath = getSubPathTrieRoot(); + IndexTrieNode *SubPath = BAI->getModule().getIndexTrieRoot(); // For each single-user projection of BAI, construct or get a node // from the trie representing the index of the field or tuple element diff --git a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp index f27984e9ec524..eace1f0eac6bb 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp @@ -754,7 +754,7 @@ static void checkCaptureAccess(ApplySite Apply, AccessState &State) { // The unknown argument access is considered a modify of the root subpath. auto argAccess = RecordedAccess(SILAccessKind::Modify, Apply.getLoc(), - State.ASA->getSubPathTrieRoot()); + Apply.getModule().getIndexTrieRoot()); // Construct a conflicting RecordedAccess if one doesn't already exist. const AccessInfo &info = AccessIt->getSecond(); diff --git a/lib/SILOptimizer/Transforms/CopyPropagation.cpp b/lib/SILOptimizer/Transforms/CopyPropagation.cpp index 82bb2269f0d4f..fdb66d4055c72 100644 --- a/lib/SILOptimizer/Transforms/CopyPropagation.cpp +++ b/lib/SILOptimizer/Transforms/CopyPropagation.cpp @@ -123,12 +123,12 @@ /// ===----------------------------------------------------------------------=== #define DEBUG_TYPE "copy-propagation" +#include "swift/Basic/IndexTrie.h" #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/Projection.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/CFGOptUtils.h" -#include "swift/SILOptimizer/Utils/IndexTrie.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Statistic.h" diff --git a/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp b/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp index 384d7291e07d5..58a47795051b7 100644 --- a/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp @@ -24,6 +24,7 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "dead-object-elim" +#include "swift/Basic/IndexTrie.h" #include "swift/AST/ResilienceExpansion.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/DebugUtils.h" @@ -38,7 +39,6 @@ #include "swift/SILOptimizer/Analysis/ArraySemantic.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" -#include "swift/SILOptimizer/Utils/IndexTrie.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/SILOptimizer/Utils/SILSSAUpdater.h" #include "swift/SILOptimizer/Utils/ValueLifetime.h" From 7774f06f10eabe16ef6b157f92e8fab5e9e5f029 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 27 Aug 2020 17:35:10 -0700 Subject: [PATCH 515/745] Change IndexTrie to allow signed indices --- include/swift/Basic/IndexTrie.h | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/include/swift/Basic/IndexTrie.h b/include/swift/Basic/IndexTrie.h index 9428ff2e87c8f..e9a5133a67162 100644 --- a/include/swift/Basic/IndexTrie.h +++ b/include/swift/Basic/IndexTrie.h @@ -22,15 +22,18 @@ namespace swift { // Trie node representing a sequence of unsigned integer indices. class IndexTrieNode { - static const unsigned RootIdx = ~0U; - unsigned Index; +public: + static const int RootIndex = std::numeric_limits::min(); + +private: + int Index; llvm::SmallVector Children; IndexTrieNode *Parent; public: - IndexTrieNode(): Index(RootIdx), Parent(nullptr) {} + IndexTrieNode() : Index(RootIndex), Parent(nullptr) {} - explicit IndexTrieNode(unsigned V, IndexTrieNode *P): Index(V), Parent(P) {} + explicit IndexTrieNode(int V, IndexTrieNode *P) : Index(V), Parent(P) {} IndexTrieNode(IndexTrieNode &) =delete; IndexTrieNode &operator=(const IndexTrieNode&) =delete; @@ -40,19 +43,18 @@ class IndexTrieNode { delete N; } - bool isRoot() const { return Index == RootIdx; } + bool isRoot() const { return Index == RootIndex; } bool isLeaf() const { return Children.empty(); } - unsigned getIndex() const { return Index; } + int getIndex() const { return Index; } - IndexTrieNode *getChild(unsigned Idx) { - assert(Idx != RootIdx); + IndexTrieNode *getChild(int Idx) { + assert(Idx != RootIndex); - auto I = std::lower_bound(Children.begin(), Children.end(), Idx, - [](IndexTrieNode *a, unsigned i) { - return a->Index < i; - }); + auto I = + std::lower_bound(Children.begin(), Children.end(), Idx, + [](IndexTrieNode *a, int i) { return a->Index < i; }); if (I != Children.end() && (*I)->Index == Idx) return *I; auto *N = new IndexTrieNode(Idx, this); From 2767b51d615d5fcf464557ef404c491f4259e262 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Sun, 5 Apr 2020 18:51:43 -0700 Subject: [PATCH 516/745] Change Projection to support signed indices. Change ProjectionIndex for ref_tail_addr to std::numeric_limits::max(); This is necessary to disambiguate the tail elements from ref_element_addr field zero. --- include/swift/SIL/Projection.h | 39 +++++++------- lib/SIL/Utils/Projection.cpp | 53 ++++++++++--------- lib/SILOptimizer/LoopTransforms/ArrayOpt.h | 6 +-- .../LoopTransforms/ArrayPropertyOpt.cpp | 2 +- .../LoopTransforms/COWArrayOpt.cpp | 2 +- 5 files changed, 51 insertions(+), 51 deletions(-) diff --git a/include/swift/SIL/Projection.h b/include/swift/SIL/Projection.h index c78c11ed7e2a6..ece22804b153c 100644 --- a/include/swift/SIL/Projection.h +++ b/include/swift/SIL/Projection.h @@ -67,9 +67,9 @@ inline bool isStrictSubSeqRelation(SubSeqRelation_t Seq) { /// Extract an integer index from a SILValue. /// -/// Return true if IndexVal is a constant index representable as unsigned +/// Return true if IndexVal is a constant index representable as an /// int. We do not support symbolic projections yet. -bool getIntegerIndex(SILValue IndexVal, unsigned &IndexConst); +bool getIntegerIndex(SILValue IndexVal, int &IndexConst); /// The kind of projection that we are representing. /// @@ -136,11 +136,18 @@ static inline bool isCastProjectionKind(ProjectionKind Kind) { /// that immediately contains it. /// /// This lightweight utility maps a SIL address projection to an index. +/// +/// project_box does not have a projection index. At the SIL level, the box +/// storage is considered part of the same object as the. The box projection is +/// does not affect access path so that box projections can occur on distinct +/// phi paths in the address def-use chain. struct ProjectionIndex { + static constexpr int TailIndex = std::numeric_limits::max(); + SILValue Aggregate; - unsigned Index; + int Index = std::numeric_limits::min(); - explicit ProjectionIndex(SILValue V) : Index(~0U) { + explicit ProjectionIndex(SILValue V) { switch (V->getKind()) { default: break; @@ -163,16 +170,9 @@ struct ProjectionIndex { break; } case ValueKind::RefTailAddrInst: { - RefTailAddrInst *REA = cast(V); - Index = 0; - Aggregate = REA->getOperand(); - break; - } - case ValueKind::ProjectBoxInst: { - ProjectBoxInst *PBI = cast(V); - // A box has only a single payload. - Index = 0; - Aggregate = PBI->getOperand(); + RefTailAddrInst *RTA = cast(V); + Index = TailIndex; + Aggregate = RTA->getOperand(); break; } case ValueKind::TupleElementAddrInst: { @@ -233,8 +233,7 @@ class Projection { : Projection(dyn_cast(I)) {} explicit Projection(SingleValueInstruction *I); - Projection(ProjectionKind Kind, unsigned NewIndex) - : Value(Kind, NewIndex) {} + Projection(ProjectionKind Kind, int NewIndex) : Value(Kind, NewIndex) {} Projection(ProjectionKind Kind, TypeBase *Ptr) : Value(Kind, Ptr) {} @@ -252,10 +251,8 @@ class Projection { /// Convenience method for getting the underlying index. Assumes that this /// projection is valid. Otherwise it asserts. - unsigned getIndex() const { - return Value.getIndex(); - } - + int getIndex() const { return Value.getIndex(); } + unsigned getHash() const { return (unsigned)Value.getStorage(); } /// Determine if I is a value projection instruction whose corresponding @@ -359,7 +356,7 @@ class Projection { return nullptr; case ValueKind::IndexAddrInst: { auto *i = cast(v); - unsigned scalar; + int scalar; if (getIntegerIndex(i->getIndex(), scalar)) return i; return nullptr; diff --git a/lib/SIL/Utils/Projection.cpp b/lib/SIL/Utils/Projection.cpp index e94adb426d6ac..8c6b783c842cb 100644 --- a/lib/SIL/Utils/Projection.cpp +++ b/lib/SIL/Utils/Projection.cpp @@ -12,10 +12,11 @@ #define DEBUG_TYPE "sil-projection" #include "swift/SIL/Projection.h" +#include "swift/Basic/IndexTrie.h" #include "swift/Basic/NullablePtr.h" -#include "swift/SIL/SILBuilder.h" -#include "swift/SIL/InstructionUtils.h" #include "swift/SIL/DebugUtils.h" +#include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILUndef.h" #include "llvm/ADT/None.h" #include "llvm/Support/Debug.h" @@ -42,22 +43,27 @@ static_assert(std::is_standard_layout::value, /// Return true if IndexVal is a constant index representable as unsigned /// int. We do not support symbolic projections yet, only 32-bit unsigned /// integers. -bool swift::getIntegerIndex(SILValue IndexVal, unsigned &IndexConst) { - if (auto *IndexLiteral = dyn_cast(IndexVal)) { - APInt ConstInt = IndexLiteral->getValue(); - // IntegerLiterals are signed. - if (ConstInt.isIntN(32) && ConstInt.isNonNegative()) { - IndexConst = (unsigned)ConstInt.getSExtValue(); - return true; - } - } - return false; +bool swift::getIntegerIndex(SILValue IndexVal, int &IndexConst) { + auto *IndexLiteral = dyn_cast(IndexVal); + if (!IndexLiteral) + return false; + + APInt ConstInt = IndexLiteral->getValue(); + // Reserve 1 bit for encoding. See AccessPath::Index. + if (!ConstInt.isSignedIntN(31)) + return false; + + IndexConst = ConstInt.getSExtValue(); + assert(((IndexConst << 1) >> 1) == IndexConst); + return true; } //===----------------------------------------------------------------------===// // Projection //===----------------------------------------------------------------------===// +constexpr int ProjectionIndex::TailIndex; + Projection::Projection(SingleValueInstruction *I) : Value() { if (!I) return; @@ -72,21 +78,21 @@ Projection::Projection(SingleValueInstruction *I) : Value() { auto *SEAI = cast(I); Value = ValueTy(ProjectionKind::Struct, SEAI->getFieldIndex()); assert(getKind() == ProjectionKind::Struct); - assert(getIndex() == SEAI->getFieldIndex()); + assert(getIndex() == int(SEAI->getFieldIndex())); break; } case SILInstructionKind::StructExtractInst: { auto *SEI = cast(I); Value = ValueTy(ProjectionKind::Struct, SEI->getFieldIndex()); assert(getKind() == ProjectionKind::Struct); - assert(getIndex() == SEI->getFieldIndex()); + assert(getIndex() == int(SEI->getFieldIndex())); break; } case SILInstructionKind::RefElementAddrInst: { auto *REAI = cast(I); Value = ValueTy(ProjectionKind::Class, REAI->getFieldIndex()); assert(getKind() == ProjectionKind::Class); - assert(getIndex() == REAI->getFieldIndex()); + assert(getIndex() == int(REAI->getFieldIndex())); break; } case SILInstructionKind::RefTailAddrInst: { @@ -108,28 +114,28 @@ Projection::Projection(SingleValueInstruction *I) : Value() { auto *TEI = cast(I); Value = ValueTy(ProjectionKind::Tuple, TEI->getFieldIndex()); assert(getKind() == ProjectionKind::Tuple); - assert(getIndex() == TEI->getFieldIndex()); + assert(getIndex() == int(TEI->getFieldIndex())); break; } case SILInstructionKind::TupleElementAddrInst: { auto *TEAI = cast(I); Value = ValueTy(ProjectionKind::Tuple, TEAI->getFieldIndex()); assert(getKind() == ProjectionKind::Tuple); - assert(getIndex() == TEAI->getFieldIndex()); + assert(getIndex() == int(TEAI->getFieldIndex())); break; } case SILInstructionKind::UncheckedEnumDataInst: { auto *UEDI = cast(I); Value = ValueTy(ProjectionKind::Enum, UEDI->getElementNo()); assert(getKind() == ProjectionKind::Enum); - assert(getIndex() == UEDI->getElementNo()); + assert(getIndex() == int(UEDI->getElementNo())); break; } case SILInstructionKind::UncheckedTakeEnumDataAddrInst: { auto *UTEDAI = cast(I); Value = ValueTy(ProjectionKind::Enum, UTEDAI->getElementNo()); assert(getKind() == ProjectionKind::Enum); - assert(getIndex() == UTEDAI->getElementNo()); + assert(getIndex() == int(UTEDAI->getElementNo())); break; } case SILInstructionKind::IndexAddrInst: { @@ -138,9 +144,10 @@ Projection::Projection(SingleValueInstruction *I) : Value() { // updated and a MaxLargeIndex will need to be used here. Currently we // represent large Indexes using a 64 bit integer, so we don't need to mess // with anything. - unsigned NewIndex = 0; + int NewIndex = 0; auto *IAI = cast(I); - if (getIntegerIndex(IAI->getIndex(), NewIndex)) { + // TODO: handle negative indices + if (getIntegerIndex(IAI->getIndex(), NewIndex) && NewIndex >= 0) { Value = ValueTy(ProjectionKind::Index, NewIndex); assert(getKind() == ProjectionKind::Index); assert(getIndex() == NewIndex); @@ -321,10 +328,6 @@ void Projection::getFirstLevelProjections( if (auto *C = Ty.getClassOrBoundGenericClass()) { unsigned Count = 0; - for (auto *superDecl = C->getSuperclassDecl(); superDecl != nullptr; - superDecl = superDecl->getSuperclassDecl()) { - Count += superDecl->getStoredProperties().size(); - } for (auto *VDecl : C->getStoredProperties()) { (void) VDecl; Projection P(ProjectionKind::Class, Count++); diff --git a/lib/SILOptimizer/LoopTransforms/ArrayOpt.h b/lib/SILOptimizer/LoopTransforms/ArrayOpt.h index 00bf1e0d9b492..2139d2a5aabec 100644 --- a/lib/SILOptimizer/LoopTransforms/ArrayOpt.h +++ b/lib/SILOptimizer/LoopTransforms/ArrayOpt.h @@ -63,7 +63,7 @@ class StructUseCollector { /// Do not form a path with an IndexAddrInst because we have no way to /// distinguish between indexing and subelement access. The same index could /// either refer to the next element (indexed) or a subelement. - static SILValue getAccessPath(SILValue V, SmallVectorImpl& Path) { + static SILValue getAccessPath(SILValue V, SmallVectorImpl &Path) { V = stripCasts(V); if (auto *IA = dyn_cast(V)) { // Don't include index_addr projections in the access path. We could if @@ -89,7 +89,7 @@ class StructUseCollector { VisitedSet Visited; /// Collect all uses of the value at the given address. - void collectUses(ValueBase *V, ArrayRef AccessPath) { + void collectUses(ValueBase *V, ArrayRef AccessPath) { // Save our old indent and increment. // Collect all users of the address and loads. collectAddressUses(V, AccessPath, nullptr); @@ -142,7 +142,7 @@ class StructUseCollector { /// StructVal is invalid, then the value is the address of the Struct. If /// StructVal is valid, the value is the address of an element within the /// Struct. - void collectAddressUses(ValueBase *V, ArrayRef AccessPathSuffix, + void collectAddressUses(ValueBase *V, ArrayRef AccessPathSuffix, Operand *StructVal) { for (auto *UI : V->getUses()) { // Keep the operand, not the instruction in the visited set. The same diff --git a/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp b/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp index 76a30bfaca3d2..9e9334ff59061 100644 --- a/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp +++ b/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp @@ -402,7 +402,7 @@ class ArrayPropertiesAnalysis { if (!Call.canHoist(Preheader->getTerminator(), DomTree)) return false; - SmallVector AccessPath; + SmallVector AccessPath; SILValue ArrayContainer = StructUseCollector::getAccessPath(Arr, AccessPath); diff --git a/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp b/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp index 1141d8dd7ea6d..ed54541cfb107 100644 --- a/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp +++ b/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp @@ -843,7 +843,7 @@ bool COWArrayOpt::hoistMakeMutable(ArraySemanticsCall MakeMutable, return false; } - SmallVector AccessPath; + SmallVector AccessPath; SILValue ArrayContainer = StructUseCollector::getAccessPath(CurrentArrayAddr, AccessPath); bool arrayContainerIsUnique = checkUniqueArrayContainer(ArrayContainer); From 7554a6aa3134b8b6078aac4ad21b58871a55d757 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Wed, 26 Aug 2020 22:23:27 -0700 Subject: [PATCH 517/745] Fix a pointer addition bug in SwiftReflectionTest. --- stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift b/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift index b077bd4923a2e..cb2350b845f2c 100644 --- a/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift +++ b/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift @@ -216,7 +216,7 @@ internal struct ReflectionInfo : Sequence { } internal func sendBytes(from address: UnsafePointer, count: Int) { - var source = address + var source = UnsafeRawPointer(address) var bytesLeft = count debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") } while bytesLeft > 0 { From 92a181671ea7a622286281a81a97a0b2f032c76d Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 27 Aug 2020 17:38:35 -0700 Subject: [PATCH 518/745] Fix ValueTracking isUniquelyIdentified to use AccessedStorage. To clarify and unify logic, improve precision, and behave consistently with other code that does the same thing. --- .../SILOptimizer/Analysis/ValueTracking.h | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/include/swift/SILOptimizer/Analysis/ValueTracking.h b/include/swift/SILOptimizer/Analysis/ValueTracking.h index 2ab2715102cbb..32569f2f25bbf 100644 --- a/include/swift/SILOptimizer/Analysis/ValueTracking.h +++ b/include/swift/SILOptimizer/Analysis/ValueTracking.h @@ -54,9 +54,21 @@ bool pointsToLocalObject(SILValue V); /// - an address projection based on an exclusive argument with no levels of /// indirection (e.g. ref_element_addr, project_box, etc.). inline bool isUniquelyIdentified(SILValue V) { - return pointsToLocalObject(V) - || (V->getType().isAddress() - && isExclusiveArgument(getAccessedAddress(V))); + SILValue objectRef = V; + if (V->getType().isAddress()) { + auto storage = findAccessedStorage(V); + if (!storage) + return false; + + if (storage.isUniquelyIdentifiedAfterEnforcement()) + return true; + + if (!storage.isObjectAccess()) + return false; + + objectRef = storage.getObject(); + } + return pointsToLocalObject(objectRef); } enum class IsZeroKind { From cc0aa2f8b8740221420ac35e86d7e67cbcd8529b Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 27 Aug 2020 17:48:16 -0700 Subject: [PATCH 519/745] Add an AccessPath abstraction and formalize memory access Things that have come up recently but are somewhat blocked on this: - Moving AccessMarkerElimination down in the pipeline - SemanticARCOpts correctness and improvements - AliasAnalysis improvements - LICM performance regressions - RLE/DSE improvements Begin to formalize the model for valid memory access in SIL. Ignoring ownership, every access is a def-use chain in three parts: object root -> formal access base -> memory operation address AccessPath abstracts over this path and standardizes the identity of a memory access throughout the optimizer. This abstraction is the basis for a new AccessPathVerification. With that verification, we now have all the properties we need for the type of analysis requires for exclusivity enforcement, but now generalized for any memory analysis. This is suitable for an extremely lightweight analysis with no side data structures. We currently have a massive amount of ad-hoc memory analysis throughout SIL, which is incredibly unmaintainable, bug-prone, and not performance-robust. We can begin taking advantage of this verifably complete model to solve that problem. The properties this gives us are: Access analysis must be complete over memory operations: every memory operation needs a recognizable valid access. An access can be unidentified only to the extent that it is rooted in some non-address type and we can prove that it is at least *not* part of an access to a nominal class or global property. Pointer provenance is also required for future IRGen-level bitfield optimizations. Access analysis must be complete over address users: for an identified object root all memory accesses including subobjects must be discoverable. Access analysis must be symmetric: use-def and def-use analysis must be consistent. AccessPath is merely a wrapper around the existing accessed-storage utilities and IndexTrieNode. Existing passes already very succesfully use this approach, but in an ad-hoc way. With a general utility we can: - update passes to use this approach to identify memory access, reducing the space and time complexity of those algorithms. - implement an inexpensive on-the-fly, debug mode address lifetime analysis - implement a lightweight debug mode alias analysis - ultimately improve the power, efficiency, and maintainability of full alias analysis - make our type-based alias analysis sensistive to the access path --- include/swift/SIL/MemAccessUtils.h | 784 +++++++--- include/swift/SIL/PatternMatch.h | 10 + .../SILOptimizer/Analysis/ValueTracking.h | 9 +- lib/IRGen/IRGenSIL.cpp | 2 +- lib/SIL/Utils/MemAccessUtils.cpp | 1280 ++++++++++++++--- .../LoadBorrowInvalidationChecker.cpp | 40 +- lib/SILOptimizer/Analysis/AliasAnalysis.cpp | 2 +- lib/SILOptimizer/Analysis/MemoryBehavior.cpp | 13 +- lib/SILOptimizer/LoopTransforms/LICM.cpp | 2 +- .../Mandatory/DiagnoseStaticExclusivity.cpp | 10 +- 10 files changed, 1742 insertions(+), 410 deletions(-) diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index dd198db2b6814..a65effe525448 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -22,20 +22,40 @@ /// To verify access markers, SIL checks that all memory operations either have /// an address that originates in begin_access, or originates from a pattern /// that is recognized as a non-formal-access. This implies that every SIL -/// memory operation has a recognizable address source. +/// memory operation has a recognizable address source. Given the address of a +/// memory operation, there are three levels of APIs that inspect the origin of +/// that address: /// -/// If the memory operation is part of a formal access, then getAddressAccess() -/// returns the begin_access marker. +/// 1. getAccessAddress(): Find the originating address as close as possible to +/// the address of the formal access *without* looking past any storage +/// casts. This is useful when the type of the returned access address must be +/// consistent with the memory operation's type (the same type or a parent +/// type). For a formal access, this typically returns the begin_access, but it +/// is not guaranteed to because some accesses contain storage casts. For +/// non-formal access, it returns a best-effort address corresponding to the +/// base of an access. /// -/// AccessedStorage identifies the storage location of a memory access. +/// 2. getAccessBegin(): If the memory operation is part of a formal access, +/// then this is guaranteed to return the begin_access marker. Otherwise, it +/// returns the best-effort address or pointer corresponding to the base of an +/// access. Useful to find the scope of a formal access. /// -/// identifyFormalAccess() returns the formally accessed storage of a -/// begin_access instruction. This must return a valid AccessedStorage value -/// unless the access has "Unsafe" enforcement. The formal access location may -/// be nested within an outer begin_access. For the purpose of exclusivity, -/// nested accesses are considered distinct formal accesses so they return -/// distinct AccessedStorage values even though they may access the same -/// memory. +/// 3. getAccessBase(): Find the ultimate base of any address corresponding to +/// the accessed object, regardless of whether the address is nested within +/// access scopes, and regardless of any storage casts. This returns either an +/// address type, pointer type, or box type, but never a reference type. +/// Each object's property or its tail storage is separately accessed. +/// +/// For better identification an access base, use findAccessedStorage(). It +/// returns an AccessedStorage value that identifies the storage location of a +/// memory access. It provides APIs for inspecting type of accessed storage and +/// allows for disambiguation between different types of storage and different +/// properties within a class. +/// +/// findAccessedStorage() follows the same logic as getAccessBase(), but if the +/// base is not recognized as a valid access, it returns invalid +/// AccessedStorage. It also performs further analysis to determine the root +/// reference of an object access. /// /// findAccessedStorage() returns the outermost AccessedStorage for any memory /// address. It can be called on the address of a memory operation, the address @@ -50,19 +70,28 @@ /// formal access. /// /// The AccessEnforcementWMO pass is an example of an optimistic optimization -/// that relies on the above requirements for correctness. If -/// findAccessedStorage() simply bailed out on an unrecognized memory address by -/// returning an invalid AccessedStorage, then the optimization could make -/// incorrect assumptions about the absence of access to globals or class -/// properties. +/// that relies on this requirement for correctness. If findAccessedStorage() +/// simply bailed out on an unrecognized memory address by returning an invalid +/// AccessedStorage, then the optimization could make incorrect assumptions +/// about the absence of access to globals or class properties. +/// +/// identifyFormalAccess() is similar to findAccessedStorage(), but returns the +/// formally accessed storage of a begin_access instruction. This must return a +/// valid AccessedStorage value unless the access has "Unsafe" enforcement. The +/// formal access location may be nested within an outer begin_access. For the +/// purpose of exclusivity, nested accesses are considered distinct formal +/// accesses so they return distinct AccessedStorage values even though they may +/// access the same memory. /// //===----------------------------------------------------------------------===// #ifndef SWIFT_SIL_MEMACCESSUTILS_H #define SWIFT_SIL_MEMACCESSUTILS_H +#include "swift/Basic/IndexTrie.h" #include "swift/SIL/ApplySite.h" #include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/Projection.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILGlobalVariable.h" @@ -75,7 +104,7 @@ namespace swift { -/// Get the base address of a formal access by stripping access markers. +/// Get the source address of a formal access by stripping access markers. /// /// Postcondition: If \p v is an address, then the returned value is also an /// address (pointer-to-address is not stripped). @@ -86,65 +115,49 @@ inline SILValue stripAccessMarkers(SILValue v) { return v; } -/// An address projection that may be inside of a formal access, such as -/// (begin_borrow, struct_element_addr, tuple_element_addr). -struct AccessProjection { - SingleValueInstruction *projectionInst = nullptr; - - /// If \p v is not a recognized access projection the result is invalid. - AccessProjection(SILValue v) { - switch (v->getKind()) { - default: - break; - - case ValueKind::StructElementAddrInst: - case ValueKind::TupleElementAddrInst: - case ValueKind::UncheckedTakeEnumDataAddrInst: - case ValueKind::TailAddrInst: - case ValueKind::IndexAddrInst: - projectionInst = cast(v); - }; - } - - operator bool() const { return projectionInst != nullptr; } - - SILValue baseAddress() const { return projectionInst->getOperand(0); } -}; - -/// Return the base address after stripping access projections. If \p v is an -/// access projection, return the enclosing begin_access. Otherwise, return a -/// "best effort" base address. +/// Return the source address after stripping as many access projections as +/// possible without losing the address type. /// -/// Precondition: \p v must be an address. +/// For formal accesses, this typically returns the begin_access, but may fail +/// for accesses that call into an addressor, which performs pointer +/// conversion. /// -/// To get the base address of the formal access behind the access marker, -/// either call stripAccessMarkers() on the returned value, or call -/// getAccessedAddress() on \p v. +/// If there is no access marker, then this returns the "best-effort" address +/// corresponding to the accessed variable. This never looks through +/// pointer_to_address or other conversions that may change the address type +/// other than via type-safe (TBAA-compatible) projection. +SILValue getAccessAddress(SILValue address); + +/// Return the source address or pointer after stripping all access projections +/// and storage casts. /// -/// To identify the underlying storage object of the access, call -/// findAccessedStorage() either on \p v or on the returned address. -SILValue getAddressAccess(SILValue v); - -/// Convenience for stripAccessMarkers(getAddressAccess(v)). -SILValue getAccessedAddress(SILValue v); +/// If this is a formal access, then it is guaranteed to return the immediately +/// enclosing begin_access and may "see through" storage casts to do so. +/// +/// If there is no access marker, then it returns a "best effort" address +/// corresponding to the accessed variable. In this case, the returned value +/// could be a non-address pointer type. +SILValue getAccessBegin(SILValue address); -/// Return true if \p accessedAddress points to a let-variable. +/// Return the source address or pointer after stripping access projections, +/// access markers, and storage casts. /// -/// Precondition: \p accessedAddress must be an address-type value representing -/// the base of a formal access (not a projection within the access). +/// The returned base address is guaranteed to match the unique AccessedStorage +/// value for the same \p address. That is, if two calls to getAccessBase() +/// return the same base address, then they must also have the same storage. +SILValue getAccessBase(SILValue address); + +/// Return true if \p address points to a let-variable. /// /// let-variables are only written during let-variable initialization, which is -/// assumed to store directly to the same, unaliased accessedAddress. +/// assumed to store directly to the same, unaliased access base. /// /// The address of a let-variable must be the base of a formal access, not an /// access projection. A 'let' member of a struct is *not* a let-variable, /// because it's memory may be written when formally modifying the outer /// struct. A let-variable is either an entire local variable, global variable, /// or class property (these are all formal access base addresses). -/// -/// The caller should derive the accessed address using -/// stripAccessMarkers(getAccessedAddress(ptr)). -bool isLetAddress(SILValue accessedAddress); +bool isLetAddress(SILValue address); /// Return true if two accesses to the same storage may conflict given the kind /// of each access. @@ -231,8 +244,8 @@ class AccessedStorage { static const char *getKindName(Kind k); - // Give object tail storage a fake property index for convenience. - static constexpr unsigned TailIndex = ~0U; + // Give object tail storage a fake large property index for convenience. + static constexpr unsigned TailIndex = std::numeric_limits::max(); /// Directly create an AccessedStorage for class or tail property access. static AccessedStorage forClass(SILValue object, unsigned propertyIndex) { @@ -363,8 +376,10 @@ class AccessedStorage { return global; } + bool isReference() const { return getKind() == Class || getKind() == Tail; } + SILValue getObject() const { - assert(getKind() == Class || getKind() == Tail); + assert(isReference()); return value; } unsigned getPropertyIndex() const { @@ -372,6 +387,27 @@ class AccessedStorage { return getElementIndex(); } + /// Return the address or reference root that the storage was based + /// on. Returns an invalid SILValue for globals or invalid storage. + SILValue getRoot() const { + switch (getKind()) { + case AccessedStorage::Box: + case AccessedStorage::Stack: + case AccessedStorage::Argument: + case AccessedStorage::Yield: + case AccessedStorage::Unidentified: + return getValue(); // Can be invalid for Unidentified storage. + case AccessedStorage::Global: + return SILValue(); + case AccessedStorage::Class: + case AccessedStorage::Tail: + return getObject(); + case AccessedStorage::Nested: + assert(false && "AccessPath cannot identify nested access"); + return SILValue(); + } + } + /// Return true if the given storage objects have identical storage locations. /// /// This compares only the AccessedStorage base class bits, ignoring the @@ -427,18 +463,17 @@ class AccessedStorage { /// If this is a uniquely identified formal access, then it cannot /// alias with any other uniquely identified access to different storage. - /// - /// This determines whether access markers may conflict, so it cannot assume - /// that exclusivity is enforced. bool isUniquelyIdentified() const { switch (getKind()) { case Box: case Stack: case Global: return true; + case Argument: + return + getArgument()->getArgumentConvention().isExclusiveIndirectParameter(); case Class: case Tail: - case Argument: case Yield: case Nested: case Unidentified: @@ -447,16 +482,10 @@ class AccessedStorage { llvm_unreachable("unhandled kind"); } - /// Return true if this a uniquely identified formal access location assuming - /// exclusivity enforcement. Do not use this to optimize access markers. - bool isUniquelyIdentifiedAfterEnforcement() const { - if (isUniquelyIdentified()) - return true; - - return getKind() == Argument - && getArgument() - ->getArgumentConvention() - .isExclusiveIndirectParameter(); + /// Return true if this storage is guaranteed not to overlap with \p other's + /// storage. + bool isDistinctFrom(const AccessedStorage &other) const { + return isDistinctFrom<&AccessedStorage::isUniquelyIdentified>(other); } /// Return true if this identifies the base of a formal access location. @@ -470,11 +499,27 @@ class AccessedStorage { return getKind() == Class; } - // Return true if this storage is guaranteed not to overlap with \p other's - // storage. + /// Returns the ValueDecl for the underlying storage, if it can be + /// determined. Otherwise returns null. + /// + /// WARNING: This is not a constant-time operation. It is for diagnostics and + /// checking via the ValueDecl if we are processing a `let` variable. + const ValueDecl *getDecl() const; + + void print(raw_ostream &os) const; + void dump() const; + +private: + // Disable direct comparison because we allow subclassing with bitfields. + // Currently, we use DenseMapInfo to unique storage, which defines key + // equalilty only in terms of the base AccessedStorage class bits. + bool operator==(const AccessedStorage &) const = delete; + bool operator!=(const AccessedStorage &) const = delete; + + template bool isDistinctFrom(const AccessedStorage &other) const { - if (isUniquelyIdentified()) { - if (other.isUniquelyIdentified() && !hasIdenticalBase(other)) + if ((this->*IsUniqueFn)()) { + if ((other.*IsUniqueFn)() && !hasIdenticalBase(other)) return true; if (other.isObjectAccess()) @@ -484,8 +529,8 @@ class AccessedStorage { // Box/Stack storage. return false; } - if (other.isUniquelyIdentified()) - return other.isDistinctFrom(*this); + if ((other.*IsUniqueFn)()) + return other.isDistinctFrom(*this); // Neither storage is uniquely identified. if (isObjectAccess()) { @@ -508,7 +553,7 @@ class AccessedStorage { return false; } if (other.isObjectAccess()) - return other.isDistinctFrom(*this); + return other.isDistinctFrom(*this); // Neither storage is from a class or tail. // @@ -516,23 +561,6 @@ class AccessedStorage { // nested/argument access. return false; } - - /// Returns the ValueDecl for the underlying storage, if it can be - /// determined. Otherwise returns null. - /// - /// WARNING: This is not a constant-time operation. It is for diagnostics and - /// checking via the ValueDecl if we are processing a `let` variable. - const ValueDecl *getDecl() const; - - void print(raw_ostream &os) const; - void dump() const; - -private: - // Disable direct comparison because we allow subclassing with bitfields. - // Currently, we use DenseMapInfo to unique storage, which defines key - // equalilty only in terms of the base AccessedStorage class bits. - bool operator==(const AccessedStorage &) const = delete; - bool operator!=(const AccessedStorage &) const = delete; }; } // end namespace swift @@ -634,6 +662,314 @@ inline AccessedStorage identifyCapturedStorage(SILValue capturedAddress) { } // end namespace swift +//===----------------------------------------------------------------------===// +// AccessPath +//===----------------------------------------------------------------------===// + +namespace swift { + +/// Identify an addressable location based the AccessedStorage and projection +/// path. +/// +/// Each unique path from a base address implies a unique memory location within +/// that object. A path prefix identifies memory that contains all paths with +/// the same prefix. The AccessPath returned by AccessPath::compute(address) +/// identifies the object seen by any memory operation that *directly* operates +/// on 'address'. The computed path is a prefix of the paths of any contained +/// subobjects. +/// +/// Path indices, encoded by AccessPath::Index, may be either subobject +/// projections or offset indices. We print subobject indices as '#n' and offset +/// indices as '@n'. +/// +/// Example Def->Use: (Path indices) +/// struct_element_addr #1: (#1) +/// ref_tail_addr -> struct_element_addr #2: (#2) +/// ref_tail_addr -> index_addr #1 -> struct_element_addr #2: (@1, #2) +/// pointer_to_address -> struct_element_addr #2: (#2) +/// pointer_to_address -> index_addr #1 -> struct_element_addr #2: (@1, #2) +/// +/// The index of ref_element_addr is part of the storage identity and does +/// not contribute to the access path indices. +/// +/// A well-formed path has at most one offset component at the begining of the +/// path (chained index_addrs are merged into one offset). In other words, +/// taking an offset from a subobject projection is not well-formed access +/// path. However, it is possible (however undesirable) for programmers to +/// convert a subobject address into a pointer (for example, via implicit +/// conversion), then advance that pointer. Since we can't absolutely prevent +/// this, we instead consider it an invalid AccessPath. This is the only case in +/// which AccessPath::storage can differ from findAccessedStorage(). +/// +/// Storing an AccessPath ammortizes to constant space. To cache identification +/// of address locations, AccessPath should be used rather than the +/// ProjectionPath which requires quadratic space in the number of address +/// values and quadratic time when comparing addresses. +/// +/// Type-cast operations such as address_to_pointer may appear on the access +/// path. It is illegal to use these operations to cast to a non-layout +/// compatible type. TODO: add enforcement for this rule. +class AccessPath { +public: + /// Create the AccessPath for any memory operation on the given address. + static AccessPath compute(SILValue address); + + // Encode a dynamic index_addr as an UnknownOffset. + static constexpr int UnknownOffset = std::numeric_limits::min() >> 1; + + struct PathNode; + + // An access path index. + // + // Note: + // - IndexTrieNode::RootIndex = INT_MIN = 0x80000000 + // - AccessedStorage::TailIndex = INT_MAX = 0x7FFFFFFF + // - AccessPath::UnknownOffset = (INT_MIN>>1) = 0xC0000000 + // - An offset index is never zero + class Index { + public: + friend struct PathNode; + + // Use the sign bit to identify offset indices. Subobject projections are + // always positive. + constexpr static unsigned IndexFlag = unsigned(1) << 31; + static int encodeOffset(int indexValue) { + assert(indexValue != 0 && "an offset index cannot be zero"); + // Must be able to sign-extended the 31-bit value. + assert(((indexValue << 1) >> 1) == indexValue); + return indexValue | IndexFlag; + } + + // Encode a positive field index, property index, or TailIndex. + static Index forSubObjectProjection(unsigned projIdx) { + assert(Index(projIdx).isSubObjectProjection()); + return Index(projIdx); + } + + static Index forOffset(unsigned projIdx) { + return Index(encodeOffset(projIdx)); + } + + private: + int indexEncoding; + Index(int indexEncoding) : indexEncoding(indexEncoding) {} + + public: + bool isSubObjectProjection() const { return indexEncoding >= 0; } + + int getSubObjectIndex() const { + assert(isSubObjectProjection()); + return indexEncoding; + } + + // Sign-extend the 31-bit value. + int getOffset() const { + assert(!isSubObjectProjection()); + return ((indexEncoding << 1) >> 1); + } + + bool isUnknownOffset() const { + return indexEncoding == AccessPath::UnknownOffset; + } + + int getEncoding() const { return indexEncoding; } + + void print(raw_ostream &os) const; + + void dump() const; + }; + + // A component of the AccessPath. + // + // Transient wrapper around the underlying IndexTrieNode that encodes either a + // subobject projection or an offset index. + struct PathNode { + IndexTrieNode *node = nullptr; + + constexpr PathNode() = default; + + PathNode(IndexTrieNode *node) : node(node) {} + + bool isValid() const { return node != nullptr; } + + bool isRoot() const { return node->isRoot(); } + + bool isLeaf() const { return node->isLeaf(); } + + Index getIndex() const { return Index(node->getIndex()); } + + PathNode getParent() const { return node->getParent(); } + + // Return the PathNode from \p subNode's path one level deeper than \p + // prefixNode. + // + // Precondition: this != subNode + PathNode findPrefix(PathNode subNode) const; + + bool operator==(PathNode other) const { return node == other.node; } + bool operator!=(PathNode other) const { return node != other.node; } + }; + +private: + AccessedStorage storage; + PathNode pathNode; + // store the single offset index independent from the PathNode to simplify + // checking for path overlap. + int offset = 0; + +public: + // AccessPaths are built by AccessPath::compute(address). + // + // AccessedStorage is only used to identify the storage location; AccessPath + // ignores its subclass bits. + AccessPath(AccessedStorage storage, PathNode pathNode, int offset) + : storage(storage), pathNode(pathNode), offset(offset) { + assert(storage.getKind() != AccessedStorage::Nested); + assert(pathNode.isValid() || !storage && "Access path requires a pathNode"); + } + + AccessPath() = default; + + bool operator==(AccessPath other) const { + return + storage.hasIdenticalBase(other.storage) && pathNode == other.pathNode; + } + bool operator!=(AccessPath other) const { return !(*this == other); } + + bool isValid() const { return pathNode.isValid(); } + + AccessedStorage getStorage() const { return storage; } + + PathNode getPathNode() const { return pathNode; } + + int getOffset() const { return offset; } + + /// Return true if this path contains \p subPath. + bool contains(AccessPath subPath) const; + + /// Return true if this path may overlap with \p otherPath. + bool mayOverlap(AccessPath otherPath) const; + + /// Return the address root that the access path was based on. Returns + /// an invalid SILValue for globals or invalid storage. + SILValue getRoot() const { return storage.getRoot(); } + + /// Get all uses of all address values that have a common AccessPath. Return + /// true if all uses were found before reaching the limit. + /// + /// This should find all uses for which calling AccessPath::compute() would + /// yield an identical AccessPath. + /// + /// This fails on global variables which have no root. To collect all uses, + /// including global variable uses, use AccessPathWithBase::collectUses. + bool + collectUses(SmallVectorImpl &uses, bool collectOverlappingUses, + unsigned useLimit = std::numeric_limits::max()) const; + + void printPath(raw_ostream &os) const; + void print(raw_ostream &os) const; + void dump() const; +}; + +// Encapsulate the result of computing an AccessPath. AccessPath does not store +// the base address of the formal access because it does not always uniquely +// indentify the access, but AccessPath users may use the base address to to +// recover the def-use chain. +// +// AccessPathWithBase::collectUses is guaranteed to be complete for all storage +// types, while AccessPath::collectUses cannot handle globals. +struct AccessPathWithBase { + AccessPath accessPath; + // The address-type value that is the base of the formal access. For + // class storage, it is the ref_element_addr. For global storage it is the + // global_addr or initializer apply. For other storage, it is the same as + // accessPath.getRoot(). + // + // base may be invalid for global_addr -> address_to_pointer -> phi patterns. + // FIXME: add a structural requirement to SIL so base is always valid in OSSA. + SILValue base; + + /// \p address identifies the object seen by any memory operation that + /// directly operates on the address. For indexable addresses, this implies an + /// operation at index zero. + static AccessPathWithBase compute(SILValue address); + + AccessPathWithBase(AccessPath accessPath, SILValue base) + : accessPath(accessPath), base(base) {} + + bool operator==(AccessPathWithBase other) const { + return accessPath == other.accessPath && base == other.base; + } + bool operator!=(AccessPathWithBase other) const { return !(*this == other); } + + /// Get all uses of all address values that have a common AccessPath. Return + /// true if all uses were found before reaching the limit. + /// + /// This should find all uses for which calling AccessPath::compute() would + /// yield an identical AccessPath and, for global variables, have the same + /// access base (e.g. from the same global_addr instruction). + bool collectUses(SmallVectorImpl &uses, + bool collectOverlappingUses, + unsigned useLimit = std::numeric_limits::max()) const; + + void print(raw_ostream &os) const; + void dump() const; +}; + +inline AccessPath AccessPath::compute(SILValue address) { + return AccessPathWithBase::compute(address).accessPath; +} + +} // end namespace swift + +namespace llvm { +/// Allow AccessPath to be used in DenseMap. +template <> struct DenseMapInfo { + static inline swift::AccessPath getEmptyKey() { + return swift::AccessPath( + DenseMapInfo::getEmptyKey(), + swift::AccessPath::PathNode( + DenseMapInfo::getEmptyKey()), 0); + } + static inline swift::AccessPath getTombstoneKey() { + return swift::AccessPath( + DenseMapInfo::getTombstoneKey(), + swift::AccessPath::PathNode( + DenseMapInfo::getTombstoneKey()), 0); + } + static inline unsigned getHashValue(const swift::AccessPath &val) { + return llvm::hash_combine( + DenseMapInfo::getHashValue(val.getStorage()), + val.getPathNode().node); + } + static bool isEqual(const swift::AccessPath &lhs, + const swift::AccessPath &rhs) { + return lhs == rhs; + } +}; +template <> struct DenseMapInfo { + static inline swift::AccessPathWithBase getEmptyKey() { + return swift::AccessPathWithBase( + DenseMapInfo::getEmptyKey(), + DenseMapInfo::getEmptyKey()); + } + static inline swift::AccessPathWithBase getTombstoneKey() { + return swift::AccessPathWithBase( + DenseMapInfo::getTombstoneKey(), + DenseMapInfo::getTombstoneKey()); + } + static inline unsigned getHashValue(const swift::AccessPathWithBase &val) { + return llvm::hash_combine( + DenseMapInfo::getHashValue(val.accessPath), + DenseMapInfo::getHashValue(val.base)); + } + static bool isEqual(const swift::AccessPathWithBase &lhs, + const swift::AccessPathWithBase &rhs) { + return lhs == rhs; + } +}; +} // end namespace llvm + //===----------------------------------------------------------------------===// // MARK: Helper API for specific formal access patterns //===----------------------------------------------------------------------===// @@ -709,7 +1045,110 @@ SILBasicBlock::iterator removeBeginAccess(BeginAccessInst *beginAccess); namespace swift { -/// Abstract CRTP class for a visitor passed to \c visitAccessUseDefChain. +/// If \p svi is an access projection, return an address-type operand for the +/// incoming address. +/// +/// An access projection is on the inside of a formal access. It includes +/// struct_element_addr and tuple_element_addr, but not ref_element_addr. +/// +/// The returned address may point to any compatible type, which may alias with +/// the projected address. Arbitrary address casts are not allowed. +inline Operand *getAccessProjectionOperand(SingleValueInstruction *svi) { + switch (svi->getKind()) { + default: + return nullptr; + + case SILInstructionKind::StructElementAddrInst: + case SILInstructionKind::TupleElementAddrInst: + case SILInstructionKind::IndexAddrInst: + case SILInstructionKind::TailAddrInst: + // open_existential_addr and unchecked_take_enum_data_addr are problematic + // because they both modify memory and are access projections. Ideally, they + // would not be casts, but will likely be eliminated with opaque values. + case SILInstructionKind::OpenExistentialAddrInst: + case SILInstructionKind::UncheckedTakeEnumDataAddrInst: + return &svi->getAllOperands()[0]; + + // Special-case this indirect enum pattern: + // unchecked_take_enum_data_addr -> load -> project_box + // (the individual load and project_box are not access projections) + // + // FIXME: Make sure this case goes away with OSSA and opaque values. If not, + // then create a special instruction for this pattern. That way we have an + // invariant that all access projections are single-value address-to-address + // conversions. Then reuse this helper for both use-def an def-use traversals. + // + // Check getAccessProjectionOperand() before isAccessedStorageCast() because + // it will consider any project_box to be a storage cast. + case SILInstructionKind::ProjectBoxInst: + if (auto *load = dyn_cast(svi->getOperand(0))) + return &load->getOperandRef(); + + return nullptr; + }; +} + +/// An address, pointer, or box cast that occurs outside of the formal +/// access. These convert the base of accessed storage without affecting the +/// AccessPath. Useful for both use-def and def-use traversal. The source +/// address must be at operand(0). +/// +/// Some of these casts, such as address_to_pointer, may also occur inside of a +/// formal access. TODO: Add stricter structural guarantee such that these never +/// occur within an access. It's important to be able to get the accessed +/// address without looking though type casts or pointer_to_address [strict], +/// which we can't do if those operations are behind access projections. +inline bool isAccessedStorageCast(SingleValueInstruction *svi) { + switch (svi->getKind()) { + default: + return false; + + // Simply pass-thru the incoming address. + case SILInstructionKind::MarkUninitializedInst: + case SILInstructionKind::UncheckedAddrCastInst: + case SILInstructionKind::MarkDependenceInst: + // Look through a project_box to identify the underlying alloc_box as the + // accesed object. It must be possible to reach either the alloc_box or the + // containing enum in this loop, only looking through simple value + // propagation such as copy_value and begin_borrow. + case SILInstructionKind::ProjectBoxInst: + case SILInstructionKind::ProjectBlockStorageInst: + case SILInstructionKind::CopyValueInst: + case SILInstructionKind::BeginBorrowInst: + // Casting to RawPointer does not affect the AccessPath. When converting + // between address types, they must be layout compatible (with truncation). + case SILInstructionKind::AddressToPointerInst: + // Access to a Builtin.RawPointer. It may be important to continue looking + // through this because some RawPointers originate from identified + // locations. See the special case for global addressors, which return + // RawPointer, above. + // + // If the inductive search does not find a valid addressor, it will + // eventually reach the default case that returns in invalid location. This + // is correct for RawPointer because, although accessing a RawPointer is + // legal SIL, there is no way to guarantee that it doesn't access class or + // global storage, so returning a valid unidentified storage object would be + // incorrect. It is the caller's responsibility to know that formal access + // to such a location can be safely ignored. + // + // For example: + // + // - KeyPath Builtins access RawPointer. However, the caller can check + // that the access `isFromBuilin` and ignore the storage. + // + // - lldb generates RawPointer access for debugger variables, but SILGen + // marks debug VarDecl access as 'Unsafe' and SIL passes don't need the + // AccessedStorage for 'Unsafe' access. + case SILInstructionKind::PointerToAddressInst: + return true; + } +} + +/// Abstract CRTP class for a visiting instructions that are part of the use-def +/// chain from an accessed address up to the storage base. +/// +/// Given the address of a memory operation begin visiting at +/// getAccessedAddress(address). template class AccessUseDefChainVisitor { protected: @@ -751,32 +1190,40 @@ class AccessUseDefChainVisitor { // Result visitBase(SILValue base, AccessedStorage::Kind kind); // Result visitNonAccess(SILValue base); // Result visitPhi(SILPhiArgument *phi); - // Result visitCast(SingleValueInstruction *cast, Operand *parentAddr); - // Result visitPathComponent(SingleValueInstruction *projectedAddr, - // Operand *parentAddr); + // Result visitStorageCast(SingleValueInstruction *cast, Operand *sourceOper); + // Result visitAccessProjection(SingleValueInstruction *cast, + // Operand *sourceOper); Result visit(SILValue sourceAddr); }; template Result AccessUseDefChainVisitor::visit(SILValue sourceAddr) { + if (auto *svi = dyn_cast(sourceAddr)) { + if (auto *projOper = getAccessProjectionOperand(svi)) + return asImpl().visitAccessProjection(svi, projOper); + + if (isAccessedStorageCast(svi)) + return asImpl().visitStorageCast(svi, &svi->getAllOperands()[0]); + } switch (sourceAddr->getKind()) { default: - if (isAddressForLocalInitOnly(sourceAddr)) - return asImpl().visitUnidentified(sourceAddr); - return asImpl().visitNonAccess(sourceAddr); + break; // MARK: Handle immediately-identifiable instructions. // An AllocBox is a fully identified memory location. case ValueKind::AllocBoxInst: return asImpl().visitBoxAccess(cast(sourceAddr)); + // An AllocStack is a fully identified memory location, which may occur // after inlining code already subjected to stack promotion. case ValueKind::AllocStackInst: return asImpl().visitStackAccess(cast(sourceAddr)); + case ValueKind::GlobalAddrInst: return asImpl().visitGlobalAccess(sourceAddr); + case ValueKind::ApplyInst: { FullApplySite apply(cast(sourceAddr)); if (auto *funcRef = apply.getReferencedFunctionOrNull()) { @@ -792,25 +1239,37 @@ Result AccessUseDefChainVisitor::visit(SILValue sourceAddr) { } case ValueKind::RefElementAddrInst: return asImpl().visitClassAccess(cast(sourceAddr)); + + // ref_tail_addr project an address from a reference. + // This is a valid address producer for nested @inout argument + // access, but it is never used for formal access of identified objects. + case ValueKind::RefTailAddrInst: + return asImpl().visitTailAccess(cast(sourceAddr)); + // A yield is effectively a nested access, enforced independently in // the caller and callee. case ValueKind::BeginApplyResult: return asImpl().visitYieldAccess(cast(sourceAddr)); + // A function argument is effectively a nested access, enforced // independently in the caller and callee. case ValueKind::SILFunctionArgument: - return asImpl().visitArgumentAccess(cast(sourceAddr)); + return asImpl().visitArgumentAccess( + cast(sourceAddr)); // View the outer begin_access as a separate location because nested // accesses do not conflict with each other. case ValueKind::BeginAccessInst: return asImpl().visitNestedAccess(cast(sourceAddr)); + // Static index_addr is handled by getAccessProjectionOperand. Dynamic + // index_addr is currently unidentified because we can't form an AccessPath + // including them. case ValueKind::SILUndef: return asImpl().visitUnidentified(sourceAddr); - // MARK: The sourceAddr producer cannot immediately be classified, - // follow the use-def chain. + // MARK: The sourceAddr producer cannot immediately be classified, + // follow the use-def chain. case ValueKind::StructExtractInst: // Handle nested access to a KeyPath projection. The projection itself @@ -834,98 +1293,11 @@ Result AccessUseDefChainVisitor::visit(SILValue sourceAddr) { checkSwitchEnumBlockArg(cast(sourceAddr)); return asImpl().visitUnidentified(sourceAddr); } - // Load a box from an indirect payload of an opaque enum. - // We must have peeked past the project_box earlier in this loop. - // (the indirectness makes it a box, the load is for address-only). - // - // %payload_adr = unchecked_take_enum_data_addr %enum : $*Enum, #Enum.case - // %box = load [take] %payload_adr : $*{ var Enum } - // - // FIXME: this case should go away with opaque values. - // - // Otherwise return invalid AccessedStorage. - case ValueKind::LoadInst: - if (sourceAddr->getType().is()) { - Operand *addrOper = &cast(sourceAddr)->getOperandRef(); - assert(isa(addrOper->get())); - return asImpl().visitCast(cast(sourceAddr), - addrOper); - } - return asImpl().visitNonAccess(sourceAddr); - - // ref_tail_addr project an address from a reference. - // This is a valid address producer for nested @inout argument - // access, but it is never used for formal access of identified objects. - case ValueKind::RefTailAddrInst: - return asImpl().visitTailAccess(cast(sourceAddr)); - - // Inductive single-operand cases: - // Look through address casts to find the source address. - case ValueKind::MarkUninitializedInst: - case ValueKind::OpenExistentialAddrInst: - case ValueKind::UncheckedAddrCastInst: - // Inductive cases that apply to any type. - case ValueKind::CopyValueInst: - case ValueKind::MarkDependenceInst: - // Look through a project_box to identify the underlying alloc_box as the - // accesed object. It must be possible to reach either the alloc_box or the - // containing enum in this loop, only looking through simple value - // propagation such as copy_value. - case ValueKind::ProjectBoxInst: - // Handle project_block_storage just like project_box. - case ValueKind::ProjectBlockStorageInst: - // Look through begin_borrow in case a local box is borrowed. - case ValueKind::BeginBorrowInst: - // Casting to RawPointer does not affect the AccessPath. When converting - // between address types, they must be layout compatible (with truncation). - case ValueKind::AddressToPointerInst: - // A tail_addr is a projection that does not affect the access path because it - // must always originate from a ref_tail_addr. Any projection within the - // object's tail storage effectively has the same access path. - case ValueKind::TailAddrInst: - return asImpl().visitCast( - cast(sourceAddr), - &cast(sourceAddr)->getAllOperands()[0]); + } // end switch + if (isAddressForLocalInitOnly(sourceAddr)) + return asImpl().visitUnidentified(sourceAddr); - // Access to a Builtin.RawPointer. It may be important to continue looking - // through this because some RawPointers originate from identified - // locations. See the special case for global addressors, which return - // RawPointer, above. - // - // If the inductive search does not find a valid addressor, it will - // eventually reach the default case that returns in invalid location. This - // is correct for RawPointer because, although accessing a RawPointer is - // legal SIL, there is no way to guarantee that it doesn't access class or - // global storage, so returning a valid unidentified storage object would be - // incorrect. It is the caller's responsibility to know that formal access - // to such a location can be safely ignored. - // - // For example: - // - // - KeyPath Builtins access RawPointer. However, the caller can check - // that the access `isFromBuilin` and ignore the storage. - // - // - lldb generates RawPointer access for debugger variables, but SILGen - // marks debug VarDecl access as 'Unsafe' and SIL passes don't need the - // AccessedStorage for 'Unsafe' access. - // - // This is always considered a path component because an IndexAddr may - // project from it. - case ValueKind::PointerToAddressInst: - return asImpl().visitPathComponent( - cast(sourceAddr), - &cast(sourceAddr)->getAllOperands()[0]); - - // Address-to-address subobject projections. Projection::isAddressProjection - // returns true for these. - case ValueKind::StructElementAddrInst: - case ValueKind::TupleElementAddrInst: - case ValueKind::UncheckedTakeEnumDataAddrInst: - case ValueKind::IndexAddrInst: - return asImpl().visitPathComponent( - cast(sourceAddr), - &cast(sourceAddr)->getAllOperands()[0]); - } + return asImpl().visitNonAccess(sourceAddr); } } // end namespace swift diff --git a/include/swift/SIL/PatternMatch.h b/include/swift/SIL/PatternMatch.h index 9a7ab469ac947..07b7449e1d9eb 100644 --- a/include/swift/SIL/PatternMatch.h +++ b/include/swift/SIL/PatternMatch.h @@ -668,6 +668,16 @@ using BuiltinApplyTy = typename Apply_match::Ty; // Define matchers for most of builtin instructions. #include "swift/AST/Builtins.def" +#undef BUILTIN_UNARY_OP_MATCH_WITH_ARG_MATCHER +#undef BUILTIN_BINARY_OP_MATCH_WITH_ARG_MATCHER +#undef BUILTIN_VARARGS_OP_MATCH_WITH_ARG_MATCHER +#undef BUILTIN_CAST_OPERATION +#undef BUILTIN_CAST_OR_BITCAST_OPERATION +#undef BUILTIN_BINARY_OPERATION_ALL +#undef BUILTIN_BINARY_PREDICATE +#undef BUILTIN_MISC_OPERATION +#undef BUILTIN + //=== // Convenience compound builtin instructions matchers that succeed // if any of the sub-matchers succeed. diff --git a/include/swift/SILOptimizer/Analysis/ValueTracking.h b/include/swift/SILOptimizer/Analysis/ValueTracking.h index 32569f2f25bbf..be2ca665ae6b4 100644 --- a/include/swift/SILOptimizer/Analysis/ValueTracking.h +++ b/include/swift/SILOptimizer/Analysis/ValueTracking.h @@ -39,8 +39,8 @@ bool pointsToLocalObject(SILValue V); /// Returns true if \p V is a uniquely identified address or reference. Two /// uniquely identified pointers with distinct roots cannot alias. However, a /// uniquely identified pointer may alias with unidentified pointers. For -/// example, the uniquely identified pointer may escape to a call that returns an -/// alias of that pointer. +/// example, the uniquely identified pointer may escape to a call that returns +/// an alias of that pointer. /// /// It may be any of: /// @@ -53,6 +53,9 @@ bool pointsToLocalObject(SILValue V); /// /// - an address projection based on an exclusive argument with no levels of /// indirection (e.g. ref_element_addr, project_box, etc.). +/// +/// TODO: Fold this into the AccessedStorage API. pointsToLocalObject should be +/// performed by AccessedStorage::isUniquelyIdentified. inline bool isUniquelyIdentified(SILValue V) { SILValue objectRef = V; if (V->getType().isAddress()) { @@ -60,7 +63,7 @@ inline bool isUniquelyIdentified(SILValue V) { if (!storage) return false; - if (storage.isUniquelyIdentifiedAfterEnforcement()) + if (storage.isUniquelyIdentified()) return true; if (!storage.isObjectAccess()) diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 9661d638d83e9..690708643afe8 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -4125,7 +4125,7 @@ void IRGenSILFunction::visitRefTailAddrInst(RefTailAddrInst *i) { } static bool isInvariantAddress(SILValue v) { - SILValue accessedAddress = getAccessedAddress(v); + SILValue accessedAddress = getAccessAddress(v); if (auto *ptrRoot = dyn_cast(accessedAddress)) { return ptrRoot->isInvariant(); } diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index 6377fd2643c08..c34ae23d3fc93 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -13,11 +13,9 @@ #define DEBUG_TYPE "sil-access-utils" #include "swift/SIL/MemAccessUtils.h" -#include "swift/SIL/ApplySite.h" -#include "swift/SIL/Projection.h" -#include "swift/SIL/SILGlobalVariable.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/SILUndef.h" +#include "llvm/Support/Debug.h" using namespace swift; @@ -25,48 +23,417 @@ using namespace swift; // MARK: General Helpers //===----------------------------------------------------------------------===// -// TODO: When the optimizer stops stripping begin_access markers, then we should -// be able to assert that the result is a BeginAccessInst and the default case -// is unreachable. -SILValue swift::getAddressAccess(SILValue v) { - while (true) { - assert(v->getType().isAddress()); - auto projection = AccessProjection(v); - if (!projection) - return v; +namespace { + +enum StorageCastTy { StopAtStorageCast, IgnoreStorageCast }; - v = projection.baseAddress(); +// Handle a single phi-web within an access use-def chain. +// +// Recursively calls the useDefVisitor on any operations that aren't recognized +// as storage casts or projections. If the useDefVisitor finds a consistent +// result for all operands, then it's result will remain valid. If the +// useDefVisitor has an invalid result after processing the phi web, then it's +// original result is restored, then the phi reported to the useDefVisitor as a +// NonAccess. +// +// Phi-web's are only allowed to contain casts and projections that do not +// affect the access path. If AccessPhiVisitor reaches an unhandled projection, +// it remembers that as the commonDefinition. If after processing the entire +// web, the commonDefinition is unique, then it calls the original useDefVisitor +// to update its result. Note that visitAccessProjection and setDefinition are +// only used by visitors that process access projections; once the accessed +// address is reached, they are no longer relevant. +template +class AccessPhiVisitor + : public AccessUseDefChainVisitor> { + + UseDefVisitor &useDefVisitor; + StorageCastTy storageCastTy; + + Optional commonDefinition; + SmallVector pointerWorklist; + SmallPtrSet nestedPhis; + +public: + AccessPhiVisitor(UseDefVisitor &useDefVisitor, StorageCastTy storageCastTy) + : useDefVisitor(useDefVisitor), storageCastTy(storageCastTy) {} + + // Main entry point. + void findPhiAccess(SILPhiArgument *phiArg) && { + auto savedResult = useDefVisitor.saveResult(); + visitPhi(phiArg); + while (!pointerWorklist.empty()) { + this->visit(pointerWorklist.pop_back_val()); + } + // If a common path component was found, recursively look for the result. + if (commonDefinition) { + if (commonDefinition.getValue()) { + useDefVisitor.reenterUseDef(commonDefinition.getValue()); + } else { + // Divergent paths were found; invalidate any previously discovered + // storage. + useDefVisitor.invalidateResult(); + } + } + // If the result is now invalid, reset it and process the current phi as an + // unrecgonized access instead. + if (!useDefVisitor.isResultValid()) { + useDefVisitor.restoreResult(savedResult); + visitNonAccess(phiArg); + } + } + + // Visitor helper. + void setDefinition(SILValue def) { + if (!commonDefinition) { + commonDefinition = def; + return; + } + if (commonDefinition.getValue() != def) + commonDefinition = SILValue(); + } + + void checkVisitorResult(SILValue result) { + assert(!result && "must override any visitor that returns a result"); + } + + // MARK: Visitor implementation. + + // Recursively call the original storageVisitor for each base. We can't simply + // look for a common definition on all phi inputs, because the base may be + // cloned on each path. For example, two global_addr instructions may refer to + // the same global storage. Those global_addr instructions may each be + // converted to a RawPointer before being passed into the non-address phi. + void visitBase(SILValue base, AccessedStorage::Kind kind) { + checkVisitorResult(useDefVisitor.visitBase(base, kind)); + } + + void visitNonAccess(SILValue value) { + checkVisitorResult(useDefVisitor.visitNonAccess(value)); + } + + void visitNestedAccess(BeginAccessInst *access) { + checkVisitorResult(useDefVisitor.visitNestedAccess(access)); + } + + void visitPhi(SILPhiArgument *phiArg) { + if (nestedPhis.insert(phiArg).second) + phiArg->getIncomingPhiValues(pointerWorklist); + } + + void visitStorageCast(SingleValueInstruction *projectedAddr, + Operand *sourceOper) { + // Allow conversions to/from pointers and addresses on disjoint phi paths + // only if the underlying useDefVisitor allows it. + if (storageCastTy == IgnoreStorageCast) + pointerWorklist.push_back(sourceOper->get()); + else + visitNonAccess(projectedAddr); + } + + void visitAccessProjection(SingleValueInstruction *projectedAddr, + Operand *sourceOper) { + // An offset index on a phi path is always conservatively considered an + // unknown offset. + if (isa(projectedAddr) || isa(projectedAddr)) { + useDefVisitor.addUnknownOffset(); + pointerWorklist.push_back(sourceOper->get()); + return; + } + // No other access projections are expected to occur on disjoint phi + // paths. Stop searching at this projection. + setDefinition(projectedAddr); + } +}; + +enum NestedAccessTy { StopAtAccessBegin, IgnoreAccessBegin }; + +// Find the origin of an access while skipping projections and casts and +// handling phis. +template +class FindAccessVisitorImpl : public AccessUseDefChainVisitor { + using SuperTy = AccessUseDefChainVisitor; + +protected: + NestedAccessTy nestedAccessTy; + StorageCastTy storageCastTy; + + SmallPtrSet visitedPhis; + bool hasUnknownOffset = false; + +public: + FindAccessVisitorImpl(NestedAccessTy nestedAccessTy, + StorageCastTy storageCastTy) + : nestedAccessTy(nestedAccessTy), storageCastTy(storageCastTy) {} + + // MARK: AccessPhiVisitor::UseDefVisitor implementation. + // + // Subclasses must implement: + // isResultValid() + // invalidateResult() + // saveResult() + // restoreResult(Result) + // addUnknownOffset() + + void reenterUseDef(SILValue sourceAddr) { + SILValue nextAddr = this->visit(sourceAddr); + while (nextAddr) { + checkNextAddressType(nextAddr, sourceAddr); + nextAddr = this->visit(nextAddr); + } + } + + // MARK: visitor implementation. + + // Override AccessUseDefChainVisitor to ignore access markers and find the + // outer access base. + SILValue visitNestedAccess(BeginAccessInst *access) { + if (nestedAccessTy == IgnoreAccessBegin) + return access->getSource(); + + return SuperTy::visitNestedAccess(access); + } + + SILValue visitPhi(SILPhiArgument *phiArg) { + // Cycles involving phis are only handled within AccessPhiVisitor. + // Path components are not allowed in phi cycles. + if (visitedPhis.insert(phiArg).second) { + AccessPhiVisitor(this->asImpl(), storageCastTy) + .findPhiAccess(phiArg); + // Each phi operand was now reentrantly processed. Stop visiting. + return SILValue(); + } + // Cannot treat unresolved phis as "unidentified" because they may alias + // with global or class access. + return this->asImpl().visitNonAccess(phiArg); + } + + SILValue visitStorageCast(SingleValueInstruction *projectedAddr, + Operand *sourceAddr) { + assert(storageCastTy == IgnoreStorageCast); + return sourceAddr->get(); + } + + SILValue visitAccessProjection(SingleValueInstruction *projectedAddr, + Operand *sourceAddr) { + if (auto *indexAddr = dyn_cast(projectedAddr)) { + if (!Projection(indexAddr).isValid()) + this->asImpl().addUnknownOffset(); + } else if (isa(projectedAddr)) { + this->asImpl().addUnknownOffset(); + } + return sourceAddr->get(); + } + +protected: + // Helper for reenterUseDef + void checkNextAddressType(SILValue nextAddr, SILValue sourceAddr) { +#ifdef NDEBUG + return; +#endif + SILType type = nextAddr->getType(); + // FIXME: This relatively expensive pointer getAnyPointerElementType check + // is only needed because keypath generation incorrectly produces + // pointer_to_address directly from stdlib Pointer types without a + // struct_extract (as is correctly done in emitAddressorAccessor), and + // the PointerToAddressInst operand type is never verified. + if (type.getASTType()->getAnyPointerElementType()) + return; + + if (type.isAddress() || isa(type.getASTType()) + || isa(type.getASTType())) { + return; + } + llvm::errs() << "Visiting "; + sourceAddr->dump(); + llvm::errs() << " not an address "; + nextAddr->dump(); + nextAddr->getFunction()->dump(); + assert(false); + } +}; + +// Implement getAccessAddress, getAccessBegin, and getAccessBase. +class FindAccessBaseVisitor + : public FindAccessVisitorImpl { + using SuperTy = FindAccessVisitorImpl; + +protected: + Optional base; + +public: + FindAccessBaseVisitor(NestedAccessTy nestedAccessTy, + StorageCastTy storageCastTy) + : FindAccessVisitorImpl(nestedAccessTy, storageCastTy) {} + + // Returns the accessed address or an invalid SILValue. + SILValue findBase(SILValue sourceAddr) && { + reenterUseDef(sourceAddr); + return base.getValueOr(SILValue()); + } + + void setResult(SILValue foundBase) { + if (!base) + base = foundBase; + else if (base.getValue() != foundBase) + base = SILValue(); + } + + // MARK: AccessPhiVisitor::UseDefVisitor implementation. + + bool isResultValid() const { return base && bool(base.getValue()); } + + void invalidateResult() { base = SILValue(); } + + Optional saveResult() const { return base; } + + void restoreResult(Optional result) { base = result; } + + void addUnknownOffset() { return; } + + // MARK: visitor implementation. + + SILValue visitBase(SILValue base, AccessedStorage::Kind kind) { + setResult(base); + return SILValue(); } -} -SILValue swift::getAccessedAddress(SILValue v) { - while (true) { - SILValue v2 = stripAccessMarkers(getAddressAccess(v)); - if (v2 == v) - return v; - v = v2; + SILValue visitNonAccess(SILValue value) { + setResult(value); + return SILValue(); } + + // Override visitStorageCast to avoid seeing through arbitrary address casts. + SILValue visitStorageCast(SingleValueInstruction *projectedAddr, + Operand *sourceAddr) { + if (storageCastTy == StopAtStorageCast) + return visitNonAccess(projectedAddr); + + return SuperTy::visitStorageCast(projectedAddr, sourceAddr); + } +}; + +} // end anonymous namespace + +SILValue swift::getAccessAddress(SILValue address) { + assert(address->getType().isAddress()); + SILValue accessAddress = + FindAccessBaseVisitor(StopAtAccessBegin, StopAtStorageCast) + .findBase(address); + assert(accessAddress->getType().isAddress()); + return accessAddress; +} + +// TODO: When the optimizer stops stripping begin_access markers and SILGen +// protects all memory operations with at least an "unsafe" access scope, then +// we should be able to assert that this returns a BeginAccessInst. +SILValue swift::getAccessBegin(SILValue address) { + assert(address->getType().isAddress()); + return FindAccessBaseVisitor(StopAtAccessBegin, IgnoreStorageCast) + .findBase(address); } -bool swift::isLetAddress(SILValue accessedAddress) { - assert(accessedAddress == getAccessedAddress(accessedAddress) - && "caller must find the address root"); +// This is allowed to be called on a non-address pointer type. +SILValue swift::getAccessBase(SILValue address) { + return FindAccessBaseVisitor(IgnoreAccessBegin, IgnoreStorageCast) + .findBase(address); +} + +bool swift::isLetAddress(SILValue address) { + SILValue base = getAccessBase(address); + if (!base) + return false; + // Is this an address of a "let" class member? - if (auto *rea = dyn_cast(accessedAddress)) + if (auto *rea = dyn_cast(base)) return rea->getField()->isLet(); // Is this an address of a global "let"? - if (auto *gai = dyn_cast(accessedAddress)) { + if (auto *gai = dyn_cast(base)) { auto *globalDecl = gai->getReferencedGlobal()->getDecl(); return globalDecl && globalDecl->isLet(); } return false; } +//===----------------------------------------------------------------------===// +// MARK: FindReferenceRoot +//===----------------------------------------------------------------------===// + +namespace { + +// Essentially RC identity where the starting point is already a reference. +// +// FIXME: We cannot currently see through type casts for true RC identity, +// because property indices are not unique within a class hierarchy. Either fix +// RefElementAddr::getFieldNo so it returns a unique index, or figure out a +// different way to encode the property's VarDecl. (Note that the lack of a +// unique property index could be the source of bugs elsewhere). +class FindReferenceRoot { + SmallPtrSet visitedPhis; + +public: + SILValue findRoot(SILValue ref) && { + SILValue root = recursiveFindRoot(ref); + assert(root && "all phi inputs must be reachable"); + return root; + } + +protected: + // Return an invalid value for a phi with no resolved inputs. + // + // FIXME: We should be able to see through RC identity like this: + // nextRoot = stripRCIdentityCasts(nextRoot); + SILValue recursiveFindRoot(SILValue ref) { + while (true) { + SILValue nextRoot = ref; + nextRoot = stripOwnershipInsts(nextRoot); + if (nextRoot == ref) + break; + ref = nextRoot; + } + + auto *phi = dyn_cast(ref); + if (!phi || !phi->isPhiArgument()) { + return ref; + } + // Handle phis... + if (!visitedPhis.insert(phi).second) { + return SILValue(); + } + SILValue commonInput; + phi->visitIncomingPhiOperands([&](Operand *operand) { + SILValue input = recursiveFindRoot(operand->get()); + // Ignore "back/cross edges" to previously visited phis. + if (!input) + return true; + + if (!commonInput) { + commonInput = input; + return true; + } + if (commonInput == input) + return true; + + commonInput = phi; + return false; + }); + return commonInput; + } +}; + +} // end anonymous namespace + +static SILValue findReferenceRoot(SILValue ref) { + return FindReferenceRoot().findRoot(ref); +} + //===----------------------------------------------------------------------===// // MARK: AccessedStorage //===----------------------------------------------------------------------===// +constexpr unsigned AccessedStorage::TailIndex; + AccessedStorage::AccessedStorage(SILValue base, Kind kind) { assert(base && "invalid storage base"); initKind(kind); @@ -113,17 +480,17 @@ AccessedStorage::AccessedStorage(SILValue base, Kind kind) { case Class: { // Do a best-effort to find the identity of the object being projected // from. It is OK to be unsound here (i.e. miss when two ref_element_addrs - // actually refer the same address) because these addresses will be - // dynamically checked, and static analysis will be sufficiently - // conservative given that classes are not "uniquely identified". + // actually refer the same address) because, when the effort fails, static + // analysis will be sufficiently conservative given that classes are not + // "uniquely identified", and these addresses will be dynamically checked. auto *REA = cast(base); - value = stripBorrow(REA->getOperand()); + value = findReferenceRoot(REA->getOperand()); setElementIndex(REA->getFieldIndex()); break; } case Tail: { auto *RTA = cast(base); - value = stripBorrow(RTA->getOperand()); + value = findReferenceRoot(RTA->getOperand()); break; } } @@ -228,200 +595,748 @@ void AccessedStorage::print(raw_ostream &os) const { break; case Tail: os << getObject(); - os << " Tail\n"; } } -void AccessedStorage::dump() const { print(llvm::dbgs()); } +LLVM_ATTRIBUTE_USED void AccessedStorage::dump() const { print(llvm::dbgs()); } namespace { -// Find common AccessedStorage that leads to all arguments of a given -// pointer phi use. Return an invalid SILValue on failure. -// -// Also guarantees that all phi inputs follow the same access path. If any phi -// inputs have different access path components, then the phi is considered an -// invalid access. This is ok because path components always have an address -// type, and we are phasing out all address-type phis. Pointer-phis will -// continue to be allowed but they cannot affect the access path. -template -class FindPhiStorageVisitor - : public AccessUseDefChainVisitor> { - StorageVisitor &storageVisitor; - Optional commonDefinition; - SmallVector pointerWorklist; - SmallPtrSet nestedPhis; + +// Implementation of AccessUseDefChainVisitor that looks for a single common +// AccessedStorage object for all projection paths. +class FindAccessedStorageVisitor + : public FindAccessVisitorImpl { + +public: + struct Result { + Optional storage; + SILValue base; + }; + +private: + Result result; + + void setResult(AccessedStorage foundStorage, SILValue foundBase) { + if (!result.storage) { + result.storage = foundStorage; + assert(!result.base); + result.base = foundBase; + } else { + // `storage` may still be invalid. If both `storage` and `foundStorage` + // are invalid, this check passes, but we return an invalid storage + // below. + if (!result.storage->hasIdenticalBase(foundStorage)) + result.storage = AccessedStorage(); + if (result.base != foundBase) + result.base = SILValue(); + } + } + +public: + FindAccessedStorageVisitor(NestedAccessTy nestedAccessTy) + : FindAccessVisitorImpl(nestedAccessTy, IgnoreStorageCast) {} + + // Main entry point + void findStorage(SILValue sourceAddr) { this->reenterUseDef(sourceAddr); } + + AccessedStorage getStorage() const { + return result.storage.getValueOr(AccessedStorage()); + } + // getBase may return an invalid value for valid Global storage because there + // may be multiple global_addr bases for identical storage. + SILValue getBase() const { return result.base; } + + // MARK: AccessPhiVisitor::UseDefVisitor implementation. + + // A valid result requires valid storage, but not a valid base. + bool isResultValid() const { + return result.storage && bool(result.storage.getValue()); + } + + void invalidateResult() { setResult(AccessedStorage(), SILValue()); } + + Result saveResult() const { return result; } + + void restoreResult(Result savedResult) { result = savedResult; } + + void addUnknownOffset() { return; } + + // MARK: visitor implementation. + + SILValue visitBase(SILValue base, AccessedStorage::Kind kind) { + setResult(AccessedStorage(base, kind), base); + return SILValue(); + } + + SILValue visitNonAccess(SILValue value) { + invalidateResult(); + return SILValue(); + } +}; + +} // end anonymous namespace + +AccessedStorage swift::findAccessedStorage(SILValue sourceAddr) { + FindAccessedStorageVisitor visitor(IgnoreAccessBegin); + visitor.findStorage(sourceAddr); + return visitor.getStorage(); +} + +AccessedStorage swift::identifyAccessedStorageImpl(SILValue sourceAddr) { + FindAccessedStorageVisitor visitor(StopAtAccessBegin); + visitor.findStorage(sourceAddr); + return visitor.getStorage(); +} + +//===----------------------------------------------------------------------===// +// AccessPath +//===----------------------------------------------------------------------===// + +bool AccessPath::contains(AccessPath subPath) const { + assert(isValid() && subPath.isValid()); + + if (!storage.hasIdenticalBase(subPath.storage)) + return false; + + // Does the offset index match? + if (offset != subPath.offset || offset == UnknownOffset) + return false; + + return pathNode.node->isPrefixOf(subPath.pathNode.node); +} + +bool AccessPath::mayOverlap(AccessPath otherPath) const { + assert(isValid() && otherPath.isValid()); + + if (storage.isDistinctFrom(otherPath.storage)) + return false; + + // If subpaths are disjoint, they do not overlap regardless of offset. + if (!pathNode.node->isPrefixOf(otherPath.pathNode.node) + && !otherPath.pathNode.node->isPrefixOf(pathNode.node)) { + return true; + } + return offset == otherPath.offset || offset == UnknownOffset + || otherPath.offset == UnknownOffset; +} + +namespace { + +// Implementation of AccessUseDefChainVisitor that builds an AccessPath. +class AccessPathVisitor : public FindAccessVisitorImpl { + using SuperTy = FindAccessVisitorImpl; + + SILModule *module; + + // This nested visitor holds the AccessedStorage and base results. + FindAccessedStorageVisitor storageVisitor; + + // Save just enough information for to checkpoint before processing phis. Phis + // can add path components and add an unknown offset. + struct Result { + FindAccessedStorageVisitor::Result storageResult; + int savedOffset; + unsigned pathLength; + + Result(FindAccessedStorageVisitor::Result storageResult, int offset, + unsigned pathLength) + : storageResult(storageResult), savedOffset(offset), + pathLength(pathLength) {} + }; + + // Only access projections affect this path. Since they are are not allowed + // beyond phis, this path is not part of AccessPathVisitor::Result. + llvm::SmallVector reversePath; + // Holds a non-zero value if an index_addr has been processed without yet + // creating a path index for it. + int pendingOffset = 0; public: - FindPhiStorageVisitor(StorageVisitor &storageVisitor) - : storageVisitor(storageVisitor) {} + AccessPathVisitor(SILModule *module) + : FindAccessVisitorImpl(IgnoreAccessBegin, IgnoreStorageCast), + module(module), storageVisitor(IgnoreAccessBegin) {} // Main entry point. - void findPhiStorage(SILPhiArgument *phiArg) { - // Visiting a phi will call storageVisitor to set the storage result - // whenever it finds a base. - visitPhi(phiArg); - while (!pointerWorklist.empty()) { - this->visit(pointerWorklist.pop_back_val()); - } - // If a common path component was found, recursively look for the storage. - if (commonDefinition) { - if (commonDefinition.getValue()) { - auto storage = storageVisitor.findStorage(commonDefinition.getValue()); - (void)storage; // The same storageVisitor called us. It has already - // recorded the storage that it found. - } else { - // If divergent paths were found, invalidate any previously discovered - // storage. - storageVisitor.setStorage(AccessedStorage()); - } + AccessPathWithBase findAccessPath(SILValue sourceAddr) && { + this->reenterUseDef(sourceAddr); + if (auto storage = storageVisitor.getStorage()) { + return AccessPathWithBase( + AccessPath(storage, computeForwardPath(), pendingOffset), + storageVisitor.getBase()); } + return AccessPathWithBase(AccessPath(), SILValue()); } - // Visitor helper. - void setDefinition(SILValue def) { - if (!commonDefinition) { - commonDefinition = def; +protected: + void addPathOffset(int offset) { + if (pendingOffset == AccessPath::UnknownOffset) + return; + + if (offset == AccessPath::UnknownOffset) { + pendingOffset = offset; return; } - if (commonDefinition.getValue() != def) - commonDefinition = SILValue(); + // Accumulate static offsets + pendingOffset = pendingOffset + offset; } - // MARK: Visitor implementation. - - void checkResult(SILValue result) { - assert(!result && "must override any visitor that returns a result"); + // Return the trie node corresponding to the current state of reversePath. + AccessPath::PathNode computeForwardPath() { + IndexTrieNode *forwardPath = module->getIndexTrieRoot(); + for (AccessPath::Index nextIndex : llvm::reverse(reversePath)) { + forwardPath = forwardPath->getChild(nextIndex.getEncoding()); + } + return AccessPath::PathNode(forwardPath); } - // Recursively call the original storageVisitor for each base. We can't simply - // look for a common definition on all phi inputs, because the base may be - // cloned on each path. For example, two global_addr instructions may refer to - // the same global storage. Those global_addr instructions may each be - // converted to a RawPointer before being passed into the non-address phi. - void visitBase(SILValue base, AccessedStorage::Kind kind) { - checkResult(storageVisitor.visitBase(base, kind)); +public: + // MARK: AccessPhiVisitor::UseDefVisitor implementation. + + bool isResultValid() const { return storageVisitor.isResultValid(); } + + void invalidateResult() { + storageVisitor.invalidateResult(); + // Don't clear reversePath. We my call restoreResult later. + pendingOffset = 0; } - void visitNonAccess(SILValue value) { - checkResult(storageVisitor.visitNonAccess(value)); + Result saveResult() const { + return Result(storageVisitor.saveResult(), pendingOffset, + reversePath.size()); } - void visitNestedAccess(BeginAccessInst *access) { - checkResult(storageVisitor.visitNestedAccess(access)); + void restoreResult(Result result) { + storageVisitor.restoreResult(result.storageResult); + pendingOffset = result.savedOffset; + assert(result.pathLength <= reversePath.size() + && "a phi should only add to the path"); + reversePath.erase(reversePath.begin() + result.pathLength, + reversePath.end()); } - void visitPhi(SILPhiArgument *phiArg) { - if (nestedPhis.insert(phiArg).second) - phiArg->getIncomingPhiValues(pointerWorklist); + void addUnknownOffset() { pendingOffset = AccessPath::UnknownOffset; } + + // MARK: visitor implementation. Return the address source as the next use-def + // value to process. An invalid SILValue stops def-use traversal. + + SILValue visitBase(SILValue base, AccessedStorage::Kind kind) { + return storageVisitor.visitBase(base, kind); } - void visitCast(SingleValueInstruction *projectedAddr, Operand *parentAddr) { - // Allow conversions to/from pointers and addresses on disjoint phi paths. - this->pointerWorklist.push_back(parentAddr->get()); + SILValue visitNonAccess(SILValue value) { + invalidateResult(); + return SILValue(); } - void visitPathComponent(SingleValueInstruction *projectedAddr, - Operand *parentAddr) { - // Path components are not expected to occur on disjoint phi paths. Stop - // searching at this projection. - setDefinition(projectedAddr); + // Override FindAccessVisitorImpl to record path components. + SILValue visitAccessProjection(SingleValueInstruction *projectedAddr, + Operand *sourceAddr) { + auto projIdx = ProjectionIndex(projectedAddr); + if (auto *indexAddr = dyn_cast(projectedAddr)) { + addPathOffset(projIdx.isValid() ? projIdx.Index + : AccessPath::UnknownOffset); + } else if (isa(projectedAddr)) { + addPathOffset(AccessPath::UnknownOffset); + } else if (projIdx.isValid()) { + if (pendingOffset) { + LLVM_DEBUG(llvm::dbgs() << "Subobject projection with offset index: " + << *projectedAddr); + // Return an invalid result even though findAccessedStorage() may be + // able to find valid storage, because an offset from a subobject is an + // invalid access path. + return visitNonAccess(projectedAddr); + } + reversePath.push_back( + AccessPath::Index::forSubObjectProjection(projIdx.Index)); + } else { + // Ignore everything in getAccessProjectionOperand that is an access + // projection with no affect on the access path. + assert(isa(projectedAddr) + || isa(projectedAddr) + || isa(projectedAddr)); + } + return sourceAddr->get(); } }; -} // namespace + +} // end anonymous namespace + +AccessPathWithBase AccessPathWithBase::compute(SILValue address) { + return AccessPathVisitor(address->getModule()).findAccessPath(address); +} namespace { -// Implementation of AccessUseDefChainVisitor that looks for a single common -// AccessedStorage object for all projection paths. -template -class FindAccessedStorageVisitorBase - : public AccessUseDefChainVisitor { -protected: - Optional storage; - SmallPtrSet visitedPhis; + +// Perform def-use DFS traversal along a given AccessPath. DFS terminates at +// each discovered use. +// +// If \p collectOverlappingUses is false, then the collected uses all have the +// same AccessPath. Uses that exactly match the AccessPath may either be exact +// uses, or may be subobject projections within that access path, including +// struct_element_addr and tuple_element_addr. The transitive uses of those +// subobject projections are not included. +// +// If \p collectOverlappingUses is true, then the collected uses also include +// uses that access an object that contains the given AccessPath. As before, +// overlapping uses do not include transitive uses of subobject projections +// contained by the current path; the def-use traversal stops at those +// projections regardless of collectOverlappingUses. However, overlapping uses +// may be at an unknown offset relative to the current path, so they don't +// necessarily contain the current path. +// +// Example: path = "(#2)" +// %base = ... // access base +// load %base // containing use +// %elt1 = struct_element_addr %base, #1 // non-use (ignored) +// load %elt1 // non-use (unseen) +// %elt2 = struct_element_addr %base, #2 // chained use (ignored) +// load %elt2 // exact use +// %sub = struct_element_addr %elt2, #i // projection use +// load %sub // interior use (ignored) +// +// A use may be a BranchInst if the corresponding phi does not have common +// AccessedStorage. +// +// For class storage, the def-use traversal starts at the reference +// root. Eventually, traversal reach the base address of the formal access: +// +// %ref = ... // reference root +// %base = ref_element_addr %refRoot // formal access address +// load %base // use +class CollectAccessPathUses { + // Origin of the def-use traversal + AccessedStorage storage; + + // Result: Exact uses, projection uses, and containing uses. + SmallVectorImpl &uses; + + bool collectOverlappingUses; + unsigned useLimit; + + // Access path indices from storage to exact uses + SmallVector pathIndices; // in use-def order + + // A point in the def-use traversal. isRef() is true only for object access + // prior to reaching the base address. + struct DFSEntry { + // Next potential use to visit and flag indicating whether traversal has + // reachaed the access base yet. + llvm::PointerIntPair useAndIsRef; + unsigned pathCursor; // position within pathIndices + int offset; // index_addr offsets seen prior to this use + + DFSEntry(Operand *use, bool isRef, unsigned pathCursor, int offset) + : useAndIsRef(use, isRef), pathCursor(pathCursor), offset(offset) {} + + Operand *getUse() const { return useAndIsRef.getPointer(); } + // Is this pointer a reference? + bool isRef() const { return useAndIsRef.getInt(); } + }; + SmallVector dfsStack; + + SmallPtrSet visitedPhis; public: - // Main entry point. May be called reentrantly by the phi visitor. - AccessedStorage findStorage(SILValue sourceAddr) { - SILValue nextAddr = this->visit(sourceAddr); - while (nextAddr) { - assert(nextAddr->getType().isAddress() - || isa(nextAddr->getType().getASTType()) - || isa(nextAddr->getType().getASTType())); - nextAddr = this->visit(nextAddr); + CollectAccessPathUses(AccessPath accessPath, SmallVectorImpl &uses, + bool collectOverlappingUses, unsigned useLimit) + : storage(accessPath.getStorage()), uses(uses), + collectOverlappingUses(collectOverlappingUses), useLimit(useLimit) { + assert(accessPath.isValid()); + for (AccessPath::PathNode currentNode = accessPath.getPathNode(); + !currentNode.isRoot(); currentNode = currentNode.getParent()) { + assert(currentNode.getIndex().isSubObjectProjection() && + "a valid AccessPath does not contain any intermediate offsets"); + pathIndices.push_back(currentNode.getIndex()); + } + if (int offset = accessPath.getOffset()) + pathIndices.push_back(AccessPath::Index::forOffset(offset)); + + // The search will start from the object root, not the formal access base, + // so add the class index to the front. + auto storage = accessPath.getStorage(); + if (storage.getKind() == AccessedStorage::Class) { + pathIndices.push_back(AccessPath::Index::forSubObjectProjection( + storage.getPropertyIndex())); + } + if (storage.getKind() == AccessedStorage::Tail) { + pathIndices.push_back(AccessPath::Index::forSubObjectProjection( + ProjectionIndex::TailIndex)); } - return storage.getValueOr(AccessedStorage()); } - void setStorage(AccessedStorage foundStorage) { - if (!storage) { - storage = foundStorage; - } else { - // `storage` may still be invalid. If both `storage` and `foundStorage` - // are invalid, this check passes, but we return an invalid storage - // below. - if (!storage->hasIdenticalBase(foundStorage)) - storage = AccessedStorage(); + // Return true if all uses were collected. This is always true as long as the + // access has a single root, or globalBase is provided, and there is no + // useLimit. + // + // For Global storage \p globalBase must be provided as the head of the + // def-use search. + bool collectUses(SILValue globalBase = SILValue()) && { + SILValue root = storage.getRoot(); + if (!root) { + assert(storage.getKind() == AccessedStorage::Global); + if (!globalBase) + return false; + + root = globalBase; } + // If the expected path has an unknown offset, then none of the uses are + // exact. + if (!collectOverlappingUses && !pathIndices.empty() + && pathIndices.back().isUnknownOffset()) { + return true; + } + pushUsers(root, + DFSEntry(nullptr, storage.isReference(), pathIndices.size(), 0)); + while (!dfsStack.empty()) { + if (!visitUser(dfsStack.pop_back_val())) + return false; + } + return true; } - // MARK: visitor implementation. +protected: + void pushUsers(SILValue def, const DFSEntry &dfs) { + for (auto *use : def->getUses()) + pushUser(DFSEntry(use, dfs.isRef(), dfs.pathCursor, dfs.offset)); + } - SILValue visitBase(SILValue base, AccessedStorage::Kind kind) { - setStorage(AccessedStorage(base, kind)); - return SILValue(); + void pushUser(DFSEntry dfs) { + Operand *use = dfs.getUse(); + if (auto *bi = dyn_cast(use->getUser())) { + if (pushPhiUses(bi->getArgForOperand(use), dfs)) + return; + } + // If we didn't find and process a phi, continue DFS. + dfsStack.emplace_back(dfs); } - SILValue visitNonAccess(SILValue value) { - setStorage(AccessedStorage()); - return SILValue(); + // Return true if this phi has been processed and does not need to be + // considered as a separate use. + bool pushPhiUses(const SILPhiArgument *phi, DFSEntry dfs) { + if (!visitedPhis.insert(phi).second) + return true; + + // If this phi has a common base, continue to follow the access path. This + // check is different for reference types vs pointer types. + if (dfs.isRef()) { + assert(!dfs.offset && "index_addr not allowed on reference roots"); + // When isRef is true, the address access hasn't been seen yet and + // we're still following the reference root's users. Check if all phi + // inputs have the same reference root before looking through it. + if (findReferenceRoot(phi) == storage.getObject()) { + pushUsers(phi, dfs); + return true; + } + // The branch will be pushed onto the normal user list. + return false; + } + // Check if all phi inputs have the same accessed storage before + // looking through it. If the phi input differ the its storage is invalid. + auto phiPath = AccessPath::compute(phi); + if (phiPath.isValid()) { + assert(phiPath.getStorage().hasIdenticalBase(storage) + && "inconsistent phi storage"); + // If the phi paths have different offsets, its path has unknown offset. + if (phiPath.getOffset() == AccessPath::UnknownOffset) { + if (!collectOverlappingUses) + return true; + dfs.offset = AccessPath::UnknownOffset; + } + pushUsers(phi, dfs); + return true; + } + // The branch will be pushed onto the normal user list. + return false; } - SILValue visitPhi(SILPhiArgument *phiArg) { - // Cycles involving phis are only handled within FindPhiStorageVisitor. - // Path components are not allowed in phi cycles. - if (visitedPhis.insert(phiArg).second) { - FindPhiStorageVisitor(this->asImpl()).findPhiStorage(phiArg); - return SILValue(); + // Return the offset at the current DFS path cursor, or zero. + int getPathOffset(const DFSEntry &dfs) const { + if (dfs.pathCursor == 0 + || pathIndices[dfs.pathCursor - 1].isSubObjectProjection()) { + return 0; } - // Cannot treat unresolved phis as "unidentified" because they may alias - // with global or class access. - return visitNonAccess(phiArg); + return pathIndices[dfs.pathCursor - 1].getOffset(); } - SILValue visitCast(SingleValueInstruction *projectedAddr, - Operand *parentAddr) { - return parentAddr->get(); + // Returns true as long as the useLimit is not reached. + bool visitUser(DFSEntry dfs) { + Operand *use = dfs.getUse(); + assert(!(dfs.isRef() && use->get()->getType().isAddress())); + if (auto *svi = dyn_cast(use->getUser())) { + if (use->getOperandNumber() == 0 + && visitSingleValueUser(svi, dfs) == IgnoredUse) { + return true; + } + } + // We weren't able to "see through" any more address conversions; so + // record this as a use. + + // Do the path offsets match? + if (!checkAndUpdateOffset(dfs)) + return true; + + // Is this a full or partial path match? + if (!collectOverlappingUses && dfs.pathCursor > 0) + return true; + + // Record the use if we haven't reached the limit. + if (uses.size() == useLimit) + return false; + + uses.push_back(use); + return true; } - SILValue visitPathComponent(SingleValueInstruction *projectedAddr, - Operand *parentAddr) { - return parentAddr->get(); + // Return true if the accumulated offset matches the current path index. + // Update the DFSEntry and pathCursor to skip remaining offsets. + bool checkAndUpdateOffset(DFSEntry &dfs) { + int pathOffset = getPathOffset(dfs); + if (pathOffset == 0) { + // No offset is on the expected path. + if (collectOverlappingUses && dfs.offset == AccessPath::UnknownOffset) { + dfs.offset = 0; + } + return dfs.offset == 0; + } + // pop the offset from the expected path; there should only be one. + --dfs.pathCursor; + assert(getPathOffset(dfs) == 0 && "only one offset index allowed"); + + int useOffset = dfs.offset; + dfs.offset = 0; + + // Ignore all uses on this path unless we're collecting containing uses. + // UnknownOffset appears to overlap with all offsets and subobject uses. + if (pathOffset == AccessPath::UnknownOffset + || useOffset == AccessPath::UnknownOffset) { + return collectOverlappingUses; + } + // A known offset must match regardless of collectOverlappingUses. + return pathOffset == useOffset; } -}; -struct FindAccessedStorageVisitor - : public FindAccessedStorageVisitorBase { + enum UseKind { LeafUse, IgnoredUse }; + UseKind visitSingleValueUser(SingleValueInstruction *svi, DFSEntry dfs); - SILValue visitNestedAccess(BeginAccessInst *access) { - return access->getSource(); + // Handle non-index_addr projections. + UseKind followProjection(SingleValueInstruction *svi, DFSEntry dfs) { + if (!checkAndUpdateOffset(dfs)) + return IgnoredUse; + + if (dfs.pathCursor == 0) + return LeafUse; + + AccessPath::Index pathIndex = pathIndices[dfs.pathCursor - 1]; + auto projIdx = ProjectionIndex(svi); + assert(projIdx.isValid()); + // Only subobjects indices are expected because offsets are handled above. + if (projIdx.Index == pathIndex.getSubObjectIndex()) { + --dfs.pathCursor; + pushUsers(svi, dfs); + } + return IgnoredUse; } }; -struct IdentifyAccessedStorageVisitor - : public FindAccessedStorageVisitorBase {}; +} // end anonymous namespace -} // namespace +// During the def-use traversal, visit a single-value instruction in which the +// used address is at operand zero. +// +// This must handle the def-use side of all operations that +// AccessUseDefChainVisitor::visit can handle. +// +// Return IgnoredUse if the def-use traversal either continues past \p +// svi or ignores this use. +// +// FIXME: Reuse getAccessProjectionOperand() instead of using special cases once +// the unchecked_take_enum_data_addr -> load -> project_box pattern is fixed. +CollectAccessPathUses::UseKind +CollectAccessPathUses::visitSingleValueUser(SingleValueInstruction *svi, + DFSEntry dfs) { + if (isAccessedStorageCast(svi)) { + pushUsers(svi, dfs); + return IgnoredUse; + } + switch (svi->getKind()) { + default: + return LeafUse; -AccessedStorage swift::findAccessedStorage(SILValue sourceAddr) { - return FindAccessedStorageVisitor().findStorage(sourceAddr); + case SILInstructionKind::BeginAccessInst: + pushUsers(svi, dfs); + return IgnoredUse; + + // Handle ref_element_addr since we start at the object root instead of + // the access base. + case SILInstructionKind::RefElementAddrInst: + assert(dfs.isRef()); + assert(dfs.pathCursor > 0 && "ref_element_addr cannot occur within access"); + dfs.useAndIsRef.setInt(false); + return followProjection(svi, dfs); + + case SILInstructionKind::RefTailAddrInst: { + assert(dfs.isRef()); + assert(dfs.pathCursor > 0 && "ref_tail_addr cannot occur within an access"); + dfs.useAndIsRef.setInt(false); + --dfs.pathCursor; + AccessPath::Index pathIndex = pathIndices[dfs.pathCursor]; + assert(pathIndex.isSubObjectProjection()); + if (pathIndex.getSubObjectIndex() == AccessedStorage::TailIndex) + pushUsers(svi, dfs); + + return IgnoredUse; + } + + // MARK: Access projections + + case SILInstructionKind::StructElementAddrInst: + case SILInstructionKind::TupleElementAddrInst: + return followProjection(svi, dfs); + + case SILInstructionKind::IndexAddrInst: + case SILInstructionKind::TailAddrInst: { + auto projIdx = ProjectionIndex(svi); + if (projIdx.isValid()) { + if (dfs.offset != AccessPath::UnknownOffset) + dfs.offset += projIdx.Index; + else + assert(collectOverlappingUses); + } else if (collectOverlappingUses) { + dfs.offset = AccessPath::UnknownOffset; + } else { + return IgnoredUse; + } + pushUsers(svi, dfs); + return IgnoredUse; + } + + // open_existential_addr and unchecked_take_enum_data_addr are classified as + // access projections, but they also modify memory. Both see through them and + // also report them as uses. + case SILInstructionKind::OpenExistentialAddrInst: + case SILInstructionKind::UncheckedTakeEnumDataAddrInst: + pushUsers(svi, dfs); + return LeafUse; + + case SILInstructionKind::StructExtractInst: + // Handle nested access to a KeyPath projection. The projection itself + // uses a Builtin. However, the returned UnsafeMutablePointer may be + // converted to an address and accessed via an inout argument. + if (isUnsafePointerExtraction(cast(svi))) { + pushUsers(svi, dfs); + return IgnoredUse; + } + return LeafUse; + + case SILInstructionKind::LoadInst: + // Load a box from an indirect payload of an opaque enum. See comments + // in AccessUseDefChainVisitor::visit. Record this load as a leaf-use even + // when we look through its project_box because anyone inspecting the load + // itself will see the same AccessPath. + // FIXME: if this doesn't go away with opaque values, add a new instruction + // for load+project_box. + if (svi->getType().is()) { + Operand *addrOper = &cast(svi)->getOperandRef(); + assert(isa(addrOper->get())); + // Push the project_box uses + for (auto *use : svi->getUses()) { + if (isa(use->getUser())) + pushUser(DFSEntry(use, dfs.isRef(), dfs.pathCursor, dfs.offset)); + } + } + return LeafUse; + } } -AccessedStorage swift::identifyAccessedStorageImpl(SILValue sourceAddr) { - return IdentifyAccessedStorageVisitor().findStorage(sourceAddr); +bool AccessPath::collectUses(SmallVectorImpl &uses, + bool collectOverlappingUses, + unsigned useLimit) const { + return CollectAccessPathUses(*this, uses, collectOverlappingUses, useLimit) + .collectUses(); +} + +bool AccessPathWithBase::collectUses(SmallVectorImpl &uses, + bool collectOverlappingUses, + unsigned useLimit) const { + CollectAccessPathUses collector(accessPath, uses, collectOverlappingUses, + useLimit); + if (accessPath.getRoot()) + return std::move(collector).collectUses(); + + if (!base) + return false; + + return std::move(collector).collectUses(base); +} + +void AccessPath::Index::print(raw_ostream &os) const { + if (isSubObjectProjection()) + os << '#' << getSubObjectIndex(); + else { + os << '@'; + if (getOffset() == AccessPath::UnknownOffset) + os << "Unknown"; + else + os << getOffset(); + } +} + +LLVM_ATTRIBUTE_USED void AccessPath::Index::dump() const { + print(llvm::dbgs()); +} + +static void recursivelyPrintPath(AccessPath::PathNode node, raw_ostream &os) { + AccessPath::PathNode parent = node.getParent(); + if (!parent.isRoot()) { + recursivelyPrintPath(parent, os); + os << ","; + } + node.getIndex().print(os); +} + +void AccessPath::printPath(raw_ostream &os) const { + os << "Path: "; + if (!isValid()) { + os << "INVALID\n"; + return; + } + os << "("; + PathNode node = getPathNode(); + if (offset != 0) { + Index::forOffset(offset).print(os); + if (!node.isRoot()) + os << ","; + } + if (!node.isRoot()) + recursivelyPrintPath(node, os); + os << ")\n"; +} + +void AccessPath::print(raw_ostream &os) const { + if (!isValid()) { + os << "INVALID\n"; + return; + } + os << "Storage: "; + getStorage().print(os); + printPath(os); +} + +LLVM_ATTRIBUTE_USED void AccessPath::dump() const { print(llvm::dbgs()); } + +void AccessPathWithBase::print(raw_ostream &os) const { + if (base) + os << "Base: " << base; + + accessPath.print(os); +} + +LLVM_ATTRIBUTE_USED void AccessPathWithBase::dump() const { + print(llvm::dbgs()); } //===----------------------------------------------------------------------===// -// MARK: Helper API +// MARK: Helper API for specific formal access patterns //===----------------------------------------------------------------------===// static bool isScratchBuffer(SILValue value) { @@ -556,7 +1471,9 @@ bool swift::isExternalGlobalAddressor(ApplyInst *AI) { // Return true if the given StructExtractInst extracts the RawPointer from // Unsafe[Mutable]Pointer. bool swift::isUnsafePointerExtraction(StructExtractInst *SEI) { - assert(isa(SEI->getType().getASTType())); + if (!isa(SEI->getType().getASTType())) + return false; + auto &C = SEI->getModule().getASTContext(); auto *decl = SEI->getStructDecl(); return decl == C.getUnsafeMutablePointerDecl() @@ -652,9 +1569,9 @@ SILBasicBlock::iterator swift::removeBeginAccess(BeginAccessInst *beginAccess) { // Verification //===----------------------------------------------------------------------===// -/// Helper for visitApplyAccesses that visits address-type call arguments, -/// including arguments to @noescape functions that are passed as closures to -/// the current call. +// Helper for visitApplyAccesses that visits address-type call arguments, +// including arguments to @noescape functions that are passed as closures to +// the current call. static void visitApplyAccesses(ApplySite apply, llvm::function_ref visitor) { for (Operand &oper : apply.getArgumentOperands()) { @@ -686,14 +1603,27 @@ static void visitBuiltinAddress(BuiltinInst *builtin, builtin->dump(); llvm_unreachable("unexpected builtin memory access."); - // WillThrow exists for the debugger, does nothing. + // Handle builtin "generic_add"($*V, $*V, $*V) and the like. +#define BUILTIN(Id, Name, Attrs) +#define BUILTIN_BINARY_OPERATION_POLYMORPHIC(Id, Name) \ + case BuiltinValueKind::Id: + +#include "swift/AST/Builtins.def" + + visitor(&builtin->getAllOperands()[1]); + visitor(&builtin->getAllOperands()[2]); + return; + + // WillThrow exists for the debugger, does nothing. case BuiltinValueKind::WillThrow: return; - // Buitins that affect memory but can't be formal accesses. + // Buitins that affect memory but can't be formal accesses. + case BuiltinValueKind::AssumeTrue: case BuiltinValueKind::UnexpectedError: case BuiltinValueKind::ErrorInMain: case BuiltinValueKind::IsOptionalType: + case BuiltinValueKind::CondFailMessage: case BuiltinValueKind::AllocRaw: case BuiltinValueKind::DeallocRaw: case BuiltinValueKind::Fence: @@ -703,14 +1633,15 @@ static void visitBuiltinAddress(BuiltinInst *builtin, case BuiltinValueKind::Unreachable: case BuiltinValueKind::CondUnreachable: case BuiltinValueKind::DestroyArray: - case BuiltinValueKind::COWBufferForReading: case BuiltinValueKind::UnsafeGuaranteed: case BuiltinValueKind::UnsafeGuaranteedEnd: case BuiltinValueKind::Swift3ImplicitObjCEntrypoint: + case BuiltinValueKind::PoundAssert: + case BuiltinValueKind::IntInstrprofIncrement: case BuiltinValueKind::TSanInoutAccess: return; - // General memory access to a pointer in first operand position. + // General memory access to a pointer in first operand position. case BuiltinValueKind::CmpXChg: case BuiltinValueKind::AtomicLoad: case BuiltinValueKind::AtomicStore: @@ -720,8 +1651,8 @@ static void visitBuiltinAddress(BuiltinInst *builtin, // visitor(&builtin->getAllOperands()[0]); return; - // Arrays: (T.Type, Builtin.RawPointer, Builtin.RawPointer, - // Builtin.Word) + // Arrays: (T.Type, Builtin.RawPointer, Builtin.RawPointer, + // Builtin.Word) case BuiltinValueKind::CopyArray: case BuiltinValueKind::TakeArrayNoAlias: case BuiltinValueKind::TakeArrayFrontToBack: @@ -811,11 +1742,11 @@ void swift::visitAccessedAddress(SILInstruction *I, visitor(&I->getAllOperands()[0]); return; + case SILInstructionKind::InitExistentialAddrInst: + case SILInstructionKind::InjectEnumAddrInst: #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Load##Name##Inst: #include "swift/AST/ReferenceStorage.def" - case SILInstructionKind::InitExistentialAddrInst: - case SILInstructionKind::InjectEnumAddrInst: case SILInstructionKind::LoadInst: case SILInstructionKind::LoadBorrowInst: case SILInstructionKind::OpenExistentialAddrInst: @@ -845,6 +1776,8 @@ void swift::visitAccessedAddress(SILInstruction *I, case SILInstructionKind::BeginAccessInst: case SILInstructionKind::BeginApplyInst: case SILInstructionKind::BeginBorrowInst: + case SILInstructionKind::BeginCOWMutationInst: + case SILInstructionKind::EndCOWMutationInst: case SILInstructionKind::BeginUnpairedAccessInst: case SILInstructionKind::BindMemoryInst: case SILInstructionKind::CheckedCastValueBranchInst: @@ -863,6 +1796,7 @@ void swift::visitAccessedAddress(SILInstruction *I, case SILInstructionKind::EndLifetimeInst: case SILInstructionKind::ExistentialMetatypeInst: case SILInstructionKind::FixLifetimeInst: + case SILInstructionKind::GlobalAddrInst: case SILInstructionKind::InitExistentialValueInst: case SILInstructionKind::IsUniqueInst: case SILInstructionKind::IsEscapingClosureInst: diff --git a/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp b/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp index 1f5e84803d5b9..b5aea5103bc67 100644 --- a/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp +++ b/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp @@ -453,12 +453,20 @@ bool LoadBorrowNeverInvalidatedAnalysis:: bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated( LoadBorrowInst *lbi) { - SILValue address = getAddressAccess(lbi->getOperand()); + + SILValue address = getAccessBegin(lbi->getOperand()); if (!address) return false; + auto storage = findAccessedStorage(address); + // If we couldn't find an access storage, return that we are assumed to write. + if (!storage) { + llvm::errs() << "Couldn't compute access storage?!\n"; + return false; + } + // If we have a let address, then we are already done. - if (isLetAddress(stripAccessMarkers(address))) + if (storage.isLetAccess(lbi->getFunction())) return true; // At this point, we know that we /may/ have writes. Now we go through various @@ -478,24 +486,34 @@ bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated( // Otherwise, validate that any writes to our begin_access is not when the // load_borrow's result is live. + // + // FIXME: do we verify that the load_borrow scope is always nested within + // the begin_access scope (to ensure no aliasing access)? return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, bai); } + // FIXME: the subsequent checks appear to assume that 'address' is not aliased + // within the scope of the load_borrow. This can only be assumed when either + // the load_borrow is nested within an access scope or when the + // storage.isUniquelyIdentified() and all uses of storage.getRoot() have been + // analyzed. The later can be done with AccessPath::collectUses(). + // Check if our unidentified access storage is a project_box. In such a case, // validate that all uses of the project_box are not writes that overlap with // our load_borrow's result. These are things that may not be a formal access // base. // - // FIXME: we don't seem to verify anywhere that a pointer_to_address cannot - // itself be derived from another address that is accessible in the same - // function, either because it was returned from a call or directly - // address_to_pointer'd. + // FIXME: Remove this PointerToAddress check. It appears to be incorrect. we + // don't verify anywhere that a pointer_to_address cannot itself be derived + // from another address that is accessible in the same function, either + // because it was returned from a call or directly address_to_pointer'd. if (isa(address)) { return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, address); } + // FIXME: This ProjectBoxInst // If we have a project_box, we need to see if our base, modulo begin_borrow, // copy_value have any other project_box that we need to analyze. if (auto *pbi = dyn_cast(address)) { @@ -503,13 +521,6 @@ bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated( pbi->getOperand()); } - auto storage = findAccessedStorage(address); - - // If we couldn't find an access storage, return that we are assumed to write. - if (!storage) { - llvm::errs() << "Couldn't compute access storage?!\n"; - return false; - } switch (storage.getKind()) { case AccessedStorage::Stack: { @@ -562,7 +573,8 @@ bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated( } case AccessedStorage::Unidentified: { // Otherwise, we didn't understand this, so bail. - llvm::errs() << "Unidentified access storage: " << storage; + llvm::errs() << "Unidentified access storage: "; + storage.dump(); return false; } case AccessedStorage::Nested: { diff --git a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp index f9b28518454ca..44c40297e4b2c 100644 --- a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp @@ -334,7 +334,7 @@ static bool isAccessedAddressTBAASafe(SILValue V) { if (!V->getType().isAddress()) return false; - SILValue accessedAddress = getAccessedAddress(V); + SILValue accessedAddress = getAccessAddress(V); if (isa(accessedAddress)) return true; diff --git a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp index b867c18b6300a..376c9a220313d 100644 --- a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp +++ b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp @@ -78,11 +78,13 @@ class MemoryBehaviorVisitor } /// If 'V' is an address projection within a formal access, return the - /// canonical address of the formal access. Otherwise, return 'V' itself, - /// which is either a reference or unknown pointer or address. + /// canonical address of the formal access if possible without looking past + /// any storage casts. Otherwise, a "best-effort" address + /// + /// If 'V' is an address, then the returned value is also an address. SILValue getValueAddress() { if (!cachedValueAddress) { - cachedValueAddress = V->getType().isAddress() ? getAccessedAddress(V) : V; + cachedValueAddress = V->getType().isAddress() ? getAccessAddress(V) : V; } return cachedValueAddress; } @@ -147,7 +149,7 @@ class MemoryBehaviorVisitor case SILAccessKind::Modify: if (isLetValue()) { - assert(stripAccessMarkers(beginAccess) != getValueAddress() + assert(getAccessBase(beginAccess) != getValueAddress() && "let modification not allowed"); return MemBehavior::None; } @@ -251,8 +253,7 @@ MemBehavior MemoryBehaviorVisitor::visitLoadInst(LoadInst *LI) { MemBehavior MemoryBehaviorVisitor::visitStoreInst(StoreInst *SI) { // No store besides the initialization of a "let"-variable // can have any effect on the value of this "let" variable. - if (isLetValue() - && (getAccessedAddress(SI->getDest()) != getValueAddress())) { + if (isLetValue() && (getAccessBase(SI->getDest()) != getValueAddress())) { return MemBehavior::None; } // If the store dest cannot alias the pointer in question, then the diff --git a/lib/SILOptimizer/LoopTransforms/LICM.cpp b/lib/SILOptimizer/LoopTransforms/LICM.cpp index 39c4628324e7c..6eb5e30a7f988 100644 --- a/lib/SILOptimizer/LoopTransforms/LICM.cpp +++ b/lib/SILOptimizer/LoopTransforms/LICM.cpp @@ -711,7 +711,7 @@ static bool analyzeBeginAccess(BeginAccessInst *BI, return true; } return BIAccessedStorageNonNested.isDistinctFrom( - findAccessedStorage(OtherBI)); + findAccessedStorage(OtherBI)); }; if (!std::all_of(BeginAccesses.begin(), BeginAccesses.end(), safeBeginPred)) diff --git a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp index eace1f0eac6bb..674036973164d 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp @@ -964,20 +964,20 @@ static void checkStaticExclusivity(SILFunction &Fn, PostOrderFunctionInfo *PO, // Check that the given address-type operand is guarded by begin/end access // markers. static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) { - SILValue address = getAddressAccess(memOper->get()); + SILValue accessBegin = getAccessBegin(memOper->get()); SILInstruction *memInst = memOper->getUser(); - auto error = [address, memInst]() { + auto error = [accessBegin, memInst]() { llvm::dbgs() << "Memory access not protected by begin_access:\n"; memInst->printInContext(llvm::dbgs()); - llvm::dbgs() << "Accessing: " << address; + llvm::dbgs() << "Accessing: " << accessBegin; llvm::dbgs() << "In function:\n"; memInst->getFunction()->print(llvm::dbgs()); abort(); }; // Check if this address is guarded by an access. - if (auto *BAI = dyn_cast(address)) { + if (auto *BAI = dyn_cast(accessBegin)) { if (BAI->getEnforcement() == SILAccessEnforcement::Unsafe) return; @@ -1017,7 +1017,7 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) { return; } - const AccessedStorage &storage = findAccessedStorage(address); + const AccessedStorage &storage = findAccessedStorage(accessBegin); // findAccessedStorage may return an invalid storage object if the address // producer is not recognized by its allowlist. For the purpose of // verification, we assume that this can only happen for local From 1e9c4d13ad7caf62665ef996e898ec21f70e3fa1 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 3 Sep 2020 13:02:48 -0700 Subject: [PATCH 520/745] Update LoadCopyToLoadBorrow for MemAccessUtils API. --- lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp b/lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp index 157e21c3a9406..38d6304030741 100644 --- a/lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp +++ b/lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp @@ -266,8 +266,13 @@ class StorageGuaranteesLoadVisitor return next(parentAddr->get()); } - void visitPathComponent(SingleValueInstruction *projectedAddr, - Operand *parentAddr) { + void visitStorageCast(SingleValueInstruction *projectedAddr, + Operand *parentAddr) { + return next(parentAddr->get()); + } + + void visitAccessProjection(SingleValueInstruction *projectedAddr, + Operand *parentAddr) { return next(parentAddr->get()); } From 7e435fa2de512117028e202ea001c28724674a80 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 27 Aug 2020 17:46:58 -0700 Subject: [PATCH 521/745] Add AccessPath support to the AccessedStorageDumper pass. Add a flag: enable-accessed-storage-dump-uses --- .../UtilityPasses/AccessedStorageDumper.cpp | 58 ++++++++++++++----- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp b/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp index ee5208a4bc6aa..31685d7a1c6bf 100644 --- a/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp @@ -1,8 +1,8 @@ -//===--- AccessedStorageDumper.cpp - Dump accessed storage for functions ---===// +//===--- AccessedStorageDumper.cpp - Dump accessed storage ----------------===// // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -17,24 +17,52 @@ #include "swift/SIL/SILValue.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" using namespace swift; -static void dumpAccessedStorage(SILInstruction *inst) { - visitAccessedAddress( - inst, - [&](Operand *operand) { - inst->print(llvm::outs()); - findAccessedStorage(operand->get()).print(llvm::outs()); - } - ); -} +static llvm::cl::opt EnableDumpUses( + "enable-accessed-storage-dump-uses", llvm::cl::init(false), + llvm::cl::desc("With --sil-access-storage-dumper, dump all uses")); namespace { /// Dumps sorage information for each access. class AccessedStorageDumper : public SILModuleTransform { + llvm::SmallVector uses; + + void dumpAccessedStorage(Operand *operand) { + findAccessedStorage(operand->get()).print(llvm::outs()); + auto pathAndBase = AccessPathWithBase::compute(operand->get()); + pathAndBase.print(llvm::outs()); + + if (!pathAndBase.accessPath.isValid() || !EnableDumpUses) + return; + + uses.clear(); + pathAndBase.collectUses(uses, /*collectContainingUses*/ false); + llvm::outs() << "Exact Uses {\n"; + for (auto *useOperand : uses) { + llvm::outs() << *useOperand->getUser() << " "; + auto usePathAndBase = AccessPathWithBase::compute(useOperand->get()); + usePathAndBase.accessPath.printPath(llvm::outs()); + assert(pathAndBase.accessPath.contains(usePathAndBase.accessPath) + && "access path does not contain use access path"); + } + llvm::outs() << "}\n"; + uses.clear(); + pathAndBase.collectUses(uses, /*collectContainingUses*/ true); + llvm::outs() << "Overlapping Uses {\n"; + for (auto *useOperand : uses) { + llvm::outs() << *useOperand->getUser() << " "; + auto usePathAndBase = AccessPathWithBase::compute(useOperand->get()); + usePathAndBase.accessPath.printPath(llvm::outs()); + assert(pathAndBase.accessPath.mayOverlap(usePathAndBase.accessPath) + && "access path does not contain use access path"); + } + llvm::outs() << "}\n"; + } void run() override { for (auto &fn : *getModule()) { @@ -45,8 +73,12 @@ class AccessedStorageDumper : public SILModuleTransform { } for (auto &bb : fn) { for (auto &inst : bb) { - if (inst.mayReadOrWriteMemory()) - dumpAccessedStorage(&inst); + if (inst.mayReadOrWriteMemory()) { + llvm::outs() << "###For MemOp: " << inst; + visitAccessedAddress(&inst, [this](Operand *operand) { + dumpAccessedStorage(operand); + }); + } } } } From b2d1ac16315b2ecda356d0b873b29744eca675ce Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 27 Aug 2020 17:40:45 -0700 Subject: [PATCH 522/745] Add AccessPathVerification pass and run it in the pipeline. --- include/swift/SIL/MemAccessUtils.h | 2 + .../swift/SILOptimizer/PassManager/Passes.def | 2 + lib/SIL/Utils/MemAccessUtils.cpp | 2 +- lib/SILOptimizer/PassManager/PassPipeline.cpp | 10 ++ .../UtilityPasses/AccessPathVerification.cpp | 135 ++++++++++++++++++ lib/SILOptimizer/UtilityPasses/CMakeLists.txt | 1 + 6 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 lib/SILOptimizer/UtilityPasses/AccessPathVerification.cpp diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index a65effe525448..e6afa239c528e 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -844,6 +844,8 @@ class AccessPath { int getOffset() const { return offset; } + bool hasUnknownOffset() const { return offset == UnknownOffset; } + /// Return true if this path contains \p subPath. bool contains(AccessPath subPath) const; diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 4c640d63981c9..79cf3644b3f8b 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -70,6 +70,8 @@ PASS(AccessSummaryDumper, "access-summary-dump", "Dump Address Parameter Access Summary") PASS(AccessedStorageAnalysisDumper, "accessed-storage-analysis-dump", "Dump Accessed Storage Analysis Summaries") +PASS(AccessPathVerification, "access-path-verification", + "Verify Access Paths (and Accessed Storage)") PASS(AccessedStorageDumper, "accessed-storage-dump", "Dump Accessed Storage Information") PASS(AccessMarkerElimination, "access-marker-elim", diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index c34ae23d3fc93..05470396d48ac 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -1274,7 +1274,7 @@ void AccessPath::Index::print(raw_ostream &os) const { os << '#' << getSubObjectIndex(); else { os << '@'; - if (getOffset() == AccessPath::UnknownOffset) + if (isUnknownOffset()) os << "Unknown"; else os << getOffset(); diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 57038b5b5ac67..9ebe5b37eadf9 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -423,6 +423,11 @@ static void addPerfDebugSerializationPipeline(SILPassPipelinePlan &P) { static void addPrepareOptimizationsPipeline(SILPassPipelinePlan &P) { P.startPipeline("PrepareOptimizationPasses"); + // Verify AccessedStorage once in OSSA before optimizing. +#ifndef NDEBUG + P.addAccessPathVerification(); +#endif + P.addForEachLoopUnroll(); P.addMandatoryCombine(); P.addAccessMarkerElimination(); @@ -670,6 +675,11 @@ static void addLastChanceOptPassPipeline(SILPassPipelinePlan &P) { // A loop might have only one dynamic access now, i.e. hoistable P.addLICM(); + // Verify AccessedStorage once again after optimizing and lowering OSSA. +#ifndef NDEBUG + P.addAccessPathVerification(); +#endif + // Only has an effect if the -assume-single-thread option is specified. P.addAssumeSingleThreaded(); diff --git a/lib/SILOptimizer/UtilityPasses/AccessPathVerification.cpp b/lib/SILOptimizer/UtilityPasses/AccessPathVerification.cpp new file mode 100644 index 0000000000000..261115d957ab7 --- /dev/null +++ b/lib/SILOptimizer/UtilityPasses/AccessPathVerification.cpp @@ -0,0 +1,135 @@ +//===--- AccessPathVerification.cpp - verify access paths and storage -----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +/// +/// Verify AccessPath computation. For the address of every memory operation in +/// the module, compute the access path, compute all the users of that path, +/// then verify that all users have the same access path. +/// +/// This is potentially expensive, so there is a fast mode that limits the +/// number of uses visited per path. +/// +/// During full verification, also check that all addresses that share an +/// AccessPath are covered when computed the use list of that AccessPath. This +/// is important because optimizations may assume that the use list is +/// complete. +/// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "access-path-verification" +#include "swift/SIL/MemAccessUtils.h" +#include "swift/SIL/PrettyStackTrace.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILValue.h" +#include "swift/SILOptimizer/PassManager/Passes.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "llvm/Support/Debug.h" + +using namespace swift; + +namespace { + +/// Verify access path and uses of each access. +class AccessPathVerification : public SILModuleTransform { + llvm::DenseMap useToPathMap; + + // Transient uses + llvm::SmallVector uses; + +public: + void verifyAccessPath(Operand *operand) { + auto pathAndBase = AccessPathWithBase::compute(operand->get()); + auto accessPath = pathAndBase.accessPath; + if (!accessPath.isValid()) + return; + + auto iterAndInserted = useToPathMap.try_emplace(operand, accessPath); + // If this use was already computed from a previously visited path, make + // sure the path we just computed matches. + if (!iterAndInserted.second) { + auto collectedFromPath = iterAndInserted.first->second; + if (collectedFromPath != accessPath) { + llvm::errs() << "Address use: " << *operand->getUser() + << " collected from path\n "; + collectedFromPath.dump(); + llvm::errs() << " has different path\n "; + accessPath.dump(); + operand->getUser()->getFunction()->dump(); + assert(false && "computed path does not match collected path"); + } + return; + } + // This is a new path, so map all its uses. + assert(uses.empty()); + pathAndBase.collectUses(uses, /*collectContainingUses*/ false); + bool foundOperandUse = false; + for (Operand *use : uses) { + if (use == operand) { + foundOperandUse = true; + continue; + } + // (live) subobject projections within an access will be mapped later as a + // separate path. + switch (use->getUser()->getKind()) { + default: + break; + case SILInstructionKind::StructElementAddrInst: + case SILInstructionKind::TupleElementAddrInst: + case SILInstructionKind::IndexAddrInst: + continue; + } + auto iterAndInserted = useToPathMap.try_emplace(use, accessPath); + if (!iterAndInserted.second) { + llvm::errs() << "Address use: " << *operand->getUser() + << " with path...\n"; + pathAndBase.dump(); + llvm::errs() << " was not collected for: " << *use->getUser(); + llvm::errs() << " with path...\n"; + auto computedPath = iterAndInserted.first->second; + computedPath.dump(); + use->getUser()->getFunction()->dump(); + assert(false && "missing collected use"); + } + } + if (!foundOperandUse && !accessPath.hasUnknownOffset()) { + llvm::errs() << "Address use: " << *operand->getUser() + << " is not a use of path\n "; + accessPath.dump(); + assert(false && "not a user of its own computed path "); + } + uses.clear(); + } + + void run() override { + for (auto &fn : *getModule()) { + if (fn.empty()) + continue; + + PrettyStackTraceSILFunction functionDumper("...", &fn); + for (auto &bb : fn) { + for (auto &inst : bb) { + if (inst.mayReadOrWriteMemory()) + visitAccessedAddress(&inst, [this](Operand *operand) { + return verifyAccessPath(operand); + }); + } + } + useToPathMap.clear(); + } + } +}; + +} // end anonymous namespace + +SILTransform *swift::createAccessPathVerification() { + return new AccessPathVerification(); +} diff --git a/lib/SILOptimizer/UtilityPasses/CMakeLists.txt b/lib/SILOptimizer/UtilityPasses/CMakeLists.txt index 871ae9f29d3ec..0fbf885ed8e7b 100644 --- a/lib/SILOptimizer/UtilityPasses/CMakeLists.txt +++ b/lib/SILOptimizer/UtilityPasses/CMakeLists.txt @@ -1,6 +1,7 @@ target_sources(swiftSILOptimizer PRIVATE AADumper.cpp AccessSummaryDumper.cpp + AccessPathVerification.cpp AccessedStorageAnalysisDumper.cpp AccessedStorageDumper.cpp BasicCalleePrinter.cpp From 800c061d00d57f0afeb330e5585312a3d73971c2 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 27 Aug 2020 17:47:41 -0700 Subject: [PATCH 523/745] accessed_storage.sil tests --- test/SILOptimizer/accessed_storage.sil | 318 ++++++++++++++++++++++++- 1 file changed, 316 insertions(+), 2 deletions(-) diff --git a/test/SILOptimizer/accessed_storage.sil b/test/SILOptimizer/accessed_storage.sil index 37e26e17b348b..26c9775556a1a 100644 --- a/test/SILOptimizer/accessed_storage.sil +++ b/test/SILOptimizer/accessed_storage.sil @@ -1,4 +1,7 @@ // RUN: %target-sil-opt %s -accessed-storage-dump -enable-sil-verify-all -o /dev/null | %FileCheck %s +// RUN: %target-sil-opt %s -access-path-verification -o /dev/null + +// REQUIRES: PTRSIZE=64 sil_stage canonical @@ -14,6 +17,9 @@ struct MyStruct { // CHECK-LABEL: @testStructPhiCommon // CHECK: store // CHECK: Argument %0 = argument of bb0 : $*MyStruct +// CHECK: Base: %0 = argument of bb0 : $*MyStruct +// CHECK: Storage: Argument %0 = argument of bb0 : $*MyStruct +// CHECK: Path: (#0) sil hidden @testStructPhiCommon : $@convention(thin) (@inout MyStruct) -> () { bb0(%0 : $*MyStruct): %2 = struct_element_addr %0 : $*MyStruct, #MyStruct.i @@ -42,6 +48,7 @@ bb3(%6 : $Builtin.RawPointer) : // CHECK-LABEL: @testStructPhiDivergent // CHECK: store // CHECK: INVALID +// CHECK: INVALID sil hidden @testStructPhiDivergent : $@convention(thin) (@inout MyStruct) -> () { bb0(%0 : $*MyStruct): cond_br undef, bb1, bb2 @@ -75,6 +82,7 @@ bb3(%6 : $Builtin.RawPointer) : // CHECK-LABEL: @testStructPhiChained // CHECK: store // CHECK: INVALID +// CHECK: INVALID sil hidden @testStructPhiChained : $@convention(thin) (@inout MyStruct, @inout Int64) -> () { bb0(%0 : $*MyStruct, %1 : $*Int64): cond_br undef, bb1, bb5 @@ -123,9 +131,15 @@ struct MyArray { // CHECK-LABEL: @arrayValue // CHECK: load [trivial] %{{.*}} : $*Builtin.Int64 -// CHECK: Tail %{{.*}} = unchecked_ref_cast [[REF:%[0-9]+]] : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Base: %{{.*}} = ref_tail_addr [immutable] %{{.*}} : $__ContiguousArrayStorageBase, $Int64 +// CHECK: Storage: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Path: (@3,#0) // CHECK: load [trivial] %{{.*}} : $*Builtin.Int64 -// CHECK: Tail %{{.*}} = unchecked_ref_cast [[REF:%[0-9]+]] : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Base: %{{.*}} = ref_tail_addr [immutable] %{{.*}} : $__ContiguousArrayStorageBase, $Int64 +// CHECK: Storage: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Path: (@4,#0) sil [ossa] @arrayValue : $@convention(thin) (@guaranteed MyArray) -> Int64 { bb0(%0 : @guaranteed $MyArray): %1 = integer_literal $Builtin.Word, 3 @@ -149,3 +163,303 @@ bb0(%0 : @guaranteed $MyArray): %19 = struct $Int64 (%16 : $Builtin.Int64) return %19 : $Int64 } + +// CHECK-LABEL: @staticIndexAddrChain +// CHECK: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Base: %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Path: (@1,#0) +// CHECK: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK: Argument %{{.*}} = argument of bb0 : $Builtin.RawPointer +// CHECK: Base: %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Path: (@2,#0) +sil [ossa] @staticIndexAddrChain : $@convention(thin) (Builtin.RawPointer) -> () { +bb0(%0 : $Builtin.RawPointer): + %1 = pointer_to_address %0 : $Builtin.RawPointer to $*MyStruct + %2 = integer_literal $Builtin.Word, 1 + %3 = index_addr %1 : $*MyStruct, %2 : $Builtin.Word + %4 = struct_element_addr %3 : $*MyStruct, #MyStruct.i + %5 = load [trivial] %4 : $*Int64 + %6 = index_addr %3 : $*MyStruct, %2 : $Builtin.Word + %7 = struct_element_addr %6 : $*MyStruct, #MyStruct.i + %8 = load [trivial] %7 : $*Int64 + %99 = tuple () + return %99 : $() +} + +// CHECK-LABEL: @staticIndexAddrSubobject +// CHECK: ###For MemOp: %5 = load [trivial] %4 : $*Int64 +// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer // user: %1 +// CHECK: INVALID +sil [ossa] @staticIndexAddrSubobject : $@convention(thin) (Builtin.RawPointer) -> () { +bb0(%0 : $Builtin.RawPointer): + %1 = pointer_to_address %0 : $Builtin.RawPointer to $*MyStruct + %2 = struct_element_addr %1 : $*MyStruct, #MyStruct.i + %3 = integer_literal $Builtin.Word, 1 + %4 = index_addr %2 : $*Int64, %3 : $Builtin.Word + %5 = load [trivial] %4 : $*Int64 + %99 = tuple () + return %99 : $() +} + +// CHECK-LABEL: @dynamicIndexAddrChain +// CHECK: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Base: %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Path: (@Unknown,#0) +// CHECK: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Base: %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Path: (@Unknown,#0) +sil [ossa] @dynamicIndexAddrChain : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> () { +bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word): + %2 = pointer_to_address %0 : $Builtin.RawPointer to $*MyStruct + %3 = index_addr %2 : $*MyStruct, %1 : $Builtin.Word + %4 = struct_element_addr %3 : $*MyStruct, #MyStruct.i + %5 = load [trivial] %4 : $*Int64 + %6 = integer_literal $Builtin.Word, 1 + %7 = index_addr %3 : $*MyStruct, %6 : $Builtin.Word + %8 = struct_element_addr %7 : $*MyStruct, #MyStruct.i + %9 = load [trivial] %8 : $*Int64 + %99 = tuple () + return %99 : $() +} + +// CHECK-LABEL: @staticIndexAddrCancel +// CHECK: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Base: %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Path: (@1,#0) +// CHECK: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Base: %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Path: (@2,#0) +sil [ossa] @staticIndexAddrCancel : $@convention(thin) (Builtin.RawPointer) -> () { +bb0(%0 : $Builtin.RawPointer): + %1 = pointer_to_address %0 : $Builtin.RawPointer to $*MyStruct + %2 = integer_literal $Builtin.Word, 1 + %3 = index_addr %1 : $*MyStruct, %2 : $Builtin.Word + %4 = struct_element_addr %3 : $*MyStruct, #MyStruct.i + %5 = load [trivial] %4 : $*Int64 + %6 = integer_literal $Builtin.Word, -1 + %7 = index_addr %3 : $*MyStruct, %2 : $Builtin.Word + %8 = struct_element_addr %7 : $*MyStruct, #MyStruct.i + %9 = load [trivial] %8 : $*Int64 + %99 = tuple () + return %99 : $() +} + +class A { + var prop0: Int64 +} +class B : A { + var prop1: Int64 +} + +// CHECK-LABEL: @testNonUniquePropertyIndex +// CHECK: store %0 to %{{.*}} : $*Int64 +// CHECK: Class %{{.*}} = alloc_ref $B +// CHECK: Field: var prop1: Int64 Index: 1 +// CHECK: Base: %{{.*}} = ref_element_addr %{{.*}} : $B, #B.prop1 +// CHECK: Storage: Class %{{.*}} = alloc_ref $B +// CHECK: Field: var prop1: Int64 Index: 1 +// CHECK: Path: () +// CHECK: store %0 to %{{.*}} : $*Int64 +// CHECK: Class %{{.*}} = upcast %{{.*}} : $B to $A +// CHECK: Field: var prop0: Int64 Index: 0 +// CHECK: Base: %{{.*}} = ref_element_addr %{{.*}} : $A, #A.prop0 +// CHECK: Storage: Class %{{.*}} = upcast %{{.*}} : $B to $A +// CHECK: Field: var prop0: Int64 Index: 0 +// CHECK: Path: () +sil @testNonUniquePropertyIndex : $@convention(thin) (Int64) -> () { +bb0(%0 : $Int64): + %1 = alloc_ref $B + %2 = ref_element_addr %1 : $B, #B.prop1 + store %0 to %2 : $*Int64 + %4 = upcast %1 : $B to $A + %5 = ref_element_addr %4 : $A, #A.prop0 + store %0 to %5 : $*Int64 + %99 = tuple () + return %99 : $() +} + +// CHECK-LABEL: @testRefTailAndStruct0 +// CHECK: %{{.*}} = load %{{.*}} : $*Builtin.Int64 +// CHECK: Class %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Field: @usableFromInline final var countAndCapacity: _ArrayBody Index: 0 +// CHECK: Base: %{{.*}} = ref_element_addr [immutable] %{{.*}} : $__ContiguousArrayStorageBase, #__ContiguousArrayStorageBase.countAndCapacity +// CHECK: Storage: Class %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Field: @usableFromInline final var countAndCapacity: _ArrayBody Index: 0 +// CHECK: Path: (#0,#0,#0) +// CHECK: %{{.*}} = load %{{.*}} : $*_StringGuts +// CHECK: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Base: %{{.*}} = ref_tail_addr [immutable] %{{.*}} : $__ContiguousArrayStorageBase, $String +// CHECK: Storage: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Path: (#0) +// CHECK: %{{.*}} = load %{{.*}} : $*String +// CHECK: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Base: %{{.*}} = ref_tail_addr [immutable] %{{.*}} : $__ContiguousArrayStorageBase, $String +// CHECK: Storage: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Path: () +sil hidden [noinline] @testRefTailAndStruct0 : $@convention(thin) (@owned MyArray) -> () { +bb0(%0 : $MyArray): + %1 = struct_extract %0 : $MyArray, #MyArray._buffer + %2 = struct_extract %1 : $_MyArrayBuffer, #_MyArrayBuffer._storage + %3 = struct_extract %2 : $_MyBridgeStorage, #_MyBridgeStorage.rawValue + %4 = unchecked_ref_cast %3 : $Builtin.BridgeObject to $__ContiguousArrayStorageBase + %5 = ref_element_addr [immutable] %4 : $__ContiguousArrayStorageBase, #__ContiguousArrayStorageBase.countAndCapacity + %6 = struct_element_addr %5 : $*_ArrayBody, #_ArrayBody._storage + %7 = struct_element_addr %6 : $*_SwiftArrayBodyStorage, #_SwiftArrayBodyStorage.count + %8 = struct_element_addr %7 : $*Int, #Int._value + %9 = load %8 : $*Builtin.Int64 + %10 = ref_tail_addr [immutable] %4 : $__ContiguousArrayStorageBase, $String + %11 = struct_element_addr %10 : $*String, #String._guts + %12 = load %11 : $*_StringGuts + %13 = load %10 : $*String + %14 = tuple () + return %14 : $() +} + +// CHECK-LABEL: @testPointerDynamicIndex +// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct +// CHECK: Base: %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Path: (@Unknown) +sil [serialized] [ossa] @testPointerDynamicIndex : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> () { +bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word): + %2 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*MyStruct + %3 = index_addr %2 : $*MyStruct, %1 : $Builtin.Word + %4 = address_to_pointer %3 : $*MyStruct to $Builtin.RawPointer + %5 = pointer_to_address %4 : $Builtin.RawPointer to [strict] $*MyStruct + %6 = load [trivial] %5 : $*MyStruct + %7 = tuple () + return %7 : $() +} + +// CHECK-LABEL: @testAddressToPointer +// CHECK: ###For MemOp: %3 = load [trivial] %{{.*}} : $*MyStruct +// CHECK: Base: %0 = argument of bb0 : $*MyStruct +// CHECK: Storage: Argument %0 = argument of bb0 : $*MyStruct +// CHECK: Path: () +// CHECK: ###For MemOp: %5 = load [trivial] %4 : $*Int64 +// CHECK: Base: %0 = argument of bb0 : $*MyStruct +// CHECK: Storage: Argument %0 = argument of bb0 : $*MyStruct +// CHECK: Path: (#0) +sil [serialized] [ossa] @testAddressToPointer : $@convention(thin) (@in MyStruct) -> () { +bb18(%0 : $*MyStruct): + %1 = address_to_pointer %0 : $*MyStruct to $Builtin.RawPointer + %2 = pointer_to_address %1 : $Builtin.RawPointer to [strict] $*MyStruct + %3 = load [trivial] %2 : $*MyStruct + %4 = struct_element_addr %0 : $*MyStruct, #MyStruct.i + %5 = load [trivial] %4 : $*Int64 + %6 = tuple () + return %6 : $() +} + +// CHECK-LABEL: @testIndexLoop +// CHECK: ###For MemOp: %3 = load %2 : $*AnyObject +// CHECK: Base: %1 = struct_extract %0 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue +// CHECK: Storage: Unidentified %1 = struct_extract %0 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue +// CHECK: Path: () +// CHECK: ###For MemOp: %7 = load %6 : $*AnyObject +// CHECK: Base: %1 = struct_extract %0 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue +// CHECK: Storage: Unidentified %1 = struct_extract %0 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue +// CHECK: Path: (@Unknown) +// CHECK: ###For MemOp: store %7 to %6 : $*AnyObject // id: %8 +// CHECK: Base: %1 = struct_extract %0 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue +// CHECK: Storage: Unidentified %1 = struct_extract %0 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue +// CHECK: Path: (@Unknown) +sil shared @testIndexLoop : $@convention(thin) (UnsafeMutablePointer) -> UnsafeMutablePointer { +bb0(%0 : $UnsafeMutablePointer): + %1 = struct_extract %0 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue + %2 = pointer_to_address %1 : $Builtin.RawPointer to [strict] $*AnyObject + %3 = load %2 : $*AnyObject + br bb1(%1 : $Builtin.RawPointer) + +bb1(%5 : $Builtin.RawPointer): + %6 = pointer_to_address %5 : $Builtin.RawPointer to [strict] $*AnyObject + %7 = load %6 : $*AnyObject + store %7 to %6 : $*AnyObject + %9 = integer_literal $Builtin.Word, 1 + %10 = index_addr %6 : $*AnyObject, %9 : $Builtin.Word + %11 = address_to_pointer %10 : $*AnyObject to $Builtin.RawPointer + cond_br undef, bb2, bb3(%11 : $Builtin.RawPointer) + +bb2: + br bb1(%11 : $Builtin.RawPointer) + +bb3(%14 : $Builtin.RawPointer): + %15 = struct $UnsafeMutablePointer (%14 : $Builtin.RawPointer) + return %15 : $UnsafeMutablePointer +} + +// Handle index_addr boundary conditions +// Most will end up as "unknown" offset, but we shouldn't crash. +// CHECK-LABEL: @indexAddrBoundaries +// CHECK: ###For MemOp: %4 = load %3 : $*Int +// CHECK: Path: (@Unknown) +// CHECK: ###For MemOp: %7 = load %6 : $*Int +// CHECK: Path: (@Unknown) +// CHECK: ###For MemOp: %10 = load %9 : $*Int +// CHECK: Path: (@Unknown) +// CHECK: ###For MemOp: %13 = load %12 : $*Int +// CHECK: Path: (@Unknown) +// CHECK: ###For MemOp: %16 = load %15 : $*Int +// CHECK: Path: (@1073741823) +// CHECK: ###For MemOp: %19 = load %18 : $*Int +// CHECK: Path: (@Unknown) +// CHECK: ###For MemOp: %22 = load %21 : $*Int +// CHECK: Path: (@Unknown) +// CHECK: ###For MemOp: %25 = load %24 : $*Int +// CHECK: Path: (@-1073741823) +// CHECK: ###For MemOp: %28 = load %27 : $*Int +// CHECK: Path: (@-1) +sil @indexAddrBoundaries : $@convention(thin) (Builtin.RawPointer) -> () { +bb0(%0 : $Builtin.RawPointer): + %1 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*Int + // INT_MIN (IndexTrie root) + %2 = integer_literal $Builtin.Word, 2147483648 // '0x80000000' + %3 = index_addr %1 : $*Int, %2 : $Builtin.Word + %4 = load %3 : $*Int + // INT_MIN (IndexTrie root) + %5 = integer_literal $Builtin.Word, -2147483648 // '0x80000000' + %6 = index_addr %1 : $*Int, %5 : $Builtin.Word + %7 = load %6 : $*Int + // INT_MAX (TailIndex) + %8 = integer_literal $Builtin.Word, 2147483647 // '0x7fffffff' + %9 = index_addr %1 : $*Int, %8 : $Builtin.Word + %10 = load %9 : $*Int + // Largest unsigned offset + 1 + %11 = integer_literal $Builtin.Word, 1073741824 // '0x40000000' + %12 = index_addr %1 : $*Int, %11 : $Builtin.Word + %13 = load %12 : $*Int + // Largest unsigned offset + %14 = integer_literal $Builtin.Word, 1073741823 // '0x3fffffff' + %15 = index_addr %1 : $*Int, %14 : $Builtin.Word + %16 = load %15 : $*Int + // Smallest signed offset - 1 + %17 = integer_literal $Builtin.Word, -1073741825 // '0xbfffffff' + %18 = index_addr %1 : $*Int, %17 : $Builtin.Word + %19 = load %18 : $*Int + // Smallest signed offset (Unknown offset) + %20 = integer_literal $Builtin.Word, -1073741824 // '0xc0000000' + %21 = index_addr %1 : $*Int, %20 : $Builtin.Word + %22 = load %21 : $*Int + // Smallest signed offset + 1 (concrete offset) + %23 = integer_literal $Builtin.Word, -1073741823 // '0xc0000001' + %24 = index_addr %1 : $*Int, %23 : $Builtin.Word + %25 = load %24 : $*Int + // Largest Negative + %26 = integer_literal $Builtin.Word, -1 // '0xffffffff' + %27 = index_addr %1 : $*Int, %26 : $Builtin.Word + %28 = load %27 : $*Int + // + %99 = tuple () + return %99 : $() +} From b8f3f9c84666301bb33ec68d7dea4305550b6d55 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 27 Aug 2020 17:48:06 -0700 Subject: [PATCH 524/745] accesspath_uses.sil tests --- test/SILOptimizer/accesspath_uses.sil | 532 ++++++++++++++++++++++++++ 1 file changed, 532 insertions(+) create mode 100644 test/SILOptimizer/accesspath_uses.sil diff --git a/test/SILOptimizer/accesspath_uses.sil b/test/SILOptimizer/accesspath_uses.sil new file mode 100644 index 0000000000000..883f68314e1d1 --- /dev/null +++ b/test/SILOptimizer/accesspath_uses.sil @@ -0,0 +1,532 @@ +// RUN: %target-sil-opt %s -accessed-storage-dump -enable-accessed-storage-dump-uses -enable-sil-verify-all -o /dev/null | %FileCheck %s +// RUN: %target-sil-opt %s -access-path-verification -o /dev/null + +// REQUIRES: PTRSIZE=64 + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +struct MyStruct { + @_hasStorage @_hasInitialValue var i: Int64 { get set } + @_hasStorage @_hasInitialValue var j: Int64 { get set } +} + +// unknown offset contains subobject indices +// CHECK-LABEL: @testDynamicIndexWithSubObject +// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK: Path: (#0) +// CHECK: Exact Uses { +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK-NEXT: Path: (#0) +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK-NEXT: Path: (#0) +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: } +// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct +// CHECK: Path: (@Unknown) +// CHECK-NEXT: Exact Uses { +// CHECK-NEXT: } +// CHECK-NEXT: Overlapping Uses { +// CHECK-NEXT: %{{.*}} = struct_element_addr %{{.*}} : $*MyStruct, #MyStruct.i +// CHECK-NEXT: Path: () +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: } +sil [serialized] [ossa] @testDynamicIndexWithSubObject : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> () { +bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word): + %2 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*MyStruct + %3 = struct_element_addr %2 : $*MyStruct, #MyStruct.i + %4 = load [trivial] %3 : $*Int64 + %5 = index_addr %2 : $*MyStruct, %1 : $Builtin.Word + %6 = load [trivial] %5 : $*MyStruct + %7 = tuple () + return %7 : $() +} + +// An unknown offset contains known offsets. +// CHECK-LABEL: @testDynamicIndexWithStaticIndex +// CHECK: ###For MemOp: %5 = load [trivial] %4 : $*MyStruct +// CHECK: Path: (@1) +// CHECK: Exact Uses { +// CHECK-NEXT: %5 = load [trivial] %4 : $*MyStruct +// CHECK-NEXT: Path: (@1) +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: %5 = load [trivial] %4 : $*MyStruct +// CHECK-NEXT: Path: (@1) +// CHECK-NEXT: %7 = load [trivial] %6 : $*MyStruct +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: } +// CHECK: ###For MemOp: %7 = load [trivial] %6 : $*MyStruct +// CHECK: Path: (@Unknown) +// CHECK: Exact Uses { +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: %5 = load [trivial] %4 : $*MyStruct +// CHECK-NEXT: Path: (@1) +// CHECK-NEXT: %7 = load [trivial] %6 : $*MyStruct +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: } +sil [serialized] [ossa] @testDynamicIndexWithStaticIndex : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> () { +bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word): + %2 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*MyStruct + %3 = integer_literal $Builtin.Word, 1 + %4 = index_addr %2 : $*MyStruct, %3 : $Builtin.Word + %5 = load [trivial] %4 : $*MyStruct + %6 = index_addr %2 : $*MyStruct, %1 : $Builtin.Word + %7 = load [trivial] %6 : $*MyStruct + %8 = tuple () + return %8 : $() +} + +// Even though the static offset does not match the first load, the +// dynamic offset should cancel it. +// CHECK-LABEL: @testChainedStaticDynamicIndex +// CHECK: ###For MemOp: %3 = load [trivial] %2 : $*MyStruct +// CHECK: Exact Uses { +// CHECK-NEXT: %3 = load [trivial] %2 : $*MyStruct +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK-NEXT: Overlapping Uses { +// CHECK-NEXT: %3 = load [trivial] %2 : $*MyStruct +// CHECK-NEXT: Path: () +// CHECK-NEXT: %8 = load [trivial] %7 : $*MyStruct +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: } +// CHECK: ###For MemOp: %6 = load [trivial] %5 : $*MyStruct +// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Path: (@1) +// CHECK: Exact Uses { +// CHECK-NEXT: %6 = load [trivial] %5 : $*MyStruct +// CHECK-NEXT: Path: (@1) +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: %6 = load [trivial] %5 : $*MyStruct +// CHECK-NEXT: Path: (@1) +// CHECK-NEXT: %8 = load [trivial] %7 : $*MyStruct +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: } +// CHECK: ###For MemOp: %8 = load [trivial] %7 : $*MyStruct +// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Path: (@Unknown) +// CHECK: Exact Uses { +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: %3 = load [trivial] %2 : $*MyStruct +// CHECK-NEXT: Path: () +// CHECK-NEXT: %6 = load [trivial] %5 : $*MyStruct +// CHECK-NEXT: Path: (@1) +// CHECK-NEXT: %8 = load [trivial] %7 : $*MyStruct +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: } +sil [serialized] [ossa] @testChainedStaticDynamicIndex : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> () { +bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word): + %2 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*MyStruct + %3 = load [trivial] %2 : $*MyStruct + %4 = integer_literal $Builtin.Word, 1 + %5 = index_addr %2 : $*MyStruct, %4 : $Builtin.Word + %6 = load [trivial] %5 : $*MyStruct + %7 = index_addr %5 : $*MyStruct, %1 : $Builtin.Word + %8 = load [trivial] %7 : $*MyStruct + %9 = tuple () + return %9 : $() +} + +// Static indices cancel. +// Unknown offset overlaps with subobject projections. +// +// CHECK-LABEL: @staticIndexAddrCancel +// CHECK: ###For MemOp: %3 = load [trivial] %2 : $*Int64 +// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Path: (#0) +// CHECK: Exact Uses { +// CHECK-NEXT: %3 = load [trivial] %2 : $*Int64 +// CHECK-NEXT: Path: (#0) +// CHECK-NEXT: %9 = load [trivial] %8 : $*Int64 +// CHECK-NEXT: Path: (#0) +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: %3 = load [trivial] %2 : $*Int64 +// CHECK-NEXT: Path: (#0) +// CHECK-NEXT: %9 = load [trivial] %8 : $*Int64 +// CHECK-NEXT: Path: (#0) +// CHECK-NEXT: } +// CHECK: ###For MemOp: %9 = load [trivial] %8 : $*Int64 +// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Path: (#0) +// CHECK: Exact Uses { +// CHECK-NEXT: %3 = load [trivial] %2 : $*Int64 +// CHECK-NEXT: Path: (#0) +// CHECK-NEXT: %9 = load [trivial] %8 : $*Int64 +// CHECK-NEXT: Path: (#0) +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: %3 = load [trivial] %2 : $*Int64 +// CHECK-NEXT: Path: (#0) +// CHECK-NEXT: %9 = load [trivial] %8 : $*Int64 +// CHECK-NEXT: Path: (#0) +// CHECK-NEXT: } +sil [ossa] @staticIndexAddrCancel : $@convention(thin) (Builtin.RawPointer) -> () { +bb0(%0 : $Builtin.RawPointer): + %1 = pointer_to_address %0 : $Builtin.RawPointer to $*MyStruct + %2 = struct_element_addr %1 : $*MyStruct, #MyStruct.i + %3 = load [trivial] %2 : $*Int64 + %4 = integer_literal $Builtin.Word, 1 + %5 = index_addr %1 : $*MyStruct, %4 : $Builtin.Word + %6 = integer_literal $Builtin.Word, -1 + %7 = index_addr %5 : $*MyStruct, %6 : $Builtin.Word + %8 = struct_element_addr %7 : $*MyStruct, #MyStruct.i + %9 = load [trivial] %8 : $*Int64 + %99 = tuple () + return %99 : $() +} + +// Increment an indexable address by one in a loop, with subobject +// uses inside and outside the loop. +// +// CHECK-LABEL: @testIndexLoop +// CHECK: ###For MemOp: %3 = load %2 : $*AnyObject +// CHECK: Storage: Unidentified %1 = struct_extract %0 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK-NEXT: %3 = load %2 : $*AnyObject +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: %3 = load %2 : $*AnyObject +// CHECK-NEXT: Path: () +// CHECK-NEXT: %7 = load %6 : $*AnyObject +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: store %7 to %6 : $*AnyObject +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: store %7 to %10 : $*AnyObject +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: cond_br undef, bb2, bb3(%12 : $Builtin.RawPointer) +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: } +// CHECK: ###For MemOp: %7 = load %6 : $*AnyObject +// CHECK: Storage: Unidentified %1 = struct_extract %0 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue +// CHECK: Path: (@Unknown) +// CHECK: Exact Uses { +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: %3 = load %2 : $*AnyObject +// CHECK-NEXT: Path: () +// CHECK-NEXT: %7 = load %6 : $*AnyObject +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: store %7 to %6 : $*AnyObject +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: store %7 to %10 : $*AnyObject +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: cond_br undef, bb2, bb3(%12 : $Builtin.RawPointer) +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: } +// CHECK: ###For MemOp: store %7 to %6 : $*AnyObject +// CHECK: Storage: Unidentified %1 = struct_extract %0 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue +// CHECK: Path: (@Unknown) +// CHECK: Exact Uses { +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: %3 = load %2 : $*AnyObject +// CHECK-NEXT: Path: () +// CHECK-NEXT: %7 = load %6 : $*AnyObject +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: store %7 to %6 : $*AnyObject +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: store %7 to %10 : $*AnyObject +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: cond_br undef, bb2, bb3(%12 : $Builtin.RawPointer) +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: } +// CHECK: ###For MemOp: store %7 to %10 : $*AnyObject +// CHECK: Storage: Unidentified %1 = struct_extract %0 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue +// CHECK: Path: (@Unknown) +// CHECK: Exact Uses { +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: %3 = load %2 : $*AnyObject +// CHECK-NEXT: Path: () +// CHECK-NEXT: %7 = load %6 : $*AnyObject +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: store %7 to %6 : $*AnyObject +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: store %7 to %10 : $*AnyObject +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: cond_br undef, bb2, bb3(%12 : $Builtin.RawPointer) +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: } +// CHECK: ###For MemOp: %17 = load %16 : $*AnyObject +// CHECK: Storage: Unidentified %1 = struct_extract %0 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue +// CHECK: Path: (@Unknown) +// CHECK: Exact Uses { +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: %3 = load %2 : $*AnyObject +// CHECK-NEXT: Path: () +// CHECK-NEXT: %7 = load %6 : $*AnyObject +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: store %7 to %6 : $*AnyObject +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: store %7 to %10 : $*AnyObject +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: cond_br undef, bb2, bb3(%12 : $Builtin.RawPointer) +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: } +sil shared @testIndexLoop : $@convention(thin) (UnsafeMutablePointer) -> AnyObject { +bb0(%0 : $UnsafeMutablePointer): + %1 = struct_extract %0 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue + %2 = pointer_to_address %1 : $Builtin.RawPointer to [strict] $*AnyObject + %3 = load %2 : $*AnyObject + br bb1(%1 : $Builtin.RawPointer) + +bb1(%5 : $Builtin.RawPointer): + %6 = pointer_to_address %5 : $Builtin.RawPointer to [strict] $*AnyObject + %7 = load %6 : $*AnyObject + store %7 to %6 : $*AnyObject + %9 = integer_literal $Builtin.Word, 1 + %10 = index_addr %6 : $*AnyObject, %9 : $Builtin.Word + store %7 to %10 : $*AnyObject + %12 = address_to_pointer %10 : $*AnyObject to $Builtin.RawPointer + cond_br undef, bb2, bb3(%12 : $Builtin.RawPointer) + +bb2: + br bb1(%12 : $Builtin.RawPointer) + +bb3(%15 : $Builtin.RawPointer): + %16 = pointer_to_address %15 : $Builtin.RawPointer to [strict] $*AnyObject + %17 = load %16 : $*AnyObject + return %17 : $AnyObject +} + +// CHECK-LABEL: @testEnumUses +// CHECK: ###For MemOp: copy_addr %0 to [initialization] %2 : $*IntTEnum +// CHECK: Storage: Argument %0 = argument of bb0 : $*IntTEnum +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK-NEXT: debug_value_addr %0 : $*IntTEnum, let, name "self", argno 1 +// CHECK-NEXT: Path: () +// CHECK-NEXT: copy_addr %0 to [initialization] %2 : $*IntTEnum +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: debug_value_addr %0 : $*IntTEnum, let, name "self", argno 1 +// CHECK-NEXT: Path: () +// CHECK-NEXT: copy_addr %0 to [initialization] %2 : $*IntTEnum +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK: Storage: Stack %2 = alloc_stack $IntTEnum +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK-NEXT: copy_addr %0 to [initialization] %2 : $*IntTEnum +// CHECK-NEXT: Path: () +// CHECK-NEXT: switch_enum_addr %2 : $*IntTEnum, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2 +// CHECK-NEXT: Path: () +// CHECK-NEXT: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.int!enumelt +// CHECK-NEXT: Path: () +// CHECK-NEXT: %6 = load [take] %5 : $*<τ_0_0> { var Int } +// CHECK-NEXT: Path: () +// CHECK-NEXT: %8 = load [trivial] %7 : $*Int +// CHECK-NEXT: Path: () +// CHECK-NEXT: dealloc_stack %2 : $*IntTEnum +// CHECK-NEXT: Path: () +// CHECK-NEXT: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.other!enumelt +// CHECK-NEXT: Path: () +// CHECK-NEXT: destroy_addr %13 : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: dealloc_stack %2 : $*IntTEnum +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: copy_addr %0 to [initialization] %2 : $*IntTEnum +// CHECK-NEXT: Path: () +// CHECK-NEXT: switch_enum_addr %2 : $*IntTEnum, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2 +// CHECK-NEXT: Path: () +// CHECK-NEXT: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.int!enumelt +// CHECK-NEXT: Path: () +// CHECK-NEXT: %6 = load [take] %5 : $*<τ_0_0> { var Int } +// CHECK-NEXT: Path: () +// CHECK-NEXT: %8 = load [trivial] %7 : $*Int +// CHECK-NEXT: Path: () +// CHECK-NEXT: dealloc_stack %2 : $*IntTEnum +// CHECK-NEXT: Path: () +// CHECK-NEXT: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.other!enumelt +// CHECK-NEXT: Path: () +// CHECK-NEXT: destroy_addr %13 : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: dealloc_stack %2 : $*IntTEnum +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK: ###For MemOp: switch_enum_addr %2 : $*IntTEnum, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2 +// CHECK: Storage: Stack %2 = alloc_stack $IntTEnum +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum +// CHECK: switch_enum_addr %2 : $*IntTEnum, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2 +// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.int!enumelt +// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } +// CHECK: %8 = load [trivial] %7 : $*Int +// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.other!enumelt +// CHECK: } +// CHECK: Overlapping Uses { +// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum +// CHECK: switch_enum_addr %2 : $*IntTEnum, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2 +// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.int!enumelt +// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } +// CHECK: %8 = load [trivial] %7 : $*Int +// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.other!enumelt +// CHECK: } +// CHECK: ###For MemOp: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.int!enumelt +// CHECK: Storage: Stack %2 = alloc_stack $IntTEnum +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum +// CHECK: switch_enum_addr %2 : $*IntTEnum, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2 +// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.int!enumelt +// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } +// CHECK: %8 = load [trivial] %7 : $*Int +// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.other!enumelt +// CHECK: } +// CHECK: Overlapping Uses { +// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum +// CHECK: switch_enum_addr %2 : $*IntTEnum, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2 +// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.int!enumelt +// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } +// CHECK: %8 = load [trivial] %7 : $*Int +// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.other!enumelt +// CHECK: } +// CHECK: ###For MemOp: %6 = load [take] %5 : $*<τ_0_0> { var Int } +// CHECK: Storage: Stack %2 = alloc_stack $IntTEnum +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum +// CHECK: switch_enum_addr %2 : $*IntTEnum, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2 +// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.int!enumelt +// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } +// CHECK: %8 = load [trivial] %7 : $*Int +// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.other!enumelt +// CHECK: } +// CHECK: Overlapping Uses { +// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum +// CHECK: switch_enum_addr %2 : $*IntTEnum, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2 +// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.int!enumelt +// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } +// CHECK: %8 = load [trivial] %7 : $*Int +// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.other!enumelt +// CHECK: } +// CHECK: ###For MemOp: %8 = load [trivial] %7 : $*Int +// CHECK: Storage: Stack %2 = alloc_stack $IntTEnum +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum +// CHECK: switch_enum_addr %2 : $*IntTEnum, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2 +// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.int!enumelt +// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } +// CHECK: %8 = load [trivial] %7 : $*Int +// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.other!enumelt +// CHECK: } +// CHECK: Overlapping Uses { +// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum +// CHECK: switch_enum_addr %2 : $*IntTEnum, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2 +// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.int!enumelt +// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } +// CHECK: %8 = load [trivial] %7 : $*Int +// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.other!enumelt +// CHECK: } +// CHECK: ###For MemOp: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.other!enumelt +// CHECK: Storage: Stack %2 = alloc_stack $IntTEnum +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum +// CHECK: switch_enum_addr %2 : $*IntTEnum, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2 +// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.int!enumelt +// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } +// CHECK: %8 = load [trivial] %7 : $*Int +// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.other!enumelt +// CHECK: } +// CHECK: Overlapping Uses { +// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum +// CHECK: switch_enum_addr %2 : $*IntTEnum, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2 +// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.int!enumelt +// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } +// CHECK: %8 = load [trivial] %7 : $*Int +// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.other!enumelt +// CHECK: } +enum IntTEnum { + indirect case int(Int) + case other(T) + + var getValue: Int {get } +} + +sil hidden [ossa] @testEnumUses : $@convention(method) (@in_guaranteed IntTEnum) -> Int { +bb0(%0 : $*IntTEnum): + debug_value_addr %0 : $*IntTEnum, let, name "self", argno 1 + %2 = alloc_stack $IntTEnum + copy_addr %0 to [initialization] %2 : $*IntTEnum + switch_enum_addr %2 : $*IntTEnum, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2 + +bb1: + %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.int!enumelt + %6 = load [take] %5 : $*<τ_0_0> { var Int } + %7 = project_box %6 : $<τ_0_0> { var Int } , 0 + %8 = load [trivial] %7 : $*Int + debug_value %8 : $Int, let, name "x" + destroy_value %6 : $<τ_0_0> { var Int } + dealloc_stack %2 : $*IntTEnum + br bb3(%8 : $Int) + +bb2: + %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.other!enumelt + %14 = integer_literal $Builtin.Int64, 0 + %15 = struct $Int (%14 : $Builtin.Int64) + destroy_addr %13 : $*T + dealloc_stack %2 : $*IntTEnum + br bb3(%15 : $Int) + +bb3(%19 : $Int): + return %19 : $Int +} + +class Storage {} + +// Test that tail_addr is always an unknown offset. +// +// CHECK-LABEL: @inlinedArrayProp +// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*Int +// CHECK: Tail %0 = argument of bb0 : $Storage +// CHECK: Base: %{{.*}} = ref_tail_addr %0 : $Storage, $UInt +// CHECK: Storage: Tail %0 = argument of bb0 : $Storage +// CHECK: Path: (@Unknown) +// CHECK: Exact Uses { +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: %8 = load [trivial] %7 : $*Int +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: end_access %7 : $*Int +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: end_access %5 : $*Int +// CHECK-NEXT: Path: (@Unknown) +// CHECK-NEXT: end_access %2 : $*UInt +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +sil hidden [ossa] @inlinedArrayProp : $@convention(thin) (@guaranteed Storage) -> Int { +bb0(%0 : @guaranteed $Storage): + %1 = ref_tail_addr %0 : $Storage, $UInt + %2 = begin_access [read] [static] %1 : $*UInt + %3 = integer_literal $Builtin.Word, 1 + %4 = tail_addr %2 : $*UInt, %3 : $Builtin.Word, $Int + %5 = begin_access [read] [static] %4 : $*Int + %6 = index_addr %5 : $*Int, %3 : $Builtin.Word + %7 = begin_access [read] [static] %6 : $*Int + %8 = load [trivial] %7 : $*Int + end_access %7 : $*Int + end_access %5 : $*Int + end_access %2 : $*UInt + return %8 : $Int +} From 0f428c2c06d5698aa750016ae60291424325c1bc Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 31 Aug 2020 14:30:01 -0700 Subject: [PATCH 525/745] alias-analysis test --- test/SILOptimizer/alias-analysis.sil | 60 ++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 test/SILOptimizer/alias-analysis.sil diff --git a/test/SILOptimizer/alias-analysis.sil b/test/SILOptimizer/alias-analysis.sil new file mode 100644 index 0000000000000..675211e426fb5 --- /dev/null +++ b/test/SILOptimizer/alias-analysis.sil @@ -0,0 +1,60 @@ +// RUN: %target-sil-opt %s -aa-kind=all -aa-dump -o /dev/null | %FileCheck %s + +// General black-box alias analysis tests. White-box unit tests are +// specific to the aa-kind, such as basic-aa.sil and typed-access-tb-aa.sil. + +// REQUIRES: asserts + +sil_stage canonical + +import Swift +import SwiftShims +import Builtin + +struct MyStruct { + @_hasStorage @_hasInitialValue var i: Int64 { get set } + @_hasStorage @_hasInitialValue var j: Int64 { get set } +} + +struct OuterStruct { + @_hasStorage @_hasInitialValue var s: MyStruct { get set } +} + +// Test overlaping access on a different path; the user has misused an index offset +// CHECK-LABEL: @testOffsetBehindProjection +// CHECK: PAIR #28. +// CHECK: %4 = load %3 : $*Int64 +// CHECK: %6 = load %5 : $*Int64 +// CHECK: MayAlias +sil shared @testOffsetBehindProjection : $@convention(thin) (@inout MyStruct) -> () { +bb0(%0 : $*MyStruct): + %1 = struct_element_addr %0 : $*MyStruct, #MyStruct.i + %2 = integer_literal $Builtin.Word, 1 + %3 = index_addr %1 : $*Int64, %2 : $Builtin.Word + %4 = load %3 : $*Int64 + // load from a different subobject overlaps + %5 = struct_element_addr %0 : $*MyStruct, #MyStruct.j + %6 = load %5 : $*Int64 + %99 = tuple () + return %99 : $() +} + +// CHECK-LABEL: @testOffsetsBehindProjectionOverlap +// CHECK: PAIR #28. +// CHECK: %3 = load %2 : $*Int64 +// CHECK: %7 = load %6 : $*Int64 +// CHECK: MayAlias +sil shared @testOffsetsBehindProjectionOverlap : $@convention(thin) (@inout OuterStruct) -> () { +bb0(%0 : $*OuterStruct): + %1 = struct_element_addr %0 : $*OuterStruct, #OuterStruct.s + %2 = struct_element_addr %1 : $*MyStruct, #MyStruct.i + %3 = load %2 : $*Int64 + // Loading from a different index within the same subobject still appears to overlap. + // Indexing from a subobject is always considered an unknown index. + %4 = integer_literal $Builtin.Word, 1 + %5 = index_addr %1 : $*MyStruct, %4 : $Builtin.Word + %6 = struct_element_addr %5 : $*MyStruct, #MyStruct.i + %7 = load %6 : $*Int64 + %99 = tuple () + return %99 : $() +} From 391b06d20b1bab6be5c289d66a3a56de793eef8d Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Wed, 2 Sep 2020 11:50:42 -0700 Subject: [PATCH 526/745] RLE tests for boundary Projection indices. --- test/SILOptimizer/redundant_load_elim.sil | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/test/SILOptimizer/redundant_load_elim.sil b/test/SILOptimizer/redundant_load_elim.sil index 633a72cd588ca..4549ec2c481f0 100644 --- a/test/SILOptimizer/redundant_load_elim.sil +++ b/test/SILOptimizer/redundant_load_elim.sil @@ -1176,14 +1176,27 @@ bb0(%0 : $*Builtin.Int64, %1 : $Builtin.NativeObject): return %6 : $(Builtin.Int64, Builtin.Int64) } -sil @dont_crash_on_index_addr_projection : $@convention(thin) (Builtin.RawPointer) -> Int { +sil @dont_crash_on_index_addr_projection : $@convention(thin) (Builtin.RawPointer) -> (Int, Int, Int, Int) { bb0(%0 : $Builtin.RawPointer): - %3 = integer_literal $Builtin.Word, 4294967295 + // Negative (valid constant index) + %3 = integer_literal $Builtin.Word, 4294967295 // '0xffffffff' %4 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*Int - // Just check if we can handle an index_addr projection with the special value of 0xffffffff %5 = index_addr %4 : $*Int, %3 : $Builtin.Word %6 = load %5 : $*Int - return %6 : $Int + // TailIndex (invalid constant index) + %7 = integer_literal $Builtin.Word, 2147483647 // '0x7fffffff' + %8 = index_addr %4 : $*Int, %7 : $Builtin.Word + %9 = load %8 : $*Int + // UnknownOffset (valid index) + %10 = integer_literal $Builtin.Word, 3221225472 // '0xC0000000' + %11 = index_addr %4 : $*Int, %10 : $Builtin.Word + %12 = load %11 : $*Int + // Root (unused/invalid index)) + %13 = integer_literal $Builtin.Word, 2147483648 // '0x80000000' + %14 = index_addr %4 : $*Int, %13 : $Builtin.Word + %15 = load %14 : $*Int + %99 = tuple (%6 : $Int, %9 : $Int, %12 : $Int, %15 : $Int) + return %99 : $(Int, Int, Int, Int) } sil @overwrite_int : $@convention(thin) (@inout Int, Int) -> () From 712e1abec67af6d33b7a7465d4c4c0c52fd1c55a Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Sun, 13 Sep 2020 23:38:13 -0700 Subject: [PATCH 527/745] AccessedStorage and AccessPath documentation. --- docs/SILProgrammersManual.md | 462 +++++++++++++++++++++++++++++ include/swift/SIL/MemAccessUtils.h | 3 +- 2 files changed, 464 insertions(+), 1 deletion(-) diff --git a/docs/SILProgrammersManual.md b/docs/SILProgrammersManual.md index b5cf4e9c7bd9e..7a43605f1855b 100644 --- a/docs/SILProgrammersManual.md +++ b/docs/SILProgrammersManual.md @@ -159,6 +159,468 @@ rules out PartialApplies is no excuse to conflate applied arguments with function arguments. Also, without consistent use of common idioms, it becomes overly burdensome to evolve these APIs over time. +## `AccessedStorage` and `AccessPath` + +The `AccessedStorage` and `AccessPath` types formalize memory access +in SIL. Given an address-typed SIL value, it is possible to +reliably identify the storage location of the accessed +memory. `AccessedStorage` identifies an accessed storage +location. `AccessPath` contains both a storage location and the +"access path" within that memory object. The relevant API details are +documented in MemAccessUtils.h + +### Formal access + +SIL preserves the language semantics of formal variable access in the +form of access markers. `begin_access` identifies the address of the +formal access and `end_access` delimits the scope of the access. At +the language level, a formal access is an access to a local variable +or class property. For details, see +[SE-0176: Enforce Exclusive Access to Memory](https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md) + +Access markers are preserved in SIL to: + +1. verify exclusivity enforcement + +2. optimize exclusivity checks following other transforms, such as + converting dynamic checks into static checks + +3. simplify and strengthen general analyses of memory access. For +example, `begin_access [read] %address` indicates that the accessed +address is immutable for the duration of its access scope + +### Access path def-use relationship + +Computing `AccessedStorage` and `AccessPath` for any given SIL address +involves a use-def traversal to determine the origin of the +address. It may traverse operations on address, pointer, box, and +reference types. The logic that formalizes which SIL operations may be +involved in the def-use chain is encapsulated with the +`AccessUseDefChainVisitor`. The traversal can be customized by +implementing this visitor. Customization is not expected to change the +meaning of AccessedStorage or AccessPath. Rather, it is intended for +additional pass-specific book-keeping or for higher-level convenience +APIs that operate on the use-def chain bypassing AccessedStorage +completely. + +Access def-use chains are divided by four points: the "root", the +access "base", the outer-most "access" scope, and the "address" of a +memory operation. For example: +``` + struct S { + var field: Int64 + } + class C { + var prop: S + } + + %root = alloc_ref $C + %base = ref_element_addr %root : $C, #C.prop + %access = begin_access [read] [static] %base : $*S + %address = struct_element_addr %access : $*S, #.field + %value = load [trivial] %address : $*Int64 + end_access %access : $*S +``` + +#### Reference root + +The first part of the def-use chain computes the formal access base +from the root of the object (e.g. `alloc_ref -> +ref_element_addr`). The reference root might be a locally allocated +object, a function argument, a function result, or a reference loaded +from storage. There is no enforcement on the type of operation that +can produce a reference; however, only reference types or +Builtin.BridgeObject types are only allowed in this part of the +def-use chain. The reference root is the greatest common ancestor in +the def-use graph that can identify an object by a single SILValue. If +the root as an `alloc_ref`, then it is *uniquely identified*. The +def-use chain from the root to the base may contain reference casts +(`isRCIdentityPreservingCast`) and phis. + +This example has an identifiable def-use chain from `%root` to `%base`: +``` +class A { + var prop0: Int64 +} +class B : A { +} + +bb0: + %root = alloc_ref $B + cond_br _, bb1, bb2 + +bb1: + %a1 = upcast %root : $B to $A + br bb3(%a1 : $A) + +bb2: + %a2 = upcast %root : $B to $A + br bb3(%a2 : $A) + +bb3(%a : $A): + %bridge = ref_to_bridge_object %a : $A, %bits : $Builtin.Word + %ref = bridge_object_to_ref %bridge : $Builtin.BridgeObject to $A + %base = ref_element_addr %ref : $A, #A.prop0 +``` + +Each object property and its tail storage is considered a separate +formal access base. The reference root is only one component of an +`AccessedStorage` location. AccessedStorage also identifies the class +property being accessed within that object. + +#### Access base + +The access base is the SILValue produced by an instruction that +directly identifies the kind of storage being accessed without further +use-def traversal. Common access bases are `alloc_box`, `alloc_stack`, +`global_addr`, `ref_element_addr`, and function arguments (see +`AccessedStorage::Kind`). + +The access base is the same as the "root" SILValue for all storage +kinds except global and class storage. Global storage has no root. For +class storage the root is the SILValue that identifies object, +described as the "reference root" above. + +"Box" storage is uniquely identified by an `alloc_box` +instruction. Therefore, we consider the `alloc_box` to be the base of +the access. Box storage does not apply to all box types or box +projections, which may instead originate from arguments or indirect +enums for example. + +Typically, the base is the address-type source operand of a +`begin_access`. However, the path from the access base to the +`begin_access` may include *storage casts* (see +`isAccessedStorageCast`). It may involve address, pointer, and box +types, and may traverse phis. For some kinds of storage, the base may +itself even be a non-address pointer. For phis that cannot be uniquely +resolved, the base may even be a box type. + +This example has an identifiable def-use chain from `%base` to `%access`: +``` +bb0: + %base = alloc_box $Int { var Int } + %boxadr = project_box %base : ${ var Int } + %p0 = address_to_pointer %boxadr : $*Int to $Builtin.RawPointer + cond_br _, bb1, bb2 + +bb1: + %p1 = copy_value %p0 : $Builtin.RawPointer + br bb3(%p1 : $Builtin.RawPointer) + +bb2: + br bb3(%p0 : $Builtin.RawPointer) + +bb3(%ptr : $Builtin.RawPointer): + %adr = pointer_to_address %ptr : $Builtin.RawPointer to $*Int + %access = begin_access [read] [static] %adr : $*Int +``` + +Note that address-type phis are illegal (full enforcement +pending). This is important for simplicity and efficiency, but also +allows for a class of storage optimizations, such as bitfields, in +which address storage is always uniquely determined. Currently, if a +(non-address) phi on the access path from `base` to `access` does not +have a common base, then it is considered an invalid access (the +AccessedStorage object is not valid). SIL verification ensures that a +formal access always has valid AccessedStorage (WIP). In other words, the +source of a `begin_access` marker must be a single, non-phi base. In +the future, for further simplicity, we may generally disallow box and +pointer phis unless they have a common base. + +Not all SIL memory access is part of a formal access, but the +`AccessedStorage` and `AccessPath` abstractions are universally +applicable. Non-formal access still has an access base, even though +the use-def search does not begin at a `begin_access` marker. For +non-formal access, SIL verification is not as strict. An invalid +access is allowed, but handled conservatively. This is safe as long as +those non-formal accesses can never alias with class and global +storage. Class and global access is always guarded by formal access +markers--at least until static markers are stripped from SIL. + +#### Nested access + +Nested access occurs when an access base is a function argument. The +caller always checks `@inout` arguments for exclusivity (an access +marker must exist in the caller). However, the argument itself is a +variable with its own formal access. Conflicts may occur in the callee +which were not evident in the caller. In this example, a conflict +occurs in `hasNestedAccess` but not in its caller: + +``` +func takesTwoInouts(_ : inout Int, _ : inout Int) -> () {} + +func hasNestedAccess(_ x : inout Int) -> () { + takesTwoInouts(&x, &x) +} + +var x = 0 +hasNestedAccess(&x) +``` + +Produces these access markers: +``` +sil @takesTwoInouts : $@convention(thin) (@inout Int, @inout Int) -> () + +sil @hasNestedAccess : $@convention(thin) (@inout Int) -> () { +bb0(%0 : $*Int): + %innerAccess = begin_access [modify] %0 : $*Int + %conflicting = begin_access [modify] %0 : $*Int + %f = function_ref @takesTwoInouts + apply %f(%innerAccess, %conflicting) + : $@convention(thin) (@inout Int, @inout Int) -> () + end_access %conflicting : $*Int + end_access %innerAccess : $*Int + //... +} + +%var = alloc_stack $Int +%outerAccess = begin_access [modify] %var : $*Int +%f = function_ref @hasNestedAccess +apply %f(%outerAccess) : $@convention(thin) (@inout Int) -> () { +end_access %outerAccess : $*Int +``` + +Nested accesses become part if the def-use chain after inlining. Here, +both `%innerAccess` and `%conflicting` are nested within +`%outerAccess`: + +``` +%var = alloc_stack $Int +%outerAccess = begin_access [modify] %var : $*Int +%innerAccess = begin_access [modify] %outerAccess : $*Int +%conflicting = begin_access [modify] %outerAccess : $*Int +%f = function_ref @takesTwoInouts +apply %f(%innerAccess, %conflicting) + : $@convention(thin) (@inout Int, @inout Int) -> () +end_access %conflicting : $*Int +end_access %innerAccess : $*Int +end_access %outerAccess : $*Int +``` + +For most purposes, the inner access scopes are irrelevant. When we ask +for the "accessed storage" for `%innerAccess`, we get an +`AccessedStorage` value of "Stack" kind with base `%var = +alloc_stack`. If instead of finding the original accessed storage, we +want to identify the enclosing formal access scope, we need to use a +different API that supports the special `Nested` storage kind. This is +typically only used for exclusivity diagnostics though. + +TODO: Nested static accesses that result from inlining could +potentially be removed, as long as DiagnoseStaticExclusivity has +already run. + +#### Access projections + +On the def-use chain between the *outermost* formal access scope within +the current function and a memory operation, *access projections* +identify subobjects laid out within the formally accessed +variable. The sequence of access projections between the base and the +memory address correspond to an access path. + +For example, there is no formal access for struct fields. Instead, +they are addressed using a `struct_element_addr` within the access +scope: + +``` +%access = begin_access [read] [static] %base : $*S +%memaddr = struct_element_addr %access : $*S, #.field +%value = load [trivial] %memaddr : $*Int64 +end_access %access : $*S +``` + +Note that is is possible to have a nested access scope on the address +of a struct field, which may show up as an access of +struct_element_addr after inlining. The rule is that access +projections cannot occur outside of the outermost access scope within +the function. + +Access projections are address projections--they take an address at +operand zero and produce a single address result. Other +straightforward access projections include `tuple_element_addr`, +`index_addr`, and `tail_addr` (an aligned form of `index_addr`). + +Enum payload extraction (`unchecked_take_enum_data_addr`) is also an +access projection, but it has no effect on the access path. + +Indirect enum payload extraction is a special two-instruction form of +address projection (`load : ${ var } -> project_box`). For simplicity, +and to avoid the appearance of box types on the access path, this +should eventually be encapsulated in a single SIL instruction. + +For example, the following complex def-use chain from `%base` to +`%load` actually has an empty access path: +``` +%boxadr = unchecked_take_enum_data_addr %base : $*Enum, #Enum.int!enumelt +%box = load [take] %boxadr : $*<τ_0_0> { var Int } +%valadr = project_box %box : $<τ_0_0> { var Int } , 0 +%load = load [trivial] %valadr : $*Int +``` + +Storage casts may also occur within an access. This typically results +from accessors, which perform address-to-pointer +conversion. Pointer-to-address conversion performs a type cast, and +could lead to different subobject types corresponding to the same base +and access path. Access paths still uniquely identify a memory +location because it is illegal to cast memory to non-layout-compatible +types on same execution path (without an intervening `bind_memory`). + +Address-type phis are prohibited, but because pointer and box types +may be on the def-use chain, phis may also occur on an access path. A +phi is only a valid part of an access path if it has no affect on the +path components. This means that pointer casting and unboxing may +occur on distinct phi paths, but index offsets and subobject +projections may not. These rules are currently enforced to a limited +extent, so it's possible for invalid access path to occur under +certain conditions. + +For example, the following is a valid def-use access chain, with an +access base defined in `bb0`, a memory operation in `bb3` and an +`index_addr` and `struct_element_addr` on the access path: + +``` +class A {} + +struct S { + var field0: Int64 + var field1: Int64 +} + +bb0: + %base = ref_tail_addr %ref : $A, $S + %idxproj = index_addr %tail : $*S, %idx : $Builtin.Word + %p0 = address_to_pointer %idxproj : $*S to $Builtin.RawPointer + cond_br _, bb1, bb2 + +bb1: + %pcopy = copy_value %p0 : $Builtin.RawPointer + %adr1 = pointer_to_address [strict] %pcopy : $Builtin.RawPointer to $*S + %p1 = address_to_pointer %adr1 : $*S to $Builtin.RawPointer + br bb3(%p1 : $Builtin.RawPointer) + +bb2: + br bb3(%p0 : $Builtin.RawPointer) + +bb3(%p3 : $Builtin.RawPointer): + %adr3 = pointer_to_address [strict] %p3 : $Builtin.RawPointer to $*S + %field = struct_element_addr %adr3 : $*S, $S.field0 + load %field : $*Int64 +``` + +### AccessedStorage + +`AccessedStorage` identifies an accessed storage location, be it a +box, stack location, class property, global variable, or argument. It +is implemented as a value object that requires no compile-time memory +allocation and can be used as the hash key for that location. Extra +bits are also available for information specific to a particular +optimization pass. Its API provides the kind of location being +accessed and information about the location's uniqueness or whether it +is distinct from other storage. + +Two __uniquely identified__ storage locations may only alias if their +AccessedStorage objects are identical. + +`AccessedStorage` records the "root" SILValue of the access. The root is +the same as the access base for all storage kinds except global and +class storage. For class properties, the storage root is the reference +root of the object, not the base of the property. Multiple +`ref_element_addr` projections may exist for the same property. Global +variable storage is always uniquely identified, but it is impossible +to find all uses from the def-use chain alone. Multiple `global_addr` +instructions may reference the same variable. To find all global uses, +the client must independently find all global variable references +within the function. Clients that need to know which SILValue base was +discovered during use-def traversal in all cases can make use of +`AccessedStorageWithBase` or `AccessPathWithBase`. + +### AccessPath + +`AccessPath` extends `AccessedStorage` to include the path components +that determine the address of a subobject within the access base. The +access path is a string of index offsets and subobject projection +indices. + +``` +struct S { + var field0: Int64 + var field1: Int64 +} + +%eltadr = struct_element_addr %access : $*S, #.field1 + +Path: (#1) +``` + +``` +class A {} + +%tail = ref_tail_addr %ref : $A, $S +%one = integer_literal $Builtin.Word, 1 +%elt = index_addr %tail : $*S, %one : $Builtin.Word +%field = struct_element_addr %elt : $*S, $S.field0 + +Path: (@1, #0) +``` + +Note that a projection from a reference type to the object's property +or tail storage is not part of the access path because it is already +identified by the storage location. + +Offset indices are all folded into a single index at the head of the +path (a missing offset implies offset zero). Offsets that are not +static constants are still valid but are labeled "@Unknown". Indexing +within a subobject is an ill-formed access, but is handled +conservatively since this rule cannot be fully enforced. + +For example, the following is an invalid access path, which just +happens to point to field1: +``` +%field0 = struct_element_addr %base : $*S, #field0 +%field1 = index_addr %elt : $*Int64, %one : $Builtin.Word + +Path: (INVALID) +``` + +The following APIs determine whether an access path contains another +or may overlap with another. + +`AccessPath::contains(AccessPath subPath)` + +`AccessPath::mayOverlap(AccessPath otherPath)` + +These are extremely light-weight APIs that, in the worst case, require +a trivial linked list traversal with single pointer comparison for the +length of subPath or otherPath. + +Subobjects are both contained with and overlap with their parent +storage. An unknown offset does not contain any known offsets but +overlaps with all offsets. + +### Access path uses + +For any accessed storage location and base, it must also be possible +to reliably identify all uses of that storage location within the +function for a particular access base. If the storage is uniquely +identified, then that also implies that all uses of that storage +within the function have been discovered. In other words, there are no +aliases to the same storage that aren't covered by this use set. + +The `AccessPath::collectUses()` API does this. It is possible to ask +for only the uses contained by the current path, or for all +potentially overlapping uses. It is guaranteed to return a complete +use set unless the client specifies a limit on the number of uses. + +As passes begin to adopt AccessPath::collectUses(), I expect it to +become a visitor pattern that allows the pass to perform custom +book-keeping for certain types of uses. + +The `AccessPathVerification` pass runs at key points in the pipeline +to ensure that all address uses are identified and have consistent +access paths. This pass ensures that the implementations of AccessPath +is internally consistent for all SIL patterns. Enforcing the validity +of the SIL itself, such as which operations are allowed on an access +def-use chain, is handled within the SIL verifier instead. + ## SILGen TBD: Possibly link to a separate document explaining the architecture of SILGen. diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index e6afa239c528e..bc6ad28457bbe 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -10,7 +10,8 @@ // //===----------------------------------------------------------------------===// /// -/// These utilities model the storage locations of memory access. +/// These utilities model the storage locations of memory access. See +/// ProgrammersGuide.md for high-level design. /// /// All memory operations that are part of a formal access, as defined by /// exclusivity rules, are marked by begin_access and end_access instructions. From 9c69d0242d4cf191e06ae8d2122e1265f9ddc4cc Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 8 Oct 2020 04:35:14 -0700 Subject: [PATCH 528/745] MemAccessUtils comment --- include/swift/SIL/MemAccessUtils.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index bc6ad28457bbe..fdf5c68e90706 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -890,6 +890,7 @@ struct AccessPathWithBase { // // base may be invalid for global_addr -> address_to_pointer -> phi patterns. // FIXME: add a structural requirement to SIL so base is always valid in OSSA. + // !!! make this a PtrIntPair with a the access kind SILValue base; /// \p address identifies the object seen by any memory operation that From 6f2cda1390833cca96a8087a72fe348e74733713 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 15 Oct 2020 00:04:19 -0700 Subject: [PATCH 529/745] Add AccessUseVisitor and cleanup related APIs. Add AccesssedStorage::compute and computeInScope to mirror AccessPath. Allow recovering the begin_access for Nested storage. Adds AccessedStorage.visitRoots(). --- include/swift/SIL/MemAccessUtils.h | 318 +++++--- .../SILOptimizer/Analysis/ValueTracking.h | 2 +- lib/IRGen/IRGenSIL.cpp | 2 +- lib/SIL/Utils/MemAccessUtils.cpp | 742 ++++++++++-------- lib/SILGen/SILGenLValue.cpp | 2 +- .../Analysis/AccessedStorageAnalysis.cpp | 8 +- lib/SILOptimizer/Analysis/AliasAnalysis.cpp | 2 +- lib/SILOptimizer/Analysis/MemoryBehavior.cpp | 3 +- lib/SILOptimizer/LoopTransforms/LICM.cpp | 6 +- .../Mandatory/DiagnoseStaticExclusivity.cpp | 21 +- .../Transforms/AccessEnforcementDom.cpp | 2 +- .../Transforms/AccessEnforcementOpts.cpp | 2 +- .../Transforms/AccessEnforcementWMO.cpp | 4 +- .../Transforms/PerformanceInliner.cpp | 2 +- .../UtilityPasses/AccessPathVerification.cpp | 18 +- .../UtilityPasses/AccessedStorageDumper.cpp | 42 +- test/SILOptimizer/accesspath_uses.sil | 210 ++++- test/SILOptimizer/licm.sil | 2 +- 18 files changed, 904 insertions(+), 484 deletions(-) diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index fdf5c68e90706..a1b302710acd1 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -27,16 +27,16 @@ /// memory operation, there are three levels of APIs that inspect the origin of /// that address: /// -/// 1. getAccessAddress(): Find the originating address as close as possible to -/// the address of the formal access *without* looking past any storage -/// casts. This is useful when the type of the returned access address must be -/// consistent with the memory operation's type (the same type or a parent -/// type). For a formal access, this typically returns the begin_access, but it -/// is not guaranteed to because some accesses contain storage casts. For +/// 1. getTypedAccessAddress(): Find the originating address as close as +/// possible to the address of the formal access *without* looking past any +/// storage casts. This is useful when the type of the returned access address +/// must be consistent with the memory operation's type (the same type or a +/// parent type). For a formal access, this typically returns the begin_access, +/// but it is not guaranteed to because some accesses contain storage casts. For /// non-formal access, it returns a best-effort address corresponding to the /// base of an access. /// -/// 2. getAccessBegin(): If the memory operation is part of a formal access, +/// 2. getAccessScope(): If the memory operation is part of a formal access, /// then this is guaranteed to return the begin_access marker. Otherwise, it /// returns the best-effort address or pointer corresponding to the base of an /// access. Useful to find the scope of a formal access. @@ -47,42 +47,43 @@ /// address type, pointer type, or box type, but never a reference type. /// Each object's property or its tail storage is separately accessed. /// -/// For better identification an access base, use findAccessedStorage(). It -/// returns an AccessedStorage value that identifies the storage location of a -/// memory access. It provides APIs for inspecting type of accessed storage and -/// allows for disambiguation between different types of storage and different -/// properties within a class. +/// For better identification an access base, use +/// AccessedStorage::compute(). It returns an AccessedStorage value +/// that identifies the storage location of a memory access. It provides APIs +/// for inspecting type of accessed storage and allows for disambiguation +/// between different types of storage and different properties within a class. /// -/// findAccessedStorage() follows the same logic as getAccessBase(), but if the -/// base is not recognized as a valid access, it returns invalid +/// AccessedStorage::compute() follows the same logic as getAccessBase(), but if +/// the base is not recognized as a valid access, it returns invalid /// AccessedStorage. It also performs further analysis to determine the root /// reference of an object access. /// -/// findAccessedStorage() returns the outermost AccessedStorage for any memory -/// address. It can be called on the address of a memory operation, the address -/// of a begin_access, or any other address value. If the address is from an -/// enforced begin_access or from any memory operation that is part of a formal -/// access, then it returns a valid AccessedStorage value. If the memory +/// AccessedStorage::compute() returns the outermost AccessedStorage for any +/// memory address. It can be called on the address of a memory operation, the +/// address of a begin_access, or any other address value. If the address is +/// from an enforced begin_access or from any memory operation that is part of a +/// formal access, then it returns a valid AccessedStorage value. If the memory /// operation is not part of a formal access, then it still identifies the /// accessed location as a best effort, but the result may be invalid storage. /// -/// An active goal is to require findAccessedStorage() to always return a +/// An active goal is to require compute() to always return a /// valid AccessedStorage value even for operations that aren't part of a /// formal access. /// /// The AccessEnforcementWMO pass is an example of an optimistic optimization -/// that relies on this requirement for correctness. If findAccessedStorage() -/// simply bailed out on an unrecognized memory address by returning an invalid -/// AccessedStorage, then the optimization could make incorrect assumptions -/// about the absence of access to globals or class properties. +/// that relies on this requirement for correctness. If +/// AccessedStorage::compute() simply bailed out on an unrecognized memory +/// address by returning an invalid AccessedStorage, then the optimization could +/// make incorrect assumptions about the absence of access to globals or class +/// properties. /// -/// identifyFormalAccess() is similar to findAccessedStorage(), but returns the -/// formally accessed storage of a begin_access instruction. This must return a -/// valid AccessedStorage value unless the access has "Unsafe" enforcement. The -/// formal access location may be nested within an outer begin_access. For the -/// purpose of exclusivity, nested accesses are considered distinct formal -/// accesses so they return distinct AccessedStorage values even though they may -/// access the same memory. +/// identifyFormalAccess() is similar to AccessedStorage::compute(), but returns +/// the formally accessed storage of a begin_access instruction. This must +/// return a valid AccessedStorage value unless the access has "Unsafe" +/// enforcement. The formal access location may be nested within an outer +/// begin_access. For the purpose of exclusivity, nested accesses are considered +/// distinct formal accesses so they return distinct AccessedStorage values even +/// though they may access the same memory. /// //===----------------------------------------------------------------------===// @@ -100,7 +101,7 @@ #include "llvm/ADT/DenseMap.h" //===----------------------------------------------------------------------===// -// MARK: General Helpers +// MARK: Standalone API //===----------------------------------------------------------------------===// namespace swift { @@ -127,7 +128,7 @@ inline SILValue stripAccessMarkers(SILValue v) { /// corresponding to the accessed variable. This never looks through /// pointer_to_address or other conversions that may change the address type /// other than via type-safe (TBAA-compatible) projection. -SILValue getAccessAddress(SILValue address); +SILValue getTypedAccessAddress(SILValue address); /// Return the source address or pointer after stripping all access projections /// and storage casts. @@ -138,7 +139,7 @@ SILValue getAccessAddress(SILValue address); /// If there is no access marker, then it returns a "best effort" address /// corresponding to the accessed variable. In this case, the returned value /// could be a non-address pointer type. -SILValue getAccessBegin(SILValue address); +SILValue getAccessScope(SILValue address); /// Return the source address or pointer after stripping access projections, /// access markers, and storage casts. @@ -174,6 +175,20 @@ inline bool accessKindMayConflict(SILAccessKind a, SILAccessKind b) { namespace swift { +/// Control def-use traversals, allowing them to remain with an access scope or +/// consider operations across scope boundaries. +enum class NestedAccessType { StopAtAccessBegin, IgnoreAccessBegin }; + +/// Exact uses only include uses whose AccessPath is identical to this one. +/// Inner uses have an AccessPath the same as or contained by this one. +/// Overlapping uses may contain, be contained by, or have an unknown +/// relationship with this one. An unknown relationship typically results from +/// a dynamic index_addr offset. +/// +/// The enum values are ordered. Each successive use type is a superset of the +/// previous. +enum class AccessUseType { Exact, Inner, Overlapping }; + /// Represents the identity of a storage object being accessed. /// /// Requirements: @@ -259,6 +274,32 @@ class AccessedStorage { return storage; } + /// Return an AccessedStorage value that best identifies a formally accessed + /// variable pointed to by \p sourceAddress, looking through any nested + /// formal accesses to find the underlying storage. + /// + /// \p sourceAddress may be an address, pointer, or box type. + /// + /// If \p sourceAddress is within a formal access scope, which does not have + /// "Unsafe" enforcement, then this always returns valid storage. + /// + /// If \p sourceAddress is not within a formal access scope, or within an + /// "Unsafe" scope, then this finds the formal storage if possible, otherwise + /// returning invalid storage. + static AccessedStorage compute(SILValue sourceAddress); + + /// Return an AccessedStorage object that identifies formal access scope that + /// immediately encloses \p sourceAddress. + /// + /// \p sourceAddress may be an address, pointer, or box type. + /// + /// If \p sourceAddress is within a formal access scope, this always returns a + /// valid "Nested" storage value. + /// + /// If \p sourceAddress is not within a formal access scope, then this finds + /// the formal storage if possible, otherwise returning invalid storage. + static AccessedStorage computeInScope(SILValue sourceAddress); + protected: // Checking the storage kind is far more common than other fields. Make sure // it can be byte load with no shift. @@ -394,6 +435,7 @@ class AccessedStorage { switch (getKind()) { case AccessedStorage::Box: case AccessedStorage::Stack: + case AccessedStorage::Nested: case AccessedStorage::Argument: case AccessedStorage::Yield: case AccessedStorage::Unidentified: @@ -403,12 +445,22 @@ class AccessedStorage { case AccessedStorage::Class: case AccessedStorage::Tail: return getObject(); - case AccessedStorage::Nested: - assert(false && "AccessPath cannot identify nested access"); - return SILValue(); } } + /// Visit all access roots. If any roots are visited then the original memory + /// operation access must be reachable from one of those roots. Unidentified + /// storage might not have any root. Identified storage always has at least + /// one root. Identified non-global storage always has a single root. For + /// Global storage, this visits all global_addr instructions in the function + /// that reference the same SILGlobalVariable. + /// + /// \p function must be non-null for Global storage (global_addr cannot + /// occur in a static initializer). + void + visitRoots(SILFunction *function, + llvm::function_ref visitor) const; + /// Return true if the given storage objects have identical storage locations. /// /// This compares only the AccessedStorage base class bits, ignoring the @@ -507,6 +559,21 @@ class AccessedStorage { /// checking via the ValueDecl if we are processing a `let` variable. const ValueDecl *getDecl() const; + /// Get all leaf uses of all address, pointer, or box values that have a this + /// AccessedStorage in common. Return true if all uses were found before + /// reaching the limit. + /// + /// The caller of 'collectUses' can determine the use type (exact, inner, or + /// overlapping) from the resulting \p uses list by checking 'accessPath == + /// usePath', accessPath.contains(usePath)', and + /// 'accessPath.mayOverlap(usePath)'. Alternatively, the client may call + /// 'visitAccessedStorageUses' with its own AccessUseVisitor subclass to + /// sort the use types. + bool + collectUses(SmallVectorImpl &uses, AccessUseType useTy, + SILFunction *function, + unsigned useLimit = std::numeric_limits::max()) const; + void print(raw_ostream &os) const; void dump() const; @@ -567,6 +634,7 @@ class AccessedStorage { } // end namespace swift namespace llvm { + /// Enable using AccessedStorage as a key in DenseMap. /// Do *not* include any extra pass data in key equality. /// @@ -617,54 +685,33 @@ template <> struct DenseMapInfo { return LHS.hasIdenticalBase(RHS); } }; + } // namespace llvm namespace swift { -/// Given an address used by an instruction that reads or writes memory, return -/// the AccessedStorage value that identifies the formally accessed memory, -/// looking through any nested formal accesses to find the underlying storage. -/// -/// This may return invalid storage for a memory operation that is not part of -/// a formal access or when the outermost formal access has Unsafe enforcement. -AccessedStorage findAccessedStorage(SILValue sourceAddr); - -// Helper for identifyFormalAccess. -AccessedStorage identifyAccessedStorageImpl(SILValue sourceAddr); - -/// Return an AccessedStorage object that identifies the formal access -/// represented by \p beginAccess. -/// -/// If the given access is nested within an outer access, return a Nested -/// AccessedStorage kind. This is useful for exclusivity checking to distinguish -/// between nested access vs. conflicting access on the same storage. +/// Return an AccessedStorage value that identifies formally accessed storage +/// for \p beginAccess, considering any outer access scope as having distinct +/// storage from this access scope. This is useful for exclusivity checking +/// to distinguish between nested access vs. conflicting access on the same +/// storage. /// /// May return an invalid storage for either: /// - A \p beginAccess with Unsafe enforcement /// - Non-OSSA form in which address-type block args are allowed inline AccessedStorage identifyFormalAccess(BeginAccessInst *beginAccess) { - return identifyAccessedStorageImpl(beginAccess->getSource()); + return AccessedStorage::computeInScope(beginAccess->getSource()); } inline AccessedStorage identifyFormalAccess(BeginUnpairedAccessInst *beginAccess) { - return identifyAccessedStorageImpl(beginAccess->getSource()); -} - -/// Return a valid AccessedStorage object for an address captured by a no-escape -/// closure. A no-escape closure may capture a regular storage address without -/// guarding it with an access marker. If the captured address does come from an -/// access marker, then this returns a Nested AccessedStorage kind. -inline AccessedStorage identifyCapturedStorage(SILValue capturedAddress) { - auto storage = identifyAccessedStorageImpl(capturedAddress); - assert(storage && "captured access has invalid storage"); - return storage; + return AccessedStorage::computeInScope(beginAccess->getSource()); } } // end namespace swift //===----------------------------------------------------------------------===// -// AccessPath +// MARK: AccessPath //===----------------------------------------------------------------------===// namespace swift { @@ -700,7 +747,7 @@ namespace swift { /// convert a subobject address into a pointer (for example, via implicit /// conversion), then advance that pointer. Since we can't absolutely prevent /// this, we instead consider it an invalid AccessPath. This is the only case in -/// which AccessPath::storage can differ from findAccessedStorage(). +/// which AccessPath::storage can differ from AccessedStorage::compute(). /// /// Storing an AccessPath ammortizes to constant space. To cache identification /// of address locations, AccessPath should be used rather than the @@ -712,9 +759,23 @@ namespace swift { /// compatible type. TODO: add enforcement for this rule. class AccessPath { public: - /// Create the AccessPath for any memory operation on the given address. + /// Compute the access path at \p address. This ignores begin_access markers, + /// returning the outermost AccessedStorage. + /// + /// The computed access path corresponds to the subobject for a memory + /// operation that directly operates on \p address; so, for an indexable + /// address, this implies an operation at index zero. static AccessPath compute(SILValue address); + /// Compute the access path at \p address. If \p address is within a formal + /// access, then AccessStorage will have a nested type and base will be a + /// begin_access marker. + /// + /// This is primarily useful for recovering the access scope. The original + /// storage kind will only be discovered when \p address is part of a formal + /// access, thus not within an access scope. + static AccessPath computeInScope(SILValue address); + // Encode a dynamic index_addr as an UnknownOffset. static constexpr int UnknownOffset = std::numeric_limits::min() >> 1; @@ -848,25 +909,34 @@ class AccessPath { bool hasUnknownOffset() const { return offset == UnknownOffset; } /// Return true if this path contains \p subPath. + /// + /// Identical AccessPath's contain each other. + /// + /// Returns false if either path is invalid. bool contains(AccessPath subPath) const; /// Return true if this path may overlap with \p otherPath. + /// + /// Returns true if either path is invalid. bool mayOverlap(AccessPath otherPath) const; /// Return the address root that the access path was based on. Returns /// an invalid SILValue for globals or invalid storage. SILValue getRoot() const { return storage.getRoot(); } - /// Get all uses of all address values that have a common AccessPath. Return - /// true if all uses were found before reaching the limit. - /// - /// This should find all uses for which calling AccessPath::compute() would - /// yield an identical AccessPath. + /// Get all leaf uses of all address, pointer, or box values that have a this + /// AccessedStorage in common. Return true if all uses were found before + /// reaching the limit. /// - /// This fails on global variables which have no root. To collect all uses, - /// including global variable uses, use AccessPathWithBase::collectUses. + /// The caller of 'collectUses' can determine the use type (exact, inner, or + /// overlapping) from the resulting \p uses list by checking 'accessPath == + /// usePath', accessPath.contains(usePath)', and + /// 'accessPath.mayOverlap(usePath)'. Alternatively, the client may call + /// 'visitAccessPathUses' with its own AccessUseVisitor subclass to + /// sort the use types. bool - collectUses(SmallVectorImpl &uses, bool collectOverlappingUses, + collectUses(SmallVectorImpl &uses, AccessUseType useTy, + SILFunction *function, unsigned useLimit = std::numeric_limits::max()) const; void printPath(raw_ostream &os) const; @@ -877,10 +947,7 @@ class AccessPath { // Encapsulate the result of computing an AccessPath. AccessPath does not store // the base address of the formal access because it does not always uniquely // indentify the access, but AccessPath users may use the base address to to -// recover the def-use chain. -// -// AccessPathWithBase::collectUses is guaranteed to be complete for all storage -// types, while AccessPath::collectUses cannot handle globals. +// recover the def-use chain for a specific global_addr or ref_element_addr. struct AccessPathWithBase { AccessPath accessPath; // The address-type value that is the base of the formal access. For @@ -888,16 +955,21 @@ struct AccessPathWithBase { // global_addr or initializer apply. For other storage, it is the same as // accessPath.getRoot(). // - // base may be invalid for global_addr -> address_to_pointer -> phi patterns. + // Note: base may be invalid for global_addr -> address_to_pointer -> phi + // patterns, while the accessPath is still valid. + // // FIXME: add a structural requirement to SIL so base is always valid in OSSA. - // !!! make this a PtrIntPair with a the access kind SILValue base; - /// \p address identifies the object seen by any memory operation that - /// directly operates on the address. For indexable addresses, this implies an - /// operation at index zero. + /// Compute the access path at \p address, and record the access base. This + /// ignores begin_access markers, returning the outermost AccessedStorage. static AccessPathWithBase compute(SILValue address); + /// Compute the access path at \p address, and record the access base. If \p + /// address is within a formal access, then AccessStorage will have a nested + /// type and base will be a begin_access marker. + static AccessPathWithBase computeInScope(SILValue address); + AccessPathWithBase(AccessPath accessPath, SILValue base) : accessPath(accessPath), base(base) {} @@ -906,16 +978,6 @@ struct AccessPathWithBase { } bool operator!=(AccessPathWithBase other) const { return !(*this == other); } - /// Get all uses of all address values that have a common AccessPath. Return - /// true if all uses were found before reaching the limit. - /// - /// This should find all uses for which calling AccessPath::compute() would - /// yield an identical AccessPath and, for global variables, have the same - /// access base (e.g. from the same global_addr instruction). - bool collectUses(SmallVectorImpl &uses, - bool collectOverlappingUses, - unsigned useLimit = std::numeric_limits::max()) const; - void print(raw_ostream &os) const; void dump() const; }; @@ -924,9 +986,14 @@ inline AccessPath AccessPath::compute(SILValue address) { return AccessPathWithBase::compute(address).accessPath; } +inline AccessPath AccessPath::computeInScope(SILValue address) { + return AccessPathWithBase::compute(address).accessPath; +} + } // end namespace swift namespace llvm { + /// Allow AccessPath to be used in DenseMap. template <> struct DenseMapInfo { static inline swift::AccessPath getEmptyKey() { @@ -972,8 +1039,64 @@ template <> struct DenseMapInfo { return lhs == rhs; } }; + } // end namespace llvm +//===----------------------------------------------------------------------===// +// MARK: Use visitors +//===----------------------------------------------------------------------===// + +namespace swift { + +/// Interface to the customizable use visitor. +struct AccessUseVisitor { + AccessUseType useTy; + NestedAccessType nestedAccessTy; + + AccessUseVisitor(AccessUseType useTy, NestedAccessType nestedTy) + : useTy(useTy), nestedAccessTy(nestedTy) {} + + virtual ~AccessUseVisitor() {} + + bool findInnerUses() const { return useTy >= AccessUseType::Inner; } + bool findOverlappingUses() const { + return useTy == AccessUseType::Overlapping; + } + + bool visitExactUse(Operand *use) { + return visitUse(use, AccessUseType::Exact); + } + bool visitInnerUse(Operand *use) { + return findInnerUses() ? visitUse(use, AccessUseType::Inner) : true; + } + bool visitOverlappingUse(Operand *use) { + return + findOverlappingUses() ? visitUse(use, AccessUseType::Overlapping) : true; + } + + virtual bool visitUse(Operand *use, AccessUseType useTy) = 0; +}; + +/// Visit all uses of \p storage. +/// +/// Return true if all uses were collected. This is always true as long the \p +/// visitor's visitUse method returns true. +bool visitAccessedStorageUses(AccessUseVisitor &visitor, + AccessedStorage storage, + SILFunction *function); + +/// Visit the uses of \p accessPath. +/// +/// If the storage kind is Global, then function must be non-null (global_addr +/// only occurs inside SILFunction). +/// +/// Return true if all uses were collected. This is always true as long the \p +/// visitor's visitUse method returns true. +bool visitAccessPathUses(AccessUseVisitor &visitor, AccessPath accessPath, + SILFunction *function); + +} // end namespace swift + //===----------------------------------------------------------------------===// // MARK: Helper API for specific formal access patterns //===----------------------------------------------------------------------===// @@ -1031,7 +1154,8 @@ void checkSwitchEnumBlockArg(SILPhiArgument *arg); /// This is not a member of AccessedStorage because it only makes sense to use /// in SILGen before access markers are emitted, or when verifying access /// markers. -bool isPossibleFormalAccessBase(const AccessedStorage &storage, SILFunction *F); +bool isPossibleFormalAccessBase(const AccessedStorage &storage, + SILFunction *F); /// Perform a RAUW operation on begin_access with it's own source operand. /// Then erase the begin_access and all associated end_access instructions. diff --git a/include/swift/SILOptimizer/Analysis/ValueTracking.h b/include/swift/SILOptimizer/Analysis/ValueTracking.h index be2ca665ae6b4..f98d90132e8d4 100644 --- a/include/swift/SILOptimizer/Analysis/ValueTracking.h +++ b/include/swift/SILOptimizer/Analysis/ValueTracking.h @@ -59,7 +59,7 @@ bool pointsToLocalObject(SILValue V); inline bool isUniquelyIdentified(SILValue V) { SILValue objectRef = V; if (V->getType().isAddress()) { - auto storage = findAccessedStorage(V); + auto storage = AccessedStorage::compute(V); if (!storage) return false; diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 690708643afe8..bbf99ab5b65fb 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -4125,7 +4125,7 @@ void IRGenSILFunction::visitRefTailAddrInst(RefTailAddrInst *i) { } static bool isInvariantAddress(SILValue v) { - SILValue accessedAddress = getAccessAddress(v); + SILValue accessedAddress = getTypedAccessAddress(v); if (auto *ptrRoot = dyn_cast(accessedAddress)) { return ptrRoot->isInvariant(); } diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index 05470396d48ac..3673f095b6790 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -20,7 +20,7 @@ using namespace swift; //===----------------------------------------------------------------------===// -// MARK: General Helpers +// MARK: FindAccessVisitor //===----------------------------------------------------------------------===// namespace { @@ -146,8 +146,6 @@ class AccessPhiVisitor } }; -enum NestedAccessTy { StopAtAccessBegin, IgnoreAccessBegin }; - // Find the origin of an access while skipping projections and casts and // handling phis. template @@ -155,14 +153,14 @@ class FindAccessVisitorImpl : public AccessUseDefChainVisitor { using SuperTy = AccessUseDefChainVisitor; protected: - NestedAccessTy nestedAccessTy; + NestedAccessType nestedAccessTy; StorageCastTy storageCastTy; SmallPtrSet visitedPhis; bool hasUnknownOffset = false; public: - FindAccessVisitorImpl(NestedAccessTy nestedAccessTy, + FindAccessVisitorImpl(NestedAccessType nestedAccessTy, StorageCastTy storageCastTy) : nestedAccessTy(nestedAccessTy), storageCastTy(storageCastTy) {} @@ -188,7 +186,7 @@ class FindAccessVisitorImpl : public AccessUseDefChainVisitor { // Override AccessUseDefChainVisitor to ignore access markers and find the // outer access base. SILValue visitNestedAccess(BeginAccessInst *access) { - if (nestedAccessTy == IgnoreAccessBegin) + if (nestedAccessTy == NestedAccessType::IgnoreAccessBegin) return access->getSource(); return SuperTy::visitNestedAccess(access); @@ -245,10 +243,10 @@ class FindAccessVisitorImpl : public AccessUseDefChainVisitor { return; } llvm::errs() << "Visiting "; - sourceAddr->dump(); + sourceAddr->print(llvm::errs()); llvm::errs() << " not an address "; - nextAddr->dump(); - nextAddr->getFunction()->dump(); + nextAddr->print(llvm::errs()); + nextAddr->getFunction()->print(llvm::errs()); assert(false); } }; @@ -262,7 +260,7 @@ class FindAccessBaseVisitor Optional base; public: - FindAccessBaseVisitor(NestedAccessTy nestedAccessTy, + FindAccessBaseVisitor(NestedAccessType nestedAccessTy, StorageCastTy storageCastTy) : FindAccessVisitorImpl(nestedAccessTy, storageCastTy) {} @@ -315,10 +313,15 @@ class FindAccessBaseVisitor } // end anonymous namespace -SILValue swift::getAccessAddress(SILValue address) { +//===----------------------------------------------------------------------===// +// MARK: Standalone API +//===----------------------------------------------------------------------===// + +SILValue swift::getTypedAccessAddress(SILValue address) { assert(address->getType().isAddress()); SILValue accessAddress = - FindAccessBaseVisitor(StopAtAccessBegin, StopAtStorageCast) + FindAccessBaseVisitor(NestedAccessType::StopAtAccessBegin, + StopAtStorageCast) .findBase(address); assert(accessAddress->getType().isAddress()); return accessAddress; @@ -327,15 +330,17 @@ SILValue swift::getAccessAddress(SILValue address) { // TODO: When the optimizer stops stripping begin_access markers and SILGen // protects all memory operations with at least an "unsafe" access scope, then // we should be able to assert that this returns a BeginAccessInst. -SILValue swift::getAccessBegin(SILValue address) { +SILValue swift::getAccessScope(SILValue address) { assert(address->getType().isAddress()); - return FindAccessBaseVisitor(StopAtAccessBegin, IgnoreStorageCast) + return FindAccessBaseVisitor(NestedAccessType::StopAtAccessBegin, + IgnoreStorageCast) .findBase(address); } // This is allowed to be called on a non-address pointer type. SILValue swift::getAccessBase(SILValue address) { - return FindAccessBaseVisitor(IgnoreAccessBegin, IgnoreStorageCast) + return FindAccessBaseVisitor(NestedAccessType::IgnoreAccessBegin, + IgnoreStorageCast) .findBase(address); } @@ -357,7 +362,7 @@ bool swift::isLetAddress(SILValue address) { } //===----------------------------------------------------------------------===// -// MARK: FindReferenceRoot +// MARK: FindReferenceRoot //===----------------------------------------------------------------------===// namespace { @@ -432,6 +437,18 @@ static SILValue findReferenceRoot(SILValue ref) { // MARK: AccessedStorage //===----------------------------------------------------------------------===// +SILGlobalVariable *getReferencedGlobal(SILInstruction *inst) { + if (auto *gai = dyn_cast(inst)) { + return gai->getReferencedGlobal(); + } + if (auto apply = FullApplySite::isa(inst)) { + if (auto *funcRef = apply.getReferencedFunctionOrNull()) { + return getVariableOfGlobalInit(funcRef); + } + } + return nullptr; +} + constexpr unsigned AccessedStorage::TailIndex; AccessedStorage::AccessedStorage(SILValue base, Kind kind) { @@ -463,19 +480,13 @@ AccessedStorage::AccessedStorage(SILValue base, Kind kind) { setElementIndex(cast(base)->getIndex()); break; case Global: - if (auto *GAI = dyn_cast(base)) - global = GAI->getReferencedGlobal(); - else { - FullApplySite apply(cast(base)); - auto *funcRef = apply.getReferencedFunctionOrNull(); - assert(funcRef); - global = getVariableOfGlobalInit(funcRef); - assert(global); - // Require a decl for all formally accessed globals defined in this - // module. (Access of globals defined elsewhere has Unidentified storage). - // AccessEnforcementWMO requires this. - assert(global->getDecl()); - } + global = getReferencedGlobal(cast(base)); + // Require a decl for all formally accessed globals defined in this + // module. AccessEnforcementWMO requires this. Swift globals defined in + // another module either use an addressor, which has Unidentified + // storage. Imported non-Swift globals are accessed via global_addr but have + // no declaration. + assert(global->getDecl() || isa(base)); break; case Class: { // Do a best-effort to find the identity of the object being projected @@ -496,6 +507,27 @@ AccessedStorage::AccessedStorage(SILValue base, Kind kind) { } } +void AccessedStorage::visitRoots( + SILFunction *function, + llvm::function_ref visitor) const { + if (SILValue root = getRoot()) { + visitor(root); + return; + } + if (getKind() == Unidentified) { + return; + } + assert(getKind() == Global && function); + SILGlobalVariable *global = getGlobal(); + for (auto &block : *function) { + for (auto &instruction : block) { + if (global == getReferencedGlobal(&instruction)) { + visitor(cast(&instruction)); + } + } + } +} + // Return true if the given access is on a 'let' lvalue. bool AccessedStorage::isLetAccess(SILFunction *F) const { if (auto *decl = dyn_cast_or_null(getDecl())) @@ -633,7 +665,7 @@ class FindAccessedStorageVisitor } public: - FindAccessedStorageVisitor(NestedAccessTy nestedAccessTy) + FindAccessedStorageVisitor(NestedAccessType nestedAccessTy) : FindAccessVisitorImpl(nestedAccessTy, IgnoreStorageCast) {} // Main entry point @@ -676,41 +708,43 @@ class FindAccessedStorageVisitor } // end anonymous namespace -AccessedStorage swift::findAccessedStorage(SILValue sourceAddr) { - FindAccessedStorageVisitor visitor(IgnoreAccessBegin); - visitor.findStorage(sourceAddr); +AccessedStorage AccessedStorage::compute(SILValue sourceAddress) { + FindAccessedStorageVisitor visitor(NestedAccessType::IgnoreAccessBegin); + visitor.findStorage(sourceAddress); return visitor.getStorage(); } -AccessedStorage swift::identifyAccessedStorageImpl(SILValue sourceAddr) { - FindAccessedStorageVisitor visitor(StopAtAccessBegin); - visitor.findStorage(sourceAddr); +AccessedStorage AccessedStorage::computeInScope(SILValue sourceAddress) { + FindAccessedStorageVisitor visitor(NestedAccessType::StopAtAccessBegin); + visitor.findStorage(sourceAddress); return visitor.getStorage(); } //===----------------------------------------------------------------------===// -// AccessPath +// MARK: AccessPath //===----------------------------------------------------------------------===// bool AccessPath::contains(AccessPath subPath) const { - assert(isValid() && subPath.isValid()); - - if (!storage.hasIdenticalBase(subPath.storage)) + if (!isValid() || !subPath.isValid()) { return false; - + } + if (!storage.hasIdenticalBase(subPath.storage)) { + return false; + } // Does the offset index match? - if (offset != subPath.offset || offset == UnknownOffset) + if (offset != subPath.offset || offset == UnknownOffset) { return false; - + } return pathNode.node->isPrefixOf(subPath.pathNode.node); } bool AccessPath::mayOverlap(AccessPath otherPath) const { - assert(isValid() && otherPath.isValid()); + if (!isValid() || !otherPath.isValid()) + return true; - if (storage.isDistinctFrom(otherPath.storage)) + if (storage.isDistinctFrom(otherPath.storage)) { return false; - + } // If subpaths are disjoint, they do not overlap regardless of offset. if (!pathNode.node->isPrefixOf(otherPath.pathNode.node) && !otherPath.pathNode.node->isPrefixOf(pathNode.node)) { @@ -752,9 +786,9 @@ class AccessPathVisitor : public FindAccessVisitorImpl { int pendingOffset = 0; public: - AccessPathVisitor(SILModule *module) - : FindAccessVisitorImpl(IgnoreAccessBegin, IgnoreStorageCast), - module(module), storageVisitor(IgnoreAccessBegin) {} + AccessPathVisitor(SILModule *module, NestedAccessType nestedAccessTy) + : FindAccessVisitorImpl(nestedAccessTy, IgnoreStorageCast), + module(module), storageVisitor(NestedAccessType::IgnoreAccessBegin) {} // Main entry point. AccessPathWithBase findAccessPath(SILValue sourceAddr) && { @@ -862,37 +896,112 @@ class AccessPathVisitor : public FindAccessVisitorImpl { } // end anonymous namespace AccessPathWithBase AccessPathWithBase::compute(SILValue address) { - return AccessPathVisitor(address->getModule()).findAccessPath(address); + return AccessPathVisitor(address->getModule(), + NestedAccessType::IgnoreAccessBegin) + .findAccessPath(address); +} + +AccessPathWithBase AccessPathWithBase::computeInScope(SILValue address) { + return AccessPathVisitor(address->getModule(), + NestedAccessType::StopAtAccessBegin) + .findAccessPath(address); } +void AccessPath::Index::print(raw_ostream &os) const { + if (isSubObjectProjection()) + os << '#' << getSubObjectIndex(); + else { + os << '@'; + if (isUnknownOffset()) + os << "Unknown"; + else + os << getOffset(); + } +} + +LLVM_ATTRIBUTE_USED void AccessPath::Index::dump() const { + print(llvm::dbgs()); +} + +static void recursivelyPrintPath(AccessPath::PathNode node, raw_ostream &os) { + AccessPath::PathNode parent = node.getParent(); + if (!parent.isRoot()) { + recursivelyPrintPath(parent, os); + os << ","; + } + node.getIndex().print(os); +} + +void AccessPath::printPath(raw_ostream &os) const { + os << "Path: "; + if (!isValid()) { + os << "INVALID\n"; + return; + } + os << "("; + PathNode node = getPathNode(); + if (offset != 0) { + Index::forOffset(offset).print(os); + if (!node.isRoot()) + os << ","; + } + if (!node.isRoot()) + recursivelyPrintPath(node, os); + os << ")\n"; +} + +void AccessPath::print(raw_ostream &os) const { + if (!isValid()) { + os << "INVALID\n"; + return; + } + os << "Storage: "; + getStorage().print(os); + printPath(os); +} + +LLVM_ATTRIBUTE_USED void AccessPath::dump() const { print(llvm::dbgs()); } + +void AccessPathWithBase::print(raw_ostream &os) const { + if (base) + os << "Base: " << base; + + accessPath.print(os); +} + +LLVM_ATTRIBUTE_USED void AccessPathWithBase::dump() const { + print(llvm::dbgs()); +} + +//===----------------------------------------------------------------------===// +// MARK: AccessPathDefUseTraversal +//===----------------------------------------------------------------------===// + namespace { // Perform def-use DFS traversal along a given AccessPath. DFS terminates at // each discovered use. // -// If \p collectOverlappingUses is false, then the collected uses all have the -// same AccessPath. Uses that exactly match the AccessPath may either be exact -// uses, or may be subobject projections within that access path, including -// struct_element_addr and tuple_element_addr. The transitive uses of those -// subobject projections are not included. +// For useTy == Exact, the collected uses all have the same AccessPath. +// Subobject projections within that access path and their transitive uses are +// not included. // -// If \p collectOverlappingUses is true, then the collected uses also include -// uses that access an object that contains the given AccessPath. As before, -// overlapping uses do not include transitive uses of subobject projections -// contained by the current path; the def-use traversal stops at those -// projections regardless of collectOverlappingUses. However, overlapping uses -// may be at an unknown offset relative to the current path, so they don't -// necessarily contain the current path. +// For useTy == Inner, the collected uses to subobjects contained by the +// current access path. // -// Example: path = "(#2)" +// For useTy == Overlapping, the collected uses also include uses that +// access an object that contains the given AccessPath as well as uses at +// an unknown offset relative to the current path. +// +// Example, where AccessPath == (#2): // %base = ... // access base // load %base // containing use // %elt1 = struct_element_addr %base, #1 // non-use (ignored) // load %elt1 // non-use (unseen) -// %elt2 = struct_element_addr %base, #2 // chained use (ignored) +// %elt2 = struct_element_addr %base, #2 // outer projection (followed) // load %elt2 // exact use -// %sub = struct_element_addr %elt2, #i // projection use -// load %sub // interior use (ignored) +// %sub = struct_element_addr %elt2, #i // inner projection (followed) +// load %sub // inner use // // A use may be a BranchInst if the corresponding phi does not have common // AccessedStorage. @@ -903,18 +1012,15 @@ namespace { // %ref = ... // reference root // %base = ref_element_addr %refRoot // formal access address // load %base // use -class CollectAccessPathUses { - // Origin of the def-use traversal - AccessedStorage storage; - - // Result: Exact uses, projection uses, and containing uses. - SmallVectorImpl &uses; +class AccessPathDefUseTraversal { + AccessUseVisitor &visitor; - bool collectOverlappingUses; - unsigned useLimit; + // The origin of the def-use traversal. + AccessedStorage storage; - // Access path indices from storage to exact uses - SmallVector pathIndices; // in use-def order + // Remaining access path indices from the most recently visited def to any + // exact use in def-use order. + SmallVector pathIndices; // A point in the def-use traversal. isRef() is true only for object access // prior to reaching the base address. @@ -922,10 +1028,10 @@ class CollectAccessPathUses { // Next potential use to visit and flag indicating whether traversal has // reachaed the access base yet. llvm::PointerIntPair useAndIsRef; - unsigned pathCursor; // position within pathIndices - int offset; // index_addr offsets seen prior to this use + int pathCursor; // position within pathIndices + int offset; // index_addr offsets seen prior to this use - DFSEntry(Operand *use, bool isRef, unsigned pathCursor, int offset) + DFSEntry(Operand *use, bool isRef, int pathCursor, int offset) : useAndIsRef(use, isRef), pathCursor(pathCursor), offset(offset) {} Operand *getUse() const { return useAndIsRef.getPointer(); } @@ -936,57 +1042,31 @@ class CollectAccessPathUses { SmallPtrSet visitedPhis; + // Transient traversal data should not be copied. + AccessPathDefUseTraversal(const AccessPathDefUseTraversal &) = delete; + AccessPathDefUseTraversal & + operator=(const AccessPathDefUseTraversal &) = delete; + public: - CollectAccessPathUses(AccessPath accessPath, SmallVectorImpl &uses, - bool collectOverlappingUses, unsigned useLimit) - : storage(accessPath.getStorage()), uses(uses), - collectOverlappingUses(collectOverlappingUses), useLimit(useLimit) { + AccessPathDefUseTraversal(AccessUseVisitor &visitor, AccessPath accessPath, + SILFunction *function) + : visitor(visitor), storage(accessPath.getStorage()) { assert(accessPath.isValid()); - for (AccessPath::PathNode currentNode = accessPath.getPathNode(); - !currentNode.isRoot(); currentNode = currentNode.getParent()) { - assert(currentNode.getIndex().isSubObjectProjection() && - "a valid AccessPath does not contain any intermediate offsets"); - pathIndices.push_back(currentNode.getIndex()); - } - if (int offset = accessPath.getOffset()) - pathIndices.push_back(AccessPath::Index::forOffset(offset)); - - // The search will start from the object root, not the formal access base, - // so add the class index to the front. - auto storage = accessPath.getStorage(); - if (storage.getKind() == AccessedStorage::Class) { - pathIndices.push_back(AccessPath::Index::forSubObjectProjection( - storage.getPropertyIndex())); - } - if (storage.getKind() == AccessedStorage::Tail) { - pathIndices.push_back(AccessPath::Index::forSubObjectProjection( - ProjectionIndex::TailIndex)); - } - } - // Return true if all uses were collected. This is always true as long as the - // access has a single root, or globalBase is provided, and there is no - // useLimit. - // - // For Global storage \p globalBase must be provided as the head of the - // def-use search. - bool collectUses(SILValue globalBase = SILValue()) && { - SILValue root = storage.getRoot(); - if (!root) { - assert(storage.getKind() == AccessedStorage::Global); - if (!globalBase) - return false; + initializePathIndices(accessPath); - root = globalBase; - } - // If the expected path has an unknown offset, then none of the uses are - // exact. - if (!collectOverlappingUses && !pathIndices.empty() - && pathIndices.back().isUnknownOffset()) { + storage.visitRoots(function, [this](SILValue root) { + initializeDFS(root); return true; + }); + } + + // Return true is all uses have been visited. + bool visitUses() { + // Return false if initialization failed. + if (!storage) { + return false; } - pushUsers(root, - DFSEntry(nullptr, storage.isReference(), pathIndices.size(), 0)); while (!dfsStack.empty()) { if (!visitUser(dfsStack.pop_back_val())) return false; @@ -995,6 +1075,16 @@ class CollectAccessPathUses { } protected: + void initializeDFS(SILValue root) { + // If root is a phi, record it so that its uses aren't visited twice. + if (auto *phi = dyn_cast(root)) { + if (phi->isPhiArgument()) + visitedPhis.insert(phi); + } + pushUsers(root, + DFSEntry(nullptr, storage.isReference(), pathIndices.size(), 0)); + } + void pushUsers(SILValue def, const DFSEntry &dfs) { for (auto *use : def->getUses()) pushUser(DFSEntry(use, dfs.isRef(), dfs.pathCursor, dfs.offset)); @@ -1010,135 +1100,172 @@ class CollectAccessPathUses { dfsStack.emplace_back(dfs); } - // Return true if this phi has been processed and does not need to be - // considered as a separate use. - bool pushPhiUses(const SILPhiArgument *phi, DFSEntry dfs) { - if (!visitedPhis.insert(phi).second) - return true; + bool pushPhiUses(const SILPhiArgument *phi, DFSEntry dfs); - // If this phi has a common base, continue to follow the access path. This - // check is different for reference types vs pointer types. - if (dfs.isRef()) { - assert(!dfs.offset && "index_addr not allowed on reference roots"); - // When isRef is true, the address access hasn't been seen yet and - // we're still following the reference root's users. Check if all phi - // inputs have the same reference root before looking through it. - if (findReferenceRoot(phi) == storage.getObject()) { - pushUsers(phi, dfs); - return true; - } - // The branch will be pushed onto the normal user list. - return false; - } - // Check if all phi inputs have the same accessed storage before - // looking through it. If the phi input differ the its storage is invalid. - auto phiPath = AccessPath::compute(phi); - if (phiPath.isValid()) { - assert(phiPath.getStorage().hasIdenticalBase(storage) - && "inconsistent phi storage"); - // If the phi paths have different offsets, its path has unknown offset. - if (phiPath.getOffset() == AccessPath::UnknownOffset) { - if (!collectOverlappingUses) - return true; - dfs.offset = AccessPath::UnknownOffset; - } - pushUsers(phi, dfs); - return true; - } - // The branch will be pushed onto the normal user list. - return false; - } + void initializePathIndices(AccessPath accessPath); // Return the offset at the current DFS path cursor, or zero. - int getPathOffset(const DFSEntry &dfs) const { - if (dfs.pathCursor == 0 - || pathIndices[dfs.pathCursor - 1].isSubObjectProjection()) { - return 0; - } - return pathIndices[dfs.pathCursor - 1].getOffset(); - } + int getPathOffset(const DFSEntry &dfs) const; - // Returns true as long as the useLimit is not reached. - bool visitUser(DFSEntry dfs) { - Operand *use = dfs.getUse(); - assert(!(dfs.isRef() && use->get()->getType().isAddress())); - if (auto *svi = dyn_cast(use->getUser())) { - if (use->getOperandNumber() == 0 - && visitSingleValueUser(svi, dfs) == IgnoredUse) { - return true; - } - } - // We weren't able to "see through" any more address conversions; so - // record this as a use. + // Return true if the accumulated offset matches the current path index. + // Update the DFSEntry and pathCursor to skip remaining offsets. + bool checkAndUpdateOffset(DFSEntry &dfs); - // Do the path offsets match? - if (!checkAndUpdateOffset(dfs)) - return true; + // Handle non-index_addr projections. + void followProjection(SingleValueInstruction *svi, DFSEntry dfs); - // Is this a full or partial path match? - if (!collectOverlappingUses && dfs.pathCursor > 0) - return true; + enum UseKind { LeafUse, IgnoredUse }; + UseKind visitSingleValueUser(SingleValueInstruction *svi, DFSEntry dfs); - // Record the use if we haven't reached the limit. - if (uses.size() == useLimit) - return false; + // Returns true as long as the visitor returns true. + bool visitUser(DFSEntry dfs); +}; - uses.push_back(use); - return true; - } +} // end anonymous namespace - // Return true if the accumulated offset matches the current path index. - // Update the DFSEntry and pathCursor to skip remaining offsets. - bool checkAndUpdateOffset(DFSEntry &dfs) { - int pathOffset = getPathOffset(dfs); - if (pathOffset == 0) { - // No offset is on the expected path. - if (collectOverlappingUses && dfs.offset == AccessPath::UnknownOffset) { - dfs.offset = 0; - } - return dfs.offset == 0; - } - // pop the offset from the expected path; there should only be one. - --dfs.pathCursor; - assert(getPathOffset(dfs) == 0 && "only one offset index allowed"); +// Initialize the array of remaining path indices. +void AccessPathDefUseTraversal::initializePathIndices(AccessPath accessPath) { + for (AccessPath::PathNode currentNode = accessPath.getPathNode(); + !currentNode.isRoot(); currentNode = currentNode.getParent()) { + assert(currentNode.getIndex().isSubObjectProjection() + && "a valid AccessPath does not contain any intermediate offsets"); + pathIndices.push_back(currentNode.getIndex()); + } + if (int offset = accessPath.getOffset()) { + pathIndices.push_back(AccessPath::Index::forOffset(offset)); + } + // The search will start from the object root, not the formal access base, + // so add the class index to the front. + if (storage.getKind() == AccessedStorage::Class) { + pathIndices.push_back( + AccessPath::Index::forSubObjectProjection(storage.getPropertyIndex())); + } + if (storage.getKind() == AccessedStorage::Tail) { + pathIndices.push_back( + AccessPath::Index::forSubObjectProjection(ProjectionIndex::TailIndex)); + } + // If the expected path has an unknown offset, then none of the uses are + // exact. + if (!visitor.findOverlappingUses() && !pathIndices.empty() + && pathIndices.back().isUnknownOffset()) { + return; + } +} - int useOffset = dfs.offset; - dfs.offset = 0; +// Return true if this phi has been processed and does not need to be +// considered as a separate use. +bool AccessPathDefUseTraversal::pushPhiUses(const SILPhiArgument *phi, + DFSEntry dfs) { + if (!visitedPhis.insert(phi).second) + return true; - // Ignore all uses on this path unless we're collecting containing uses. - // UnknownOffset appears to overlap with all offsets and subobject uses. - if (pathOffset == AccessPath::UnknownOffset - || useOffset == AccessPath::UnknownOffset) { - return collectOverlappingUses; + // If this phi has a common base, continue to follow the access path. This + // check is different for reference types vs pointer types. + if (dfs.isRef()) { + assert(!dfs.offset && "index_addr not allowed on reference roots"); + // When isRef is true, the address access hasn't been seen yet and + // we're still following the reference root's users. Check if all phi + // inputs have the same reference root before looking through it. + if (findReferenceRoot(phi) == storage.getObject()) { + pushUsers(phi, dfs); + return true; } - // A known offset must match regardless of collectOverlappingUses. - return pathOffset == useOffset; + // The branch will be pushed onto the normal user list. + return false; } + // Check if all phi inputs have the same accessed storage before + // looking through it. If the phi input differ the its storage is invalid. + auto phiPath = AccessPath::compute(phi); + if (phiPath.isValid()) { + assert(phiPath.getStorage().hasIdenticalBase(storage) + && "inconsistent phi storage"); + // If the phi paths have different offsets, its path has unknown offset. + if (phiPath.getOffset() == AccessPath::UnknownOffset) { + if (!visitor.findOverlappingUses()) + return true; + dfs.offset = AccessPath::UnknownOffset; + } + pushUsers(phi, dfs); + return true; + } + // The branch will be pushed onto the normal user list. + return false; +} - enum UseKind { LeafUse, IgnoredUse }; - UseKind visitSingleValueUser(SingleValueInstruction *svi, DFSEntry dfs); - - // Handle non-index_addr projections. - UseKind followProjection(SingleValueInstruction *svi, DFSEntry dfs) { - if (!checkAndUpdateOffset(dfs)) - return IgnoredUse; - - if (dfs.pathCursor == 0) - return LeafUse; +// Return the offset at the current DFS path cursor, or zero. +int AccessPathDefUseTraversal::getPathOffset(const DFSEntry &dfs) const { + if (dfs.pathCursor <= 0 + || pathIndices[dfs.pathCursor - 1].isSubObjectProjection()) { + return 0; + } + return pathIndices[dfs.pathCursor - 1].getOffset(); +} - AccessPath::Index pathIndex = pathIndices[dfs.pathCursor - 1]; - auto projIdx = ProjectionIndex(svi); - assert(projIdx.isValid()); - // Only subobjects indices are expected because offsets are handled above. - if (projIdx.Index == pathIndex.getSubObjectIndex()) { +// Return true if the accumulated offset matches the current path index. +// Update the DFSEntry and pathCursor to skip remaining offsets. +bool AccessPathDefUseTraversal::checkAndUpdateOffset(DFSEntry &dfs) { + int pathOffset = getPathOffset(dfs); + if (dfs.offset == AccessPath::UnknownOffset) { + if (pathOffset > 0) { + // Pop the offset from the expected path; there should only be + // one. Continue matching subobject indices even after seeing an unknown + // offset. A subsequent mismatching subobject index is still considered + // non-overlapping. This is valid for aliasing since an offset from a + // subobject is considered an invalid access path. --dfs.pathCursor; - pushUsers(svi, dfs); + assert(getPathOffset(dfs) == 0 && "only one offset index allowed"); } - return IgnoredUse; - } -}; + // Continue searching only if we need to find overlapping uses. Preserve the + // unknown dfs offset so we don't consider any dependent operations to be + // exact or inner uses. + return visitor.findOverlappingUses(); + } + if (pathOffset == 0) { + return dfs.offset == 0; + } + // pop the offset from the expected path; there should only be one. + --dfs.pathCursor; + assert(getPathOffset(dfs) == 0 && "only one offset index allowed"); + + // Ignore all uses on this path unless we're collecting containing uses. + // UnknownOffset appears to overlap with all offsets and subobject uses. + if (pathOffset == AccessPath::UnknownOffset) { + // Set the dfs offset to unknown to avoid considering any dependent + // operations as exact or inner uses. + dfs.offset = AccessPath::UnknownOffset; + return visitor.findOverlappingUses(); + } + int useOffset = dfs.offset; + dfs.offset = 0; + // A known offset must match regardless of findOverlappingUses. + return pathOffset == useOffset; +} -} // end anonymous namespace +// Handle non-index_addr projections. +void AccessPathDefUseTraversal::followProjection(SingleValueInstruction *svi, + DFSEntry dfs) { + if (!checkAndUpdateOffset(dfs)) { + return; + } + if (dfs.pathCursor <= 0) { + if (visitor.useTy == AccessUseType::Exact) { + assert(dfs.pathCursor == 0); + return; + } + --dfs.pathCursor; + pushUsers(svi, dfs); + return; + } + AccessPath::Index pathIndex = pathIndices[dfs.pathCursor - 1]; + auto projIdx = ProjectionIndex(svi); + assert(projIdx.isValid()); + // Only subobjects indices are expected because offsets are handled above. + if (projIdx.Index == pathIndex.getSubObjectIndex()) { + --dfs.pathCursor; + pushUsers(svi, dfs); + } + return; +} // During the def-use traversal, visit a single-value instruction in which the // used address is at operand zero. @@ -1151,9 +1278,9 @@ class CollectAccessPathUses { // // FIXME: Reuse getAccessProjectionOperand() instead of using special cases once // the unchecked_take_enum_data_addr -> load -> project_box pattern is fixed. -CollectAccessPathUses::UseKind -CollectAccessPathUses::visitSingleValueUser(SingleValueInstruction *svi, - DFSEntry dfs) { +AccessPathDefUseTraversal::UseKind +AccessPathDefUseTraversal::visitSingleValueUser(SingleValueInstruction *svi, + DFSEntry dfs) { if (isAccessedStorageCast(svi)) { pushUsers(svi, dfs); return IgnoredUse; @@ -1163,6 +1290,9 @@ CollectAccessPathUses::visitSingleValueUser(SingleValueInstruction *svi, return LeafUse; case SILInstructionKind::BeginAccessInst: + if (visitor.nestedAccessTy == NestedAccessType::StopAtAccessBegin) { + return LeafUse; + } pushUsers(svi, dfs); return IgnoredUse; @@ -1172,7 +1302,8 @@ CollectAccessPathUses::visitSingleValueUser(SingleValueInstruction *svi, assert(dfs.isRef()); assert(dfs.pathCursor > 0 && "ref_element_addr cannot occur within access"); dfs.useAndIsRef.setInt(false); - return followProjection(svi, dfs); + followProjection(svi, dfs); + return IgnoredUse; case SILInstructionKind::RefTailAddrInst: { assert(dfs.isRef()); @@ -1191,7 +1322,8 @@ CollectAccessPathUses::visitSingleValueUser(SingleValueInstruction *svi, case SILInstructionKind::StructElementAddrInst: case SILInstructionKind::TupleElementAddrInst: - return followProjection(svi, dfs); + followProjection(svi, dfs); + return IgnoredUse; case SILInstructionKind::IndexAddrInst: case SILInstructionKind::TailAddrInst: { @@ -1200,8 +1332,8 @@ CollectAccessPathUses::visitSingleValueUser(SingleValueInstruction *svi, if (dfs.offset != AccessPath::UnknownOffset) dfs.offset += projIdx.Index; else - assert(collectOverlappingUses); - } else if (collectOverlappingUses) { + assert(visitor.findOverlappingUses()); + } else if (visitor.findOverlappingUses()) { dfs.offset = AccessPath::UnknownOffset; } else { return IgnoredUse; @@ -1248,91 +1380,71 @@ CollectAccessPathUses::visitSingleValueUser(SingleValueInstruction *svi, } } -bool AccessPath::collectUses(SmallVectorImpl &uses, - bool collectOverlappingUses, - unsigned useLimit) const { - return CollectAccessPathUses(*this, uses, collectOverlappingUses, useLimit) - .collectUses(); -} - -bool AccessPathWithBase::collectUses(SmallVectorImpl &uses, - bool collectOverlappingUses, - unsigned useLimit) const { - CollectAccessPathUses collector(accessPath, uses, collectOverlappingUses, - useLimit); - if (accessPath.getRoot()) - return std::move(collector).collectUses(); - - if (!base) - return false; - - return std::move(collector).collectUses(base); -} - -void AccessPath::Index::print(raw_ostream &os) const { - if (isSubObjectProjection()) - os << '#' << getSubObjectIndex(); - else { - os << '@'; - if (isUnknownOffset()) - os << "Unknown"; - else - os << getOffset(); +bool AccessPathDefUseTraversal::visitUser(DFSEntry dfs) { + Operand *use = dfs.getUse(); + assert(!(dfs.isRef() && use->get()->getType().isAddress())); + if (auto *svi = dyn_cast(use->getUser())) { + if (use->getOperandNumber() == 0 + && visitSingleValueUser(svi, dfs) == IgnoredUse) { + return true; + } } -} + // We weren't able to "see through" any more address conversions; so + // record this as a use. -LLVM_ATTRIBUTE_USED void AccessPath::Index::dump() const { - print(llvm::dbgs()); -} + // Do the path offsets match? + if (!checkAndUpdateOffset(dfs)) + return true; -static void recursivelyPrintPath(AccessPath::PathNode node, raw_ostream &os) { - AccessPath::PathNode parent = node.getParent(); - if (!parent.isRoot()) { - recursivelyPrintPath(parent, os); - os << ","; + // Is this a partial path match? + if (dfs.pathCursor > 0 || dfs.offset == AccessPath::UnknownOffset) { + return visitor.visitOverlappingUse(use); } - node.getIndex().print(os); + if (dfs.pathCursor < 0) { + return visitor.visitInnerUse(use); + } + return visitor.visitExactUse(use); } -void AccessPath::printPath(raw_ostream &os) const { - os << "Path: "; - if (!isValid()) { - os << "INVALID\n"; - return; - } - os << "("; - PathNode node = getPathNode(); - if (offset != 0) { - Index::forOffset(offset).print(os); - if (!node.isRoot()) - os << ","; - } - if (!node.isRoot()) - recursivelyPrintPath(node, os); - os << ")\n"; +bool swift::visitAccessPathUses(AccessUseVisitor &visitor, + AccessPath accessPath, SILFunction *function) { + return AccessPathDefUseTraversal(visitor, accessPath, function).visitUses(); } -void AccessPath::print(raw_ostream &os) const { - if (!isValid()) { - os << "INVALID\n"; - return; - } - os << "Storage: "; - getStorage().print(os); - printPath(os); +bool swift::visitAccessedStorageUses(AccessUseVisitor &visitor, + AccessedStorage storage, + SILFunction *function) { + IndexTrieNode *emptyPath = function->getModule().getIndexTrieRoot(); + return visitAccessPathUses(visitor, AccessPath(storage, emptyPath, 0), + function); } -LLVM_ATTRIBUTE_USED void AccessPath::dump() const { print(llvm::dbgs()); } +class CollectAccessPathUses : public AccessUseVisitor { + // Result: Exact uses, projection uses, and containing uses. + SmallVectorImpl &uses; -void AccessPathWithBase::print(raw_ostream &os) const { - if (base) - os << "Base: " << base; + unsigned useLimit; - accessPath.print(os); -} +public: + CollectAccessPathUses(SmallVectorImpl &uses, AccessUseType useTy, + unsigned useLimit) + : AccessUseVisitor(useTy, NestedAccessType::IgnoreAccessBegin), uses(uses), + useLimit(useLimit) {} -LLVM_ATTRIBUTE_USED void AccessPathWithBase::dump() const { - print(llvm::dbgs()); + bool visitUse(Operand *use, AccessUseType useTy) { + if (uses.size() == useLimit) { + return false; + } + uses.push_back(use); + return true; + } +}; + +bool AccessPath::collectUses(SmallVectorImpl &uses, + AccessUseType useTy, SILFunction *function, + unsigned useLimit) const { + CollectAccessPathUses collector(uses, useTy, useLimit); + return visitAccessPathUses(collector, *this, function); } //===----------------------------------------------------------------------===// @@ -1566,7 +1678,7 @@ SILBasicBlock::iterator swift::removeBeginAccess(BeginAccessInst *beginAccess) { } //===----------------------------------------------------------------------===// -// Verification +// MARK: Verification //===----------------------------------------------------------------------===// // Helper for visitApplyAccesses that visits address-type call arguments, diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 443433584ca2f..fb2e6f0952347 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -603,7 +603,7 @@ SILValue UnenforcedAccess::beginAccess(SILGenFunction &SGF, SILLocation loc, if (!SGF.getOptions().VerifyExclusivity) return address; - const AccessedStorage &storage = findAccessedStorage(address); + auto storage = AccessedStorage::compute(address); // Unsafe access may have invalid storage (e.g. a RawPointer). if (storage && !isPossibleFormalAccessBase(storage, &SGF.F)) return address; diff --git a/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp b/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp index 90caebc1b3310..64a80796a3d19 100644 --- a/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp @@ -256,8 +256,7 @@ transformCalleeStorage(const StorageAccessInfo &storage, SILValue argVal = getCallerArg(fullApply, storage.getParamIndex()); if (argVal) { // Remap the argument source value and inherit the old storage info. - auto calleeStorage = findAccessedStorage(argVal); - if (calleeStorage) + if (auto calleeStorage = AccessedStorage::compute(argVal)) return StorageAccessInfo(calleeStorage, storage); } // If the argument can't be transformed, demote it to an unidentified @@ -265,7 +264,7 @@ transformCalleeStorage(const StorageAccessInfo &storage, // // This is an untested bailout. It is only reachable if the call graph // contains an edge that getCallerArg is unable to analyze OR if - // findAccessedStorage returns an invalid SILValue, which won't + // AccessedStorage::compute returns an invalid SILValue, which won't // pass SIL verification. // // FIXME: In case argVal is invalid, support Unidentified access for invalid @@ -301,8 +300,7 @@ void AccessedStorageResult::visitBeginAccess(B *beginAccess) { if (beginAccess->getEnforcement() != SILAccessEnforcement::Dynamic) return; - const AccessedStorage &storage = - findAccessedStorage(beginAccess->getSource()); + auto storage = AccessedStorage::compute(beginAccess->getSource()); if (storage.getKind() == AccessedStorage::Unidentified) { // This also catches invalid storage. diff --git a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp index 44c40297e4b2c..6e47743ba3310 100644 --- a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp @@ -334,7 +334,7 @@ static bool isAccessedAddressTBAASafe(SILValue V) { if (!V->getType().isAddress()) return false; - SILValue accessedAddress = getAccessAddress(V); + SILValue accessedAddress = getTypedAccessAddress(V); if (isa(accessedAddress)) return true; diff --git a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp index 376c9a220313d..4c810fed69b0a 100644 --- a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp +++ b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp @@ -84,7 +84,8 @@ class MemoryBehaviorVisitor /// If 'V' is an address, then the returned value is also an address. SILValue getValueAddress() { if (!cachedValueAddress) { - cachedValueAddress = V->getType().isAddress() ? getAccessAddress(V) : V; + cachedValueAddress = + V->getType().isAddress() ? getTypedAccessAddress(V) : V; } return cachedValueAddress; } diff --git a/lib/SILOptimizer/LoopTransforms/LICM.cpp b/lib/SILOptimizer/LoopTransforms/LICM.cpp index 6eb5e30a7f988..bf489fe892e13 100644 --- a/lib/SILOptimizer/LoopTransforms/LICM.cpp +++ b/lib/SILOptimizer/LoopTransforms/LICM.cpp @@ -700,18 +700,18 @@ static bool analyzeBeginAccess(BeginAccessInst *BI, InstSet &SideEffectInsts, AccessedStorageAnalysis *ASA, DominanceInfo *DT) { - const AccessedStorage &storage = findAccessedStorage(BI->getSource()); + auto storage = AccessedStorage::compute(BI->getSource()); if (!storage) { return false; } - auto BIAccessedStorageNonNested = findAccessedStorage(BI); + auto BIAccessedStorageNonNested = AccessedStorage::compute(BI); auto safeBeginPred = [&](BeginAccessInst *OtherBI) { if (BI == OtherBI) { return true; } return BIAccessedStorageNonNested.isDistinctFrom( - findAccessedStorage(OtherBI)); + AccessedStorage::compute(OtherBI)); }; if (!std::all_of(BeginAccesses.begin(), BeginAccesses.end(), safeBeginPred)) diff --git a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp index 674036973164d..f9e68af362c47 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp @@ -711,7 +711,8 @@ checkAccessSummary(ApplySite Apply, AccessState &State, // A valid AccessedStorage should always be found because Unsafe accesses // are not tracked by AccessSummaryAnalysis. - const AccessedStorage &Storage = identifyCapturedStorage(Argument); + auto Storage = AccessedStorage::computeInScope(Argument); + assert(Storage && "captured address must have valid storage"); auto AccessIt = State.Accesses->find(Storage); // Are there any accesses in progress at the time of the call? @@ -745,7 +746,8 @@ static void checkCaptureAccess(ApplySite Apply, AccessState &State) { // A valid AccessedStorage should always be found because Unsafe accesses // are not tracked by AccessSummaryAnalysis. - const AccessedStorage &Storage = identifyCapturedStorage(argOper.get()); + auto Storage = AccessedStorage::computeInScope(argOper.get()); + assert(Storage && "captured address must have valid storage"); // Are there any accesses in progress at the time of the call? auto AccessIt = State.Accesses->find(Storage); @@ -964,7 +966,7 @@ static void checkStaticExclusivity(SILFunction &Fn, PostOrderFunctionInfo *PO, // Check that the given address-type operand is guarded by begin/end access // markers. static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) { - SILValue accessBegin = getAccessBegin(memOper->get()); + SILValue accessBegin = getAccessScope(memOper->get()); SILInstruction *memInst = memOper->getUser(); auto error = [accessBegin, memInst]() { @@ -1017,13 +1019,12 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) { return; } - const AccessedStorage &storage = findAccessedStorage(accessBegin); - // findAccessedStorage may return an invalid storage object if the address - // producer is not recognized by its allowlist. For the purpose of - // verification, we assume that this can only happen for local - // initialization, not a formal memory access. The strength of - // verification rests on the completeness of the opcode list inside - // findAccessedStorage. + auto storage = AccessedStorage::compute(accessBegin); + // AccessedStorage::compute may return an invalid storage object if the + // address producer is not recognized by its allowlist. For the purpose of + // verification, we assume that this can only happen for local initialization, + // not a formal memory access. The strength of verification rests on the + // completeness of the opcode list inside AccessedStorage::compute. // // For the purpose of verification, an unidentified access is // unenforced. These occur in cases like global addressors and local buffers diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementDom.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementDom.cpp index 06fefeeac81c9..dd20d41da8e58 100644 --- a/lib/SILOptimizer/Transforms/AccessEnforcementDom.cpp +++ b/lib/SILOptimizer/Transforms/AccessEnforcementDom.cpp @@ -251,7 +251,7 @@ void DominatedAccessAnalysis::analyzeAccess(BeginAccessInst *BAI, // Only track dynamic access in the result. Static accesses still need to be // tracked by data flow, but they can't be optimized as "dominating". if (BAI->getEnforcement() == SILAccessEnforcement::Dynamic) { - AccessedStorage storage = findAccessedStorage(BAI->getSource()); + auto storage = AccessedStorage::compute(BAI->getSource()); // Copy the AccessStorage into DomAccessedStorage. All pass-specific bits // are initialized to zero. domStorage = DomAccessedStorage(storage); diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp index 88e4ed0588e96..c0011d148b2e3 100644 --- a/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp +++ b/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp @@ -566,7 +566,7 @@ bool AccessConflictAndMergeAnalysis::identifyBeginAccesses() { // now, since this optimization runs at the end of the pipeline, we // gracefully ignore unrecognized source address patterns, which show up // here as an invalid `storage` value. - AccessedStorage storage = findAccessedStorage(beginAccess->getSource()); + auto storage = AccessedStorage::compute(beginAccess->getSource()); auto iterAndInserted = storageSet.insert(storage); diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp index 36c82f0f676cc..9a16f7e22dac5 100644 --- a/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp +++ b/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp @@ -169,13 +169,13 @@ void GlobalAccessRemoval::perform() { void GlobalAccessRemoval::visitInstruction(SILInstruction *I) { if (auto *BAI = dyn_cast(I)) { - AccessedStorage storage = findAccessedStorage(BAI->getSource()); + auto storage = AccessedStorage::compute(BAI->getSource()); const VarDecl *decl = getDisjointAccessLocation(storage); recordAccess(BAI, decl, storage.getKind(), BAI->hasNoNestedConflict()); return; } if (auto *BUAI = dyn_cast(I)) { - AccessedStorage storage = findAccessedStorage(BUAI->getSource()); + auto storage = AccessedStorage::compute(BUAI->getSource()); const VarDecl *decl = getDisjointAccessLocation(storage); recordAccess(BUAI, decl, storage.getKind(), BUAI->hasNoNestedConflict()); return; diff --git a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp index 077b851c167ac..435751fcf20e2 100644 --- a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp +++ b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp @@ -447,7 +447,7 @@ bool SILPerformanceInliner::isProfitableToInline( // The access is dynamic and has no nested conflict // See if the storage location is considered by // access enforcement optimizations - AccessedStorage storage = findAccessedStorage(BAI->getSource()); + auto storage = AccessedStorage::compute(BAI->getSource()); if (BAI->hasNoNestedConflict() && (storage.isFormalAccessBase())) { BlockW.updateBenefit(ExclusivityBenefitWeight, ExclusivityBenefitBase); diff --git a/lib/SILOptimizer/UtilityPasses/AccessPathVerification.cpp b/lib/SILOptimizer/UtilityPasses/AccessPathVerification.cpp index 261115d957ab7..b575dc2bd6c16 100644 --- a/lib/SILOptimizer/UtilityPasses/AccessPathVerification.cpp +++ b/lib/SILOptimizer/UtilityPasses/AccessPathVerification.cpp @@ -47,8 +47,7 @@ class AccessPathVerification : public SILModuleTransform { public: void verifyAccessPath(Operand *operand) { - auto pathAndBase = AccessPathWithBase::compute(operand->get()); - auto accessPath = pathAndBase.accessPath; + auto accessPath = AccessPath::compute(operand->get()); if (!accessPath.isValid()) return; @@ -70,28 +69,19 @@ class AccessPathVerification : public SILModuleTransform { } // This is a new path, so map all its uses. assert(uses.empty()); - pathAndBase.collectUses(uses, /*collectContainingUses*/ false); + accessPath.collectUses(uses, AccessUseType::Exact, + operand->getParentFunction()); bool foundOperandUse = false; for (Operand *use : uses) { if (use == operand) { foundOperandUse = true; continue; } - // (live) subobject projections within an access will be mapped later as a - // separate path. - switch (use->getUser()->getKind()) { - default: - break; - case SILInstructionKind::StructElementAddrInst: - case SILInstructionKind::TupleElementAddrInst: - case SILInstructionKind::IndexAddrInst: - continue; - } auto iterAndInserted = useToPathMap.try_emplace(use, accessPath); if (!iterAndInserted.second) { llvm::errs() << "Address use: " << *operand->getUser() << " with path...\n"; - pathAndBase.dump(); + accessPath.dump(); llvm::errs() << " was not collected for: " << *use->getUser(); llvm::errs() << " with path...\n"; auto computedPath = iterAndInserted.first->second; diff --git a/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp b/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp index 31685d7a1c6bf..f1bbb45d640b0 100644 --- a/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp @@ -12,6 +12,7 @@ #define DEBUG_TYPE "sil-accessed-storage-dumper" #include "swift/SIL/MemAccessUtils.h" +#include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILValue.h" @@ -33,33 +34,49 @@ class AccessedStorageDumper : public SILModuleTransform { llvm::SmallVector uses; void dumpAccessedStorage(Operand *operand) { - findAccessedStorage(operand->get()).print(llvm::outs()); + SILFunction *function = operand->getParentFunction(); + // Print storage itself first, for comparison against AccessPath. They can + // differ in rare cases of unidentified storage with phis. + AccessedStorage::compute(operand->get()).print(llvm::outs()); + // Now print the access path and base. auto pathAndBase = AccessPathWithBase::compute(operand->get()); pathAndBase.print(llvm::outs()); - - if (!pathAndBase.accessPath.isValid() || !EnableDumpUses) + // If enable-accessed-storage-dump-uses is set, dump all types of uses. + auto accessPath = pathAndBase.accessPath; + if (!accessPath.isValid() || !EnableDumpUses) return; uses.clear(); - pathAndBase.collectUses(uses, /*collectContainingUses*/ false); + accessPath.collectUses(uses, AccessUseType::Exact, function); llvm::outs() << "Exact Uses {\n"; for (auto *useOperand : uses) { llvm::outs() << *useOperand->getUser() << " "; - auto usePathAndBase = AccessPathWithBase::compute(useOperand->get()); - usePathAndBase.accessPath.printPath(llvm::outs()); - assert(pathAndBase.accessPath.contains(usePathAndBase.accessPath) + auto usePath = AccessPath::compute(useOperand->get()); + usePath.printPath(llvm::outs()); + assert(accessPath == usePath + && "access path does not match use access path"); + } + llvm::outs() << "}\n"; + uses.clear(); + accessPath.collectUses(uses, AccessUseType::Inner, function); + llvm::outs() << "Inner Uses {\n"; + for (auto *useOperand : uses) { + llvm::outs() << *useOperand->getUser() << " "; + auto usePath = AccessPath::compute(useOperand->get()); + usePath.printPath(llvm::outs()); + assert(accessPath.contains(usePath) && "access path does not contain use access path"); } llvm::outs() << "}\n"; uses.clear(); - pathAndBase.collectUses(uses, /*collectContainingUses*/ true); + accessPath.collectUses(uses, AccessUseType::Overlapping, function); llvm::outs() << "Overlapping Uses {\n"; for (auto *useOperand : uses) { llvm::outs() << *useOperand->getUser() << " "; - auto usePathAndBase = AccessPathWithBase::compute(useOperand->get()); - usePathAndBase.accessPath.printPath(llvm::outs()); - assert(pathAndBase.accessPath.mayOverlap(usePathAndBase.accessPath) - && "access path does not contain use access path"); + auto usePath = AccessPath::compute(useOperand->get()); + usePath.printPath(llvm::outs()); + assert(accessPath.mayOverlap(usePath) + && "access path does not overlap with use access path"); } llvm::outs() << "}\n"; } @@ -71,6 +88,7 @@ class AccessedStorageDumper : public SILModuleTransform { llvm::outs() << "\n"; continue; } + PrettyStackTraceSILFunction functionDumper("...", &fn); for (auto &bb : fn) { for (auto &inst : bb) { if (inst.mayReadOrWriteMemory()) { diff --git a/test/SILOptimizer/accesspath_uses.sil b/test/SILOptimizer/accesspath_uses.sil index 883f68314e1d1..73bf391e593c9 100644 --- a/test/SILOptimizer/accesspath_uses.sil +++ b/test/SILOptimizer/accesspath_uses.sil @@ -14,6 +14,55 @@ struct MyStruct { @_hasStorage @_hasInitialValue var j: Int64 { get set } } +// CHECK-LABEL: @testInnerUse +// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct +// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK: Inner Uses { +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct +// CHECK-NEXT: Path: () +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK-NEXT: Path: (#0) +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct +// CHECK-NEXT: Path: () +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK-NEXT: Path: (#0) +// CHECK: } +// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Path: (#0) +// CHECK: Exact Uses { +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK-NEXT: Path: (#0) +// CHECK-NEXT: } +// CHECK: Inner Uses { +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK-NEXT: Path: (#0) +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct +// CHECK-NEXT: Path: () +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK-NEXT: Path: (#0) +// CHECK: } +sil [serialized] [ossa] @testInnerUse : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> () { +bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word): + %2 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*MyStruct + %3 = load [trivial] %2 : $*MyStruct + %4 = struct_element_addr %2 : $*MyStruct, #MyStruct.i + %5 = load [trivial] %4 : $*Int64 + %6 = tuple () + return %6 : $() +} + // unknown offset contains subobject indices // CHECK-LABEL: @testDynamicIndexWithSubObject // CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*Int64 @@ -22,6 +71,10 @@ struct MyStruct { // CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64 // CHECK-NEXT: Path: (#0) // CHECK-NEXT: } +// CHECK: Inner Uses { +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK-NEXT: Path: (#0) +// CHECK-NEXT: } // CHECK: Overlapping Uses { // CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64 // CHECK-NEXT: Path: (#0) @@ -32,9 +85,11 @@ struct MyStruct { // CHECK: Path: (@Unknown) // CHECK-NEXT: Exact Uses { // CHECK-NEXT: } +// CHECK-NEXT: Inner Uses { +// CHECK-NEXT: } // CHECK-NEXT: Overlapping Uses { -// CHECK-NEXT: %{{.*}} = struct_element_addr %{{.*}} : $*MyStruct, #MyStruct.i -// CHECK-NEXT: Path: () +// CHECK-NEXT: %{{.*}} = load [trivial] %3 : $*Int64 +// CHECK-NEXT: Path: (#0) // CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct // CHECK-NEXT: Path: (@Unknown) // CHECK-NEXT: } @@ -49,28 +104,69 @@ bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word): return %7 : $() } +// The index load should be reported as an overlapping uses, not an +// exact or inner use. +// CHECK: ###For MemOp: %3 = load [trivial] %2 : $*MyStruct +// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Base: %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK-NEXT: Inner Uses { +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK-NEXT: Overlapping Uses { +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct +// CHECK-NEXT: Path: () +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK-NEXT: Path: INVALID +// CHECK-NEXT: } +// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*Int64 +// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer +// CHECK: INVALID +sil [serialized] [ossa] @testDynamicIndexInsideSubObject : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> () { +bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word): + %2 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*MyStruct + %3 = load [trivial] %2 : $*MyStruct + %4 = struct_element_addr %2 : $*MyStruct, #MyStruct.i + %5 = index_addr %4 : $*Int64, %1 : $Builtin.Word + %6 = load [trivial] %5 : $*Int64 + %7 = tuple () + return %7 : $() +} + // An unknown offset contains known offsets. // CHECK-LABEL: @testDynamicIndexWithStaticIndex -// CHECK: ###For MemOp: %5 = load [trivial] %4 : $*MyStruct +// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct // CHECK: Path: (@1) // CHECK: Exact Uses { -// CHECK-NEXT: %5 = load [trivial] %4 : $*MyStruct +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct +// CHECK-NEXT: Path: (@1) +// CHECK-NEXT: } +// CHECK: Inner Uses { +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct // CHECK-NEXT: Path: (@1) // CHECK-NEXT: } // CHECK: Overlapping Uses { -// CHECK-NEXT: %5 = load [trivial] %4 : $*MyStruct +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct // CHECK-NEXT: Path: (@1) -// CHECK-NEXT: %7 = load [trivial] %6 : $*MyStruct +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct // CHECK-NEXT: Path: (@Unknown) // CHECK-NEXT: } -// CHECK: ###For MemOp: %7 = load [trivial] %6 : $*MyStruct +// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct // CHECK: Path: (@Unknown) // CHECK: Exact Uses { // CHECK-NEXT: } +// CHECK: Inner Uses { +// CHECK-NEXT: } // CHECK: Overlapping Uses { -// CHECK-NEXT: %5 = load [trivial] %4 : $*MyStruct +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct // CHECK-NEXT: Path: (@1) -// CHECK-NEXT: %7 = load [trivial] %6 : $*MyStruct +// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct // CHECK-NEXT: Path: (@Unknown) // CHECK-NEXT: } sil [serialized] [ossa] @testDynamicIndexWithStaticIndex : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> () { @@ -93,7 +189,7 @@ bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word): // CHECK-NEXT: %3 = load [trivial] %2 : $*MyStruct // CHECK-NEXT: Path: () // CHECK-NEXT: } -// CHECK-NEXT: Overlapping Uses { +// CHECK: Overlapping Uses { // CHECK-NEXT: %3 = load [trivial] %2 : $*MyStruct // CHECK-NEXT: Path: () // CHECK-NEXT: %8 = load [trivial] %7 : $*MyStruct @@ -117,6 +213,8 @@ bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word): // CHECK: Path: (@Unknown) // CHECK: Exact Uses { // CHECK-NEXT: } +// CHECK: Inner Uses { +// CHECK-NEXT: } // CHECK: Overlapping Uses { // CHECK-NEXT: %3 = load [trivial] %2 : $*MyStruct // CHECK-NEXT: Path: () @@ -304,6 +402,13 @@ bb3(%15 : $Builtin.RawPointer): return %17 : $AnyObject } +enum IntTEnum { + indirect case int(Int) + case other(T) + + var getValue: Int {get } +} + // CHECK-LABEL: @testEnumUses // CHECK: ###For MemOp: copy_addr %0 to [initialization] %2 : $*IntTEnum // CHECK: Storage: Argument %0 = argument of bb0 : $*IntTEnum @@ -457,13 +562,6 @@ bb3(%15 : $Builtin.RawPointer): // CHECK: %8 = load [trivial] %7 : $*Int // CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum, #IntTEnum.other!enumelt // CHECK: } -enum IntTEnum { - indirect case int(Int) - case other(T) - - var getValue: Int {get } -} - sil hidden [ossa] @testEnumUses : $@convention(method) (@in_guaranteed IntTEnum) -> Int { bb0(%0 : $*IntTEnum): debug_value_addr %0 : $*IntTEnum, let, name "self", argno 1 @@ -530,3 +628,81 @@ bb0(%0 : @guaranteed $Storage): end_access %2 : $*UInt return %8 : $Int } + +// CHECK-LABEL: @testBeginAccessUses +// CHECK: ###For MemOp: copy_addr [take] %1 to [initialization] %{{.*}} : $*T +// CHECK: Storage: Argument %1 = argument of bb0 : $*T +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK-NEXT: copy_addr [take] %1 to [initialization] %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: copy_addr [take] %1 to [initialization] %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK: Storage: Stack %{{.*}} = alloc_stack $T, var, name "$value" +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK-NEXT: copy_addr [take] %1 to [initialization] %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: copy_addr %{{.*}} to [initialization] %0 : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: end_access %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: destroy_addr %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: dealloc_stack %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: copy_addr [take] %1 to [initialization] %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: copy_addr %{{.*}} to [initialization] %0 : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: end_access %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: destroy_addr %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: dealloc_stack %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK: ###For MemOp: copy_addr %{{.*}} to [initialization] %0 : $*T +// CHECK: Storage: Stack %{{.*}} = alloc_stack $T, var, name "$value" +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK-NEXT: copy_addr [take] %1 to [initialization] %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: copy_addr %{{.*}} to [initialization] %0 : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: end_access %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: destroy_addr %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: dealloc_stack %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: copy_addr [take] %1 to [initialization] %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: copy_addr %{{.*}} to [initialization] %0 : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: end_access %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: destroy_addr %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: dealloc_stack %{{.*}} : $*T +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +sil [ossa] @testBeginAccessUses : $@convention(thin) (@in T) -> @out T { +bb0(%0 : $*T, %1 : $*T): + %2 = alloc_stack $T, var, name "$value" + copy_addr [take] %1 to [initialization] %2 : $*T + %4 = begin_access [modify] [static] %2 : $*T + copy_addr %4 to [initialization] %0 : $*T + end_access %4 : $*T + destroy_addr %2 : $*T + dealloc_stack %2 : $*T + %10 = tuple () + return %10 : $() +} diff --git a/test/SILOptimizer/licm.sil b/test/SILOptimizer/licm.sil index 9ac8ae2492cba..dcc8871611f91 100644 --- a/test/SILOptimizer/licm.sil +++ b/test/SILOptimizer/licm.sil @@ -756,7 +756,7 @@ bb3: } // ----------------------------------------------------------------------------- -// Reduced test case from rdar !!! +// Reduced test case from rdar://61246061 // // Test miscompilation of BidirectionalCollection._distance with // combined load/store hoisting/sinking with mutiple loads from From b272dc5e1a8357c7d882027e6bf4a7136de265ad Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Fri, 25 Sep 2020 23:43:13 -0700 Subject: [PATCH 530/745] Cache 'isLet' within AccessedStorage. Compute 'isLet' from the VarDecl that is available when constructing AccessedStorage so we don't need to recover the VarDecl for the base later. This generally makes more sense and is more efficient, but it will be necessary when we look past class casts when finding the reference root. --- include/swift/SIL/MemAccessUtils.h | 24 ++++++++----- include/swift/SIL/SILInstruction.h | 8 ++--- lib/SIL/IR/SILInstructions.cpp | 22 ++++++++---- lib/SIL/Utils/MemAccessUtils.cpp | 36 +++++++++++-------- .../LoadBorrowInvalidationChecker.cpp | 4 +-- .../SemanticARC/LoadCopyToLoadBorrowOpt.cpp | 2 +- 6 files changed, 57 insertions(+), 39 deletions(-) diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index a1b302710acd1..ba9ee554cd0bf 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -303,11 +303,11 @@ class AccessedStorage { protected: // Checking the storage kind is far more common than other fields. Make sure // it can be byte load with no shift. - static const int ReservedKindBits = 8; + static const int ReservedKindBits = 7; static_assert(ReservedKindBits >= NumKindBits, "Too many storage kinds."); static const unsigned InvalidElementIndex = - (1 << (32 - ReservedKindBits)) - 1; + (1 << (32 - (ReservedKindBits + 1))) - 1; // Form a bitfield that is effectively a union over any pass-specific data // with the fields used within this class as a common prefix. @@ -327,9 +327,10 @@ class AccessedStorage { // elementIndex can overflow while gracefully degrading analysis. For now, // reserve an absurd number of bits at a nice alignment boundary, but this // can be reduced. - SWIFT_INLINE_BITFIELD_BASE(AccessedStorage, 32, kind - : ReservedKindBits, - elementIndex : 32 - ReservedKindBits); + SWIFT_INLINE_BITFIELD_BASE(AccessedStorage, 32, + kind : ReservedKindBits, + isLet : 1, + elementIndex : 32 - (ReservedKindBits + 1)); // Define bits for use in AccessedStorageAnalysis. Each identified storage // object is mapped to one instance of this subclass. @@ -512,7 +513,7 @@ class AccessedStorage { } /// Return true if the given access is on a 'let' lvalue. - bool isLetAccess(SILFunction *F) const; + bool isLetAccess() const { return Bits.AccessedStorage.isLet; } /// If this is a uniquely identified formal access, then it cannot /// alias with any other uniquely identified access to different storage. @@ -555,9 +556,12 @@ class AccessedStorage { /// Returns the ValueDecl for the underlying storage, if it can be /// determined. Otherwise returns null. /// - /// WARNING: This is not a constant-time operation. It is for diagnostics and - /// checking via the ValueDecl if we are processing a `let` variable. - const ValueDecl *getDecl() const; + /// If \p base is provided, then it must be the accessed base for this + /// storage, as passed to the AccessedStorage constructor. What \p base is + /// provided, this is guaranteed to return a valid decl for class properties; + /// otherwise it is only a best effort based on the type of the object root + /// *before* the object is cast to the final accessed reference type. + const ValueDecl *getDecl(SILValue base = SILValue()) const; /// Get all leaf uses of all address, pointer, or box values that have a this /// AccessedStorage in common. Return true if all uses were found before @@ -629,6 +633,8 @@ class AccessedStorage { // nested/argument access. return false; } + + void setLetAccess(SILValue base); }; } // end namespace swift diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index c336ed5ab5d8a..913cb273e92a2 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -5822,13 +5822,11 @@ class TupleElementAddrInst /// object, including properties declared in a superclass. unsigned getFieldIndex(NominalTypeDecl *decl, VarDecl *property); -/// Get the property for a struct or class by its unique index. +/// Get the property for a struct or class by its unique index, or nullptr if +/// the index does not match a property declared in this struct or class or +/// one its superclasses. /// /// Precondition: \p decl must be a non-resilient struct or class. -/// -/// Precondition: \p index must be the index of a stored property -/// (as returned by getFieldIndex()) which is declared -/// in \p decl, not in a superclass. VarDecl *getIndexedField(NominalTypeDecl *decl, unsigned index); /// A common base for instructions that require a cached field index. diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index 5655e95219b8a..a752947b2f85f 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1345,15 +1345,23 @@ unsigned swift::getFieldIndex(NominalTypeDecl *decl, VarDecl *field) { /// Get the property for a struct or class by its unique index. VarDecl *swift::getIndexedField(NominalTypeDecl *decl, unsigned index) { - if (auto *classDecl = dyn_cast(decl)) { - for (auto *superDecl = classDecl->getSuperclassDecl(); superDecl != nullptr; - superDecl = superDecl->getSuperclassDecl()) { - assert(index >= superDecl->getStoredProperties().size() - && "field index cannot refer to a superclass field"); - index -= superDecl->getStoredProperties().size(); + if (auto *structDecl = dyn_cast(decl)) { + return structDecl->getStoredProperties()[index]; + } + auto *classDecl = cast(decl); + SmallVector superclasses; + for (auto *superDecl = classDecl; superDecl != nullptr; + superDecl = superDecl->getSuperclassDecl()) { + superclasses.push_back(superDecl); + } + std::reverse(superclasses.begin(), superclasses.end()); + for (auto *superDecl : superclasses) { + if (index < superDecl->getStoredProperties().size()) { + return superDecl->getStoredProperties()[index]; } + index -= superDecl->getStoredProperties().size(); } - return decl->getStoredProperties()[index]; + return nullptr; } unsigned FieldIndexCacheBase::cacheFieldIndex() { diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index 3673f095b6790..e94accc2585d4 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -452,9 +452,9 @@ SILGlobalVariable *getReferencedGlobal(SILInstruction *inst) { constexpr unsigned AccessedStorage::TailIndex; AccessedStorage::AccessedStorage(SILValue base, Kind kind) { + // For kind==Unidentified, base may be an invalid empty or tombstone value. assert(base && "invalid storage base"); initKind(kind); - switch (kind) { case Box: assert(isa(base)); @@ -505,6 +505,7 @@ AccessedStorage::AccessedStorage(SILValue base, Kind kind) { break; } } + setLetAccess(base); } void AccessedStorage::visitRoots( @@ -528,21 +529,24 @@ void AccessedStorage::visitRoots( } } -// Return true if the given access is on a 'let' lvalue. -bool AccessedStorage::isLetAccess(SILFunction *F) const { - if (auto *decl = dyn_cast_or_null(getDecl())) - return decl->isLet(); - +// Set 'isLet' to true if this storage can be determined to be a 'let' variable. +// +// \p base must be the access base for this storage, as passed to the +// AccessedStorage constructor. +void AccessedStorage::setLetAccess(SILValue base) { // It's unclear whether a global will ever be missing it's varDecl, but // technically we only preserve it for debug info. So if we don't have a decl, // check the flag on SILGlobalVariable, which is guaranteed valid, - if (getKind() == AccessedStorage::Global) - return getGlobal()->isLet(); - - return false; + if (getKind() == AccessedStorage::Global) { + Bits.AccessedStorage.isLet = getGlobal()->isLet(); + return; + } + if (auto *decl = dyn_cast_or_null(getDecl(base))) { + Bits.AccessedStorage.isLet = decl->isLet(); + } } -const ValueDecl *AccessedStorage::getDecl() const { +const ValueDecl *AccessedStorage::getDecl(SILValue base) const { switch (getKind()) { case Box: return cast(value)->getLoc().getAsASTNode(); @@ -555,7 +559,7 @@ const ValueDecl *AccessedStorage::getDecl() const { case Class: { auto *decl = getObject()->getType().getNominalOrBoundGenericNominal(); - return getIndexedField(decl, getPropertyIndex()); + return decl ? getIndexedField(decl, getPropertyIndex()) : nullptr; } case Tail: return nullptr; @@ -621,8 +625,10 @@ void AccessedStorage::print(raw_ostream &os) const { break; case Class: os << getObject(); - os << " Field: "; - getDecl()->print(os); + if (auto *decl = getDecl()) { + os << " Field: "; + decl->print(os); + } os << " Index: " << getPropertyIndex() << "\n"; break; case Tail: @@ -1655,7 +1661,7 @@ bool swift::isPossibleFormalAccessBase(const AccessedStorage &storage, // Additional checks that apply to anything that may fall through. // Immutable values are only accessed for initialization. - if (storage.isLetAccess(F)) + if (storage.isLetAccess()) return false; return true; diff --git a/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp b/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp index b5aea5103bc67..fe549173fffdf 100644 --- a/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp +++ b/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp @@ -466,9 +466,9 @@ bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated( } // If we have a let address, then we are already done. - if (storage.isLetAccess(lbi->getFunction())) + if (storage.isLetAccess()) { return true; - + } // At this point, we know that we /may/ have writes. Now we go through various // cases to try and exhaustively identify if those writes overlap with our // load_borrow. diff --git a/lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp b/lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp index 38d6304030741..71323e34eac8f 100644 --- a/lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp +++ b/lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp @@ -193,7 +193,7 @@ class StorageGuaranteesLoadVisitor void visitGlobalAccess(SILValue global) { return answer( - !AccessedStorage(global, AccessedStorage::Global).isLetAccess(&ctx.fn)); + !AccessedStorage(global, AccessedStorage::Global).isLetAccess()); } void visitClassAccess(RefElementAddrInst *field) { From f6b32aedcd0afb9962645d1ae7a8b33cf35c5998 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Sun, 27 Sep 2020 22:16:28 -0700 Subject: [PATCH 531/745] Add AccessedStorageWithBase to conviently recover the base's VarDecl --- include/swift/SIL/MemAccessUtils.h | 24 +++++++++++- lib/SIL/Utils/MemAccessUtils.cpp | 18 +++++++-- .../LoadBorrowInvalidationChecker.cpp | 4 +- .../Transforms/AccessEnforcementWMO.cpp | 39 ++++++++++--------- 4 files changed, 59 insertions(+), 26 deletions(-) diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index ba9ee554cd0bf..54f9ce67f3fdb 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -507,7 +507,7 @@ class AccessedStorage { llvm_unreachable("unhandled kind"); } - /// Return trye if the given access is guaranteed to be within a heap object. + /// Return true if the given access is guaranteed to be within a heap object. bool isObjectAccess() const { return getKind() == Class || getKind() == Tail; } @@ -696,6 +696,28 @@ template <> struct DenseMapInfo { namespace swift { +/// For convenience, encapsulate and AccessedStorage value along with its +/// accessed base address. +struct AccessedStorageWithBase { + AccessedStorage storage; + // The base of the formal access. For class storage, it is the + // ref_element_addr. For global storage it is the global_addr or initializer + // apply. For other storage, it is the same as accessPath.getRoot(). + // + // Base may be invalid for global_addr -> address_to_pointer -> phi patterns. + // FIXME: add a structural requirement to SIL so base is always valid in OSSA. + SILValue base; + + AccessedStorageWithBase(AccessedStorage storage, SILValue base) + : storage(storage), base(base) {} + + /// Identical to AccessedStorage::compute but preserves the access base. + static AccessedStorageWithBase compute(SILValue sourceAddress); + + /// Identical to AccessedStorage::computeInScope but preserves the base. + static AccessedStorageWithBase computeInScope(SILValue sourceAddress); +}; + /// Return an AccessedStorage value that identifies formally accessed storage /// for \p beginAccess, considering any outer access scope as having distinct /// storage from this access scope. This is useful for exclusivity checking diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index e94accc2585d4..95770fdf049bc 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -714,16 +714,26 @@ class FindAccessedStorageVisitor } // end anonymous namespace -AccessedStorage AccessedStorage::compute(SILValue sourceAddress) { +AccessedStorageWithBase +AccessedStorageWithBase::compute(SILValue sourceAddress) { FindAccessedStorageVisitor visitor(NestedAccessType::IgnoreAccessBegin); visitor.findStorage(sourceAddress); - return visitor.getStorage(); + return {visitor.getStorage(), visitor.getBase()}; } -AccessedStorage AccessedStorage::computeInScope(SILValue sourceAddress) { +AccessedStorageWithBase +AccessedStorageWithBase::computeInScope(SILValue sourceAddress) { FindAccessedStorageVisitor visitor(NestedAccessType::StopAtAccessBegin); visitor.findStorage(sourceAddress); - return visitor.getStorage(); + return {visitor.getStorage(), visitor.getBase()}; +} + +AccessedStorage AccessedStorage::compute(SILValue sourceAddress) { + return AccessedStorageWithBase::compute(sourceAddress).storage; +} + +AccessedStorage AccessedStorage::computeInScope(SILValue sourceAddress) { + return AccessedStorageWithBase::computeInScope(sourceAddress).storage; } //===----------------------------------------------------------------------===// diff --git a/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp b/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp index fe549173fffdf..6aea48c2fecac 100644 --- a/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp +++ b/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp @@ -454,11 +454,11 @@ bool LoadBorrowNeverInvalidatedAnalysis:: bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated( LoadBorrowInst *lbi) { - SILValue address = getAccessBegin(lbi->getOperand()); + SILValue address = getAccessScope(lbi->getOperand()); if (!address) return false; - auto storage = findAccessedStorage(address); + auto storage = AccessedStorage::compute(address); // If we couldn't find an access storage, return that we are assumed to write. if (!storage) { llvm::errs() << "Couldn't compute access storage?!\n"; diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp index 9a16f7e22dac5..e8db5bc90fc90 100644 --- a/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp +++ b/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp @@ -67,22 +67,21 @@ using llvm::DenseMap; using llvm::SmallDenseSet; // Get the VarDecl that represents the DisjointAccessLocation for the given -// AccessedStorage. Returns nullptr for any storage that can't be partitioned -// into a disjoint location. +// storage and access base. Returns nullptr for any storage that can't be +// partitioned into a disjoint location. // -// identifyFormalAccess may only return Unidentified storage for a global -// variable access if the global is defined in a different module. -// -// WARNING: Retrieving VarDecl for Class access is not constant time. -const VarDecl *getDisjointAccessLocation(const AccessedStorage &storage) { +// Global storage is expected to be disjoint because identifyFormalAccess may +// only return Unidentified storage for a global variable access if the global +// is defined in a different module. +const VarDecl * +getDisjointAccessLocation(AccessedStorageWithBase storageAndBase) { + auto storage = storageAndBase.storage; switch (storage.getKind()) { case AccessedStorage::Global: - // A global variable may return a null decl. These variables are - // implementation details that aren't formally accessed. - return storage.getGlobal()->getDecl(); - case AccessedStorage::Class: { - return cast(storage.getDecl()); - } + case AccessedStorage::Class: + // Class and Globals are always a VarDecl, but the global decl may have a + // null value for global_addr -> phi. + return cast_or_null(storage.getDecl(storageAndBase.base)); case AccessedStorage::Box: case AccessedStorage::Stack: case AccessedStorage::Tail: @@ -169,15 +168,17 @@ void GlobalAccessRemoval::perform() { void GlobalAccessRemoval::visitInstruction(SILInstruction *I) { if (auto *BAI = dyn_cast(I)) { - auto storage = AccessedStorage::compute(BAI->getSource()); - const VarDecl *decl = getDisjointAccessLocation(storage); - recordAccess(BAI, decl, storage.getKind(), BAI->hasNoNestedConflict()); + auto storageAndBase = AccessedStorageWithBase::compute(BAI->getSource()); + const VarDecl *decl = getDisjointAccessLocation(storageAndBase); + recordAccess(BAI, decl, storageAndBase.storage.getKind(), + BAI->hasNoNestedConflict()); return; } if (auto *BUAI = dyn_cast(I)) { - auto storage = AccessedStorage::compute(BUAI->getSource()); - const VarDecl *decl = getDisjointAccessLocation(storage); - recordAccess(BUAI, decl, storage.getKind(), BUAI->hasNoNestedConflict()); + auto storageAndBase = AccessedStorageWithBase::compute(BUAI->getSource()); + const VarDecl *decl = getDisjointAccessLocation(storageAndBase); + recordAccess(BUAI, decl, storageAndBase.storage.getKind(), + BUAI->hasNoNestedConflict()); return; } if (auto *KPI = dyn_cast(I)) { From 53eebddbb2f63d3f141667ee4195d3e91478a179 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Wed, 14 Oct 2020 00:09:02 -0700 Subject: [PATCH 532/745] Temporarily disable the load-borrow checker. A rewrite is ready and will be merged ASAP. --- lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp b/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp index 6aea48c2fecac..149c0e5d2aaa3 100644 --- a/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp +++ b/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp @@ -454,6 +454,9 @@ bool LoadBorrowNeverInvalidatedAnalysis:: bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated( LoadBorrowInst *lbi) { + // FIXME: To be reenabled separately in a follow-on commit. + return true; + SILValue address = getAccessScope(lbi->getOperand()); if (!address) return false; From 5e0c8f9b50d5d7b40f2fa1db19363ae1db6b1b4d Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Fri, 16 Oct 2020 13:14:15 -0700 Subject: [PATCH 533/745] The verifier should only output to llvm::errs() --- .../UtilityPasses/AccessPathVerification.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/SILOptimizer/UtilityPasses/AccessPathVerification.cpp b/lib/SILOptimizer/UtilityPasses/AccessPathVerification.cpp index b575dc2bd6c16..27c9ad9e61851 100644 --- a/lib/SILOptimizer/UtilityPasses/AccessPathVerification.cpp +++ b/lib/SILOptimizer/UtilityPasses/AccessPathVerification.cpp @@ -59,10 +59,10 @@ class AccessPathVerification : public SILModuleTransform { if (collectedFromPath != accessPath) { llvm::errs() << "Address use: " << *operand->getUser() << " collected from path\n "; - collectedFromPath.dump(); + collectedFromPath.print(llvm::errs()); llvm::errs() << " has different path\n "; - accessPath.dump(); - operand->getUser()->getFunction()->dump(); + accessPath.print(llvm::errs()); + operand->getUser()->getFunction()->print(llvm::errs()); assert(false && "computed path does not match collected path"); } return; @@ -81,19 +81,19 @@ class AccessPathVerification : public SILModuleTransform { if (!iterAndInserted.second) { llvm::errs() << "Address use: " << *operand->getUser() << " with path...\n"; - accessPath.dump(); + accessPath.print(llvm::errs()); llvm::errs() << " was not collected for: " << *use->getUser(); llvm::errs() << " with path...\n"; auto computedPath = iterAndInserted.first->second; - computedPath.dump(); - use->getUser()->getFunction()->dump(); + computedPath.print(llvm::errs()); + use->getUser()->getFunction()->print(llvm::errs()); assert(false && "missing collected use"); } } if (!foundOperandUse && !accessPath.hasUnknownOffset()) { llvm::errs() << "Address use: " << *operand->getUser() << " is not a use of path\n "; - accessPath.dump(); + accessPath.print(llvm::errs()); assert(false && "not a user of its own computed path "); } uses.clear(); From 5d6cf5cd96deab6f78263a09a5dd9e94cb63fe0b Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 16 Oct 2020 18:53:04 -0400 Subject: [PATCH 534/745] Sema: Fix failure to diagnose throwing expressions inside string interpolations We need to preserve the DiagnoseErrorOnTry bit stored in the Context when exiting a ContextScope. Otherwise, we fail to produce a diagnostic if the 'try' expression pushes an intertwining Context, such as a string interpolation or 'await'. Fixes , . --- lib/Sema/TypeCheckEffects.cpp | 16 ++++++++++++++++ test/expr/unary/async_await.swift | 9 +++++++++ test/stmt/errors.swift | 13 +++++++++++++ 3 files changed, 38 insertions(+) diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index a21c6ba1d2146..1e9a54ee2b872 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -878,6 +878,13 @@ class Context { HandlesErrors(handlesErrors), HandlesAsync(handlesAsync) { } public: + bool shouldDiagnoseErrorOnTry() const { + return DiagnoseErrorOnTry; + } + void setDiagnoseErrorOnTry(bool b) { + DiagnoseErrorOnTry = b; + } + /// Whether this is a function that rethrows. bool isRethrows() const { if (!HandlesErrors) @@ -1375,6 +1382,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker DeclContext *OldRethrowsDC; ContextFlags OldFlags; ThrowingKind OldMaxThrowingKind; + public: ContextScope(CheckEffectsCoverage &self, Optional newContext) : Self(self), OldContext(self.CurContext), @@ -1468,7 +1476,15 @@ class CheckEffectsCoverage : public EffectsHandlingWalker } ~ContextScope() { + // The "DiagnoseErrorOnTry" flag is a bit of mutable state + // in the Context itself, used to postpone diagnostic emission + // to a parent "try" expression. If something was diagnosed + // during this ContextScope, the flag may have been set, and + // we need to preseve its value when restoring the old Context. + bool DiagnoseErrorOnTry = Self.CurContext.shouldDiagnoseErrorOnTry(); Self.CurContext = OldContext; + Self.CurContext.setDiagnoseErrorOnTry(DiagnoseErrorOnTry); + Self.RethrowsDC = OldRethrowsDC; Self.Flags = OldFlags; Self.MaxThrowingKind = OldMaxThrowingKind; diff --git a/test/expr/unary/async_await.swift b/test/expr/unary/async_await.swift index 3dc31a35ab1c9..be9fcee0e1dad 100644 --- a/test/expr/unary/async_await.swift +++ b/test/expr/unary/async_await.swift @@ -133,3 +133,12 @@ func testStringInterpolation() async throws { _ = "Eventually produces \(await getInt())" _ = await "Eventually produces \(getInt())" } + +// Make sure try await works too +func invalidAsyncFunction() async { + _ = try await throwingAndAsync() // expected-error {{errors thrown from here are not handled}} +} + +func validAsyncFunction() async throws { + _ = try await throwingAndAsync() +} \ No newline at end of file diff --git a/test/stmt/errors.swift b/test/stmt/errors.swift index a8760bd1a64c3..c5db7c72500a5 100644 --- a/test/stmt/errors.swift +++ b/test/stmt/errors.swift @@ -251,3 +251,16 @@ func sr_11402_func2(_ x: SR_11402_P) { print(y) } } + +// https://bugs.swift.org/browse/SR-13654 + +func sr_13654_func() throws -> String {} + +func sr_13654_invalid_interpolation() { + _ = try "\(sr_13654_func())" // expected-error {{errors thrown from here are not handled}} + _ = "\(try sr_13654_func())" // expected-error {{errors thrown from here are not handled}} +} +func sr_13654_valid_interpolation() throws { + _ = try "\(sr_13654_func())" + _ = "\(try sr_13654_func())" +} \ No newline at end of file From f4b71b5077cd838ffe7caa8c1ae57653f033b766 Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Fri, 16 Oct 2020 19:25:32 -0400 Subject: [PATCH 535/745] [build] OpenBSD can install XCTest. --- utils/build-script-impl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index a9cc2c44c2a72..3ba943866969d 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -2889,7 +2889,7 @@ for host in "${ALL_HOSTS[@]}"; do fi case ${host} in - linux-*|freebsd-*|cygwin-*|haiku-*|android-*) ;; + linux-*|freebsd-*|openbsd-*|cygwin-*|haiku-*|android-*) ;; *) echo "error: --install-xctest is not supported on this platform" exit 1 From 3ce7899f9244ca48649892a8b9b40bdcbfb4fe4e Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Wed, 7 Oct 2020 13:53:24 -0700 Subject: [PATCH 536/745] Add check for Xcode version compatibility This adds a check validating the current Xcode version is supported for building the current sha. This makes us fail fast in the case that you've selected too new or too old of an Xcode version that might otherwise fail very late in the build process. You can also set SKIP_XCODE_VERSION_CHECK to anything to skip this. --- utils/build-script | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/utils/build-script b/utils/build-script index a9fdd92ec3c95..a7ee4ec095f25 100755 --- a/utils/build-script +++ b/utils/build-script @@ -53,6 +53,15 @@ from swift_build_support.swift_build_support.toolchain import host_toolchain # TODO: Remove this constant, it's really not helpful. HOME = os.environ.get("HOME", "/") +_SUPPORTED_XCODE_BUILDS = ( + "12A8169g", # 12.0b3 + "12A8179i", # 12.0b4 + "12A8189h", # 12.0b5 + "12A8189n", # 12.0b6 + "12A7208", # 12.0GM1 + "12A7209", # 12.0GM2 + "12A7300", # 12.0.1 +) # ----------------------------------------------------------------------------- # Helpers @@ -147,6 +156,22 @@ def print_xcodebuild_versions(file=sys.stdout): print(fmt.format(version=version, sdks=sdks), file=file) file.flush() +def validate_xcode_compatibility(): + if sys.platform != 'darwin': + return + + if os.getenv("SKIP_XCODE_VERSION_CHECK"): + print("note: skipping Xcode version check") + return + + version = shell.capture( + ['xcodebuild', '-version'], dry_run=False, echo=False).strip() + if not version.endswith(_SUPPORTED_XCODE_BUILDS): + raise SystemExit( + "error: using unsupported Xcode version:\n\n{}\n\nInstall one of: {}".format( + version, ", ".join(_SUPPORTED_XCODE_BUILDS) + ) + ) def tar(source, destination): """ @@ -1335,6 +1360,8 @@ def main(): "\' does not exist " + "(forgot to set $SWIFT_SOURCE_ROOT environment variable?)") + validate_xcode_compatibility() + # Determine if we are invoked in the preset mode and dispatch accordingly. if any([(opt.startswith("--preset") or opt == "--show-presets") for opt in sys.argv[1:]]): From 61113197fb071eb8d9e05c4ea36dc1f95249b855 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Wed, 7 Oct 2020 14:26:26 -0700 Subject: [PATCH 537/745] Improve messaging --- utils/build-script | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/utils/build-script b/utils/build-script index a7ee4ec095f25..d62945c5bb60e 100755 --- a/utils/build-script +++ b/utils/build-script @@ -53,15 +53,15 @@ from swift_build_support.swift_build_support.toolchain import host_toolchain # TODO: Remove this constant, it's really not helpful. HOME = os.environ.get("HOME", "/") -_SUPPORTED_XCODE_BUILDS = ( - "12A8169g", # 12.0b3 - "12A8179i", # 12.0b4 - "12A8189h", # 12.0b5 - "12A8189n", # 12.0b6 - "12A7208", # 12.0GM1 - "12A7209", # 12.0GM2 - "12A7300", # 12.0.1 -) +_SUPPORTED_XCODE_BUILDS = [ + ("12.0 beta 3", "12A8169g"), + ("12.0 beta 4", "12A8179i"), + ("12.0 beta 5", "12A8189h"), + ("12.0 beta 6", "12A8189n"), + ("12.0 GM 1", "12A7208"), + ("12.0 GM 2", "12A7209"), + ("12.0.1", "12A7300"), +] # ----------------------------------------------------------------------------- # Helpers @@ -166,10 +166,14 @@ def validate_xcode_compatibility(): version = shell.capture( ['xcodebuild', '-version'], dry_run=False, echo=False).strip() - if not version.endswith(_SUPPORTED_XCODE_BUILDS): + + valid_build_numbers = tuple(x[1] for x in _SUPPORTED_XCODE_BUILDS) + if not version.endswith(valid_build_numbers): + valid_versions_string = "\n".join( + "{} ({})".format(*x) for x in _SUPPORTED_XCODE_BUILDS) raise SystemExit( - "error: using unsupported Xcode version:\n\n{}\n\nInstall one of: {}".format( - version, ", ".join(_SUPPORTED_XCODE_BUILDS) + "error: using unsupported Xcode version:\n\n{}\n\nInstall one of:\n{}\n\nOr set 'SKIP_XCODE_VERSION_CHECK=1' in the environment".format( + version, valid_versions_string ) ) From 58924f0b065287761e0450b7ea580c23b28fcf4d Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Mon, 12 Oct 2020 15:57:38 -0700 Subject: [PATCH 538/745] Add comment --- utils/build-script | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/build-script b/utils/build-script index d62945c5bb60e..8a17576dfc66e 100755 --- a/utils/build-script +++ b/utils/build-script @@ -53,6 +53,8 @@ from swift_build_support.swift_build_support.toolchain import host_toolchain # TODO: Remove this constant, it's really not helpful. HOME = os.environ.get("HOME", "/") +# These versions are community sourced. At any given time only the Xcode +# version used by Swift CI is officially supported. See ci.swift.org _SUPPORTED_XCODE_BUILDS = [ ("12.0 beta 3", "12A8169g"), ("12.0 beta 4", "12A8179i"), From 15937f409196c81af47d8336645ee8cf7a489f93 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Fri, 16 Oct 2020 20:58:20 -0700 Subject: [PATCH 539/745] Update to 12.2b3 --- utils/build-script | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/utils/build-script b/utils/build-script index 8a17576dfc66e..d7b6720944ac3 100755 --- a/utils/build-script +++ b/utils/build-script @@ -56,13 +56,7 @@ HOME = os.environ.get("HOME", "/") # These versions are community sourced. At any given time only the Xcode # version used by Swift CI is officially supported. See ci.swift.org _SUPPORTED_XCODE_BUILDS = [ - ("12.0 beta 3", "12A8169g"), - ("12.0 beta 4", "12A8179i"), - ("12.0 beta 5", "12A8189h"), - ("12.0 beta 6", "12A8189n"), - ("12.0 GM 1", "12A7208"), - ("12.0 GM 2", "12A7209"), - ("12.0.1", "12A7300"), + ("12.2 beta 3", "12B5035g"), ] # ----------------------------------------------------------------------------- From e893ecfaf0a3ec463994fa2f1cf46ef75177a95c Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Sat, 17 Oct 2020 18:08:24 +0300 Subject: [PATCH 540/745] WinSDK: extract System.MCX submodule Currently this header gets included into `WinSDK.WinSock2` via `windows.h` --- stdlib/public/Platform/winsdk.modulemap | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/stdlib/public/Platform/winsdk.modulemap b/stdlib/public/Platform/winsdk.modulemap index a44f8de118874..f5f9b6793bfee 100644 --- a/stdlib/public/Platform/winsdk.modulemap +++ b/stdlib/public/Platform/winsdk.modulemap @@ -280,6 +280,11 @@ module WinSDK [system] { header "winioctl.h" export * } + + module MCX { + header "mcx.h" + export * + } } module OLE32 { From 40ef504f38bb6fd147f9e39cee16bc0795e27abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Fri, 16 Oct 2020 15:28:43 -0700 Subject: [PATCH 541/745] [Index] Don't report extensions with nothing to index in system modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The indexer was looking into “empty” extensions to public types, triggering computing their USR which could fail at deserializing the implementation-only imported types. As a solution, don’t index extensions with nothing to index in system modules. rdar://70225906 --- lib/Index/Index.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/Index/Index.cpp b/lib/Index/Index.cpp index e8e6fab61ddc9..8d4dba5536fd8 100644 --- a/lib/Index/Index.cpp +++ b/lib/Index/Index.cpp @@ -631,6 +631,22 @@ class IndexSwiftASTWalker : public SourceEntityWalker { return true; } + // Are there members or conformances in \c D that should be indexed? + bool shouldIndexMembers(ExtensionDecl *D) { + for (auto Member : D->getMembers()) + if (auto VD = dyn_cast(Member)) + if (shouldIndex(VD, /*IsRef=*/false)) + return true; + + for (auto Inherit : D->getInherited()) + if (auto T = Inherit.getType()) + if (T->getAnyNominal() && + shouldIndex(T->getAnyNominal(), /*IsRef=*/false)) + return true; + + return false; + } + /// Reports all implicit member value decl conformances that \p D introduces /// as implicit overrides at the source location of \p D, and returns the /// explicit ones so we can check against them later on when visiting them as @@ -1068,6 +1084,10 @@ bool IndexSwiftASTWalker::reportExtension(ExtensionDecl *D) { if (!shouldIndex(NTD, /*IsRef=*/false)) return true; + // Don't index "empty" extensions in imported modules. + if (IsModuleFile && !shouldIndexMembers(D)) + return true; + IndexSymbol Info; if (initIndexSymbol(D, NTD, Loc, Info)) return true; From b87fd1230b03bdf93edb3cdc859af983c8244e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Fri, 16 Oct 2020 16:58:31 -0700 Subject: [PATCH 542/745] [Index] Update test related to reporting extensions in system modules --- .../Indexing/Inputs/test_module.index.response | 11 ++++++++++- test/SourceKit/Indexing/Inputs/test_module.swift | 12 +++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/test/SourceKit/Indexing/Inputs/test_module.index.response b/test/SourceKit/Indexing/Inputs/test_module.index.response index 85b907997c761..64a05c94235c5 100644 --- a/test/SourceKit/Indexing/Inputs/test_module.index.response +++ b/test/SourceKit/Indexing/Inputs/test_module.index.response @@ -172,7 +172,16 @@ { key.kind: source.lang.swift.decl.extension.class, key.name: "C2", - key.usr: "s:e:s:11test_module2C2C6SECRETyyF" + key.usr: "s:e:s:11test_module2C2C10publicFuncyyF", + key.entities: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "publicFunc()", + key.usr: "s:11test_module2C2C10publicFuncyyF", + key.is_dynamic: 1, + key.effective_access: source.decl.effective_access.public + } + ] } ] } diff --git a/test/SourceKit/Indexing/Inputs/test_module.swift b/test/SourceKit/Indexing/Inputs/test_module.swift index f6327a737a8d3..fc5f23379d654 100644 --- a/test/SourceKit/Indexing/Inputs/test_module.swift +++ b/test/SourceKit/Indexing/Inputs/test_module.swift @@ -12,7 +12,7 @@ public class TwoInts { public class ComputedProperty { public var value : Int { get { - var result = 0 + let result = 0 return result } set(newVal) { @@ -33,6 +33,16 @@ public func globalFunc() {} private func SECRET() {} +extension C2 { + public func publicFunc() {} +} + +// Don't record extensions with nothing to index. extension C2 { internal func SECRET() {} + private func SECRET1() {} + fileprivate func SECRET2() {} } + +internal protocol InternalProto {} +extension C2: InternalProto {} From f730ff1d54a874a68c8e04690d651e0f2544fe0b Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sat, 17 Oct 2020 15:54:26 -0700 Subject: [PATCH 543/745] Disregard Overridden Candidates in Infinite Recursion Check Disregard candidates that have known override points because those points are possible targets for dynamic dispatch. This removes a class of false positives involving classes with known override points in the module, as is this case in many node-based data structures. rdar://70410948 --- .../Mandatory/DiagnoseInfiniteRecursion.cpp | 17 ++++++++++++++++- test/SILOptimizer/infinite_recursion.swift | 17 ++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp b/lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp index c8bac8b584ff5..cb9fbdd9792c2 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp @@ -52,8 +52,11 @@ static bool hasRecursiveCallInPath(SILBasicBlock &Block, if (FullApplySite FAI = FullApplySite::isa(&I)) { // Don't touch dynamic dispatch. - if (isa(FAI.getCallee())) + if (isa(FAI.getCallee()) || + isa(FAI.getCallee()) || + isa(FAI.getCallee())) { continue; + } auto &M = FAI.getModule(); if (auto *CMI = dyn_cast(FAI.getCallee())) { @@ -71,6 +74,18 @@ static bool hasRecursiveCallInPath(SILBasicBlock &Block, continue; } + if (!calleesAreStaticallyKnowable(FAI.getModule(), CMI->getMember())) { + continue; + } + + // The "statically knowable" check just means that we have all the + // callee candidates available for analysis. We still need to check + // if the current function has a known override point. + auto *method = CMI->getMember().getAbstractFunctionDecl(); + if (method->isOverridden()) { + continue; + } + auto *F = getTargetClassMethod(M, CD, CMI); if (F == Target) return true; diff --git a/test/SILOptimizer/infinite_recursion.swift b/test/SILOptimizer/infinite_recursion.swift index 3a0e03858967d..4950686e63b27 100644 --- a/test/SILOptimizer/infinite_recursion.swift +++ b/test/SILOptimizer/infinite_recursion.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify -// RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify +// RUN: %target-swift-frontend -emit-sil %s -o /dev/null -verify +// RUN: %target-swift-frontend -emit-sil %s -o /dev/null -verify func a() { // expected-warning {{all paths through this function will call itself}} a() @@ -123,7 +123,7 @@ class S { return a() } - func b() { // expected-warning {{all paths through this function will call itself}} + func b() { // No warning - has a known override. var i = 0 repeat { i += 1 @@ -171,3 +171,14 @@ func factorial(_ n : UInt) -> UInt { // expected-warning {{all paths through thi func tr(_ key: String) -> String { // expected-warning {{all paths through this function will call itself}} return tr(key) ?? key // expected-warning {{left side of nil coalescing operator '??' has non-optional type}} } + +class Node { + var parent: Node? + var rootNode: RootNode { + return parent!.rootNode // No warning - has an override. + } +} + +class RootNode: Node { + override var rootNode: RootNode { return self } +} From baa45a1167023841b03ffebca9db58b67d9c80e2 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sat, 17 Oct 2020 16:17:14 -0700 Subject: [PATCH 544/745] [Gardening] Pull Out Callee Variable --- .../Mandatory/DiagnoseInfiniteRecursion.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp b/lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp index cb9fbdd9792c2..5969a6c224ab7 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp @@ -52,14 +52,15 @@ static bool hasRecursiveCallInPath(SILBasicBlock &Block, if (FullApplySite FAI = FullApplySite::isa(&I)) { // Don't touch dynamic dispatch. - if (isa(FAI.getCallee()) || - isa(FAI.getCallee()) || - isa(FAI.getCallee())) { + const auto callee = FAI.getCallee(); + if (isa(callee) || + isa(callee) || + isa(callee)) { continue; } auto &M = FAI.getModule(); - if (auto *CMI = dyn_cast(FAI.getCallee())) { + if (auto *CMI = dyn_cast(callee)) { auto ClassType = CMI->getOperand()->getType().getASTType(); // FIXME: If we're not inside the module context of the method, @@ -93,7 +94,7 @@ static bool hasRecursiveCallInPath(SILBasicBlock &Block, continue; } - if (auto *WMI = dyn_cast(FAI.getCallee())) { + if (auto *WMI = dyn_cast(callee)) { SILFunction *F; SILWitnessTable *WT; From a4a1ff6442a7b380b380ab04c0ad5ef9ec441d54 Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Sun, 18 Oct 2020 15:14:09 +0300 Subject: [PATCH 545/745] WinSDK: prevent windows.h from hijacking imm.h Both `immdev.h` & `windows.h` include `imm.h`, and sometimes this header gets assigned to the module containing `windows.h` (currently `WinSDK.WinSock2`) instead of the Internationalization submodule --- stdlib/public/Platform/winsdk.modulemap | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/public/Platform/winsdk.modulemap b/stdlib/public/Platform/winsdk.modulemap index f5f9b6793bfee..f7c3e73c2083a 100644 --- a/stdlib/public/Platform/winsdk.modulemap +++ b/stdlib/public/Platform/winsdk.modulemap @@ -187,6 +187,7 @@ module WinSDK [system] { module IMM { header "immdev.h" + header "imm.h" export * link "Imm32.lib" From f6931c8905b9ee68a6e0711b6e6fa165fcef97f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Sat, 17 Oct 2020 15:17:12 -0700 Subject: [PATCH 546/745] [Index] Test records of extensions in sources and modules --- test/Index/Store/record-sourcefile.swift | 5 +- test/Index/Store/record-systemmodule.swift | 71 ++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 test/Index/Store/record-systemmodule.swift diff --git a/test/Index/Store/record-sourcefile.swift b/test/Index/Store/record-sourcefile.swift index e35cb8ba0c198..e206966b13f96 100644 --- a/test/Index/Store/record-sourcefile.swift +++ b/test/Index/Store/record-sourcefile.swift @@ -30,7 +30,7 @@ // CHECK: type-alias/Swift | TA | s:{{.*}} | | Def,RelChild - // CHECK: class/Swift | C1 | s:{{.*}} | | Def,Ref,RelBase,RelCont - // CHECK: instance-method/Swift | method() | s:{{.*}} | | Def,Ref,Call,Dyn,RelChild,RelRec,RelCall,RelCont - -// CHECK: class/Swift | C2 | s:{{.*}} | | Def - +// CHECK: class/Swift | C2 | s:{{.*}} | | Def,Ref - RelChild,RelBase // CHECK: instance-method/Swift | method() | s:{{.*}} | | Def,Dyn,RelChild,RelOver - // CHECK: function/Swift | takeC1(x:) | s:{{.*}} | | Def - // CHECK: instance-method(test)/Swift | testFoo() | s:{{.*}} | | Def,Dyn,RelChild - @@ -151,3 +151,6 @@ class MyTestCase: XCTestCase { // CHECK-NEXT: RelChild | s:4file10MyTestCaseC func testFoo() { test1() } } + +// CHECK: [[@LINE+1]]:11 | class/Swift | s:4file2C2C | Ref | rel: 0 +extension C2 {} diff --git a/test/Index/Store/record-systemmodule.swift b/test/Index/Store/record-systemmodule.swift new file mode 100644 index 0000000000000..7e509b264cb57 --- /dev/null +++ b/test/Index/Store/record-systemmodule.swift @@ -0,0 +1,71 @@ +// --- Prepare SDK (.swiftmodule). +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/SDK) +// RUN: mkdir -p %t/SDK/Frameworks/SomeModule.framework/Modules/SomeModule.swiftmodule +// RUN: %target-swift-frontend \ +// RUN: -emit-module \ +// RUN: -module-name SomeModule \ +// RUN: -o %t/SDK/Frameworks/SomeModule.framework/Modules/SomeModule.swiftmodule/%module-target-triple.swiftmodule \ +// RUN: -swift-version 5 \ +// RUN: -D SOME_MODULE \ +// RUN: %s + +#if SOME_MODULE + +public func someFunc() {} + +public class C2 {} + +extension C2 { + public func publicFunc() {} +} + +// Don't record extensions with nothing to index. +extension C2 {} + +extension C2 { + internal func SECRET() {} + private func SECRET1() {} + fileprivate func SECRET2() {} +} + +internal protocol SECRETProto {} +extension C2: SECRETProto {} + +// ----------------------------------------------------------------------------- +// Test-1 - '.swiftmodule' - Normal index-while-building. +// +// RUN: %empty-directory(%t/idx) +// RUN: %empty-directory(%t/modulecache) +// +// --- Built with indexing +// RUN: %target-swift-frontend \ +// RUN: -typecheck \ +// RUN: -index-system-modules \ +// RUN: -index-ignore-stdlib \ +// RUN: -index-store-path %t/idx \ +// RUN: -sdk %t/SDK \ +// RUN: -Fsystem %t/SDK/Frameworks \ +// RUN: -module-cache-path %t/modulecache \ +// RUN: -D CLIENT \ +// RUN: %s + +#elseif CLIENT + +import SomeModule +print(someFunc()) + +#endif + +// ----------------------------------------------------------------------------- +// --- Check the records. +// RUN: c-index-test core -print-record %t/idx | %FileCheck %s + +// CHECK: 0:0 | function/Swift | s:10SomeModule8someFuncyyF | Def | rel: 0 +// CHECK-NEXT: 0:0 | class/Swift | [[class_USR:s:10SomeModule2C2C]] | Def | rel: 0 +// CHECK-NEXT: 0:0 | class/Swift | [[class_USR]] | Ref,RelExt | rel: 1 +// CHECK-NEXT: RelExt | s:e:[[publicFunc_USR:s:10SomeModule2C2C10publicFuncyyF]] +// CHECK-NEXT: 0:0 | instance-method/Swift | [[publicFunc_USR]] | Def,Dyn,RelChild | rel: 1 +// CHECK-NEXT: RelChild | s:e:[[publicFunc_USR]] +// CHECK-NEXT: 0:0 | extension/ext-class/Swift | s:e:[[publicFunc_USR]] | Def | rel: 0 +// CHECK-NOT: SECRET From 87c177c4b6e22e2e7281328443d644326852f4a6 Mon Sep 17 00:00:00 2001 From: Patrick Pijnappel Date: Mon, 19 Oct 2020 10:51:18 +1100 Subject: [PATCH 547/745] [stdlib] Change misleading comment --- stdlib/public/core/Integers.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/stdlib/public/core/Integers.swift b/stdlib/public/core/Integers.swift index 1b7ba775989ff..1c07a8156619b 100644 --- a/stdlib/public/core/Integers.swift +++ b/stdlib/public/core/Integers.swift @@ -3010,9 +3010,7 @@ extension FixedWidthInteger { let minBitWidth = source.significandWidth let isExact = (minBitWidth <= exponent) let bitPattern = source.significandBitPattern - // `RawSignificand.bitWidth` is not available if `RawSignificand` does not - // conform to `FixedWidthInteger`; we can compute this value as follows if - // `source` is finite: + // Determine the number of meaningful bits in the significand bit pattern. let bitWidth = minBitWidth &+ bitPattern.trailingZeroBitCount let shift = exponent - Source.Exponent(bitWidth) // Use `Self.Magnitude` to prevent sign extension if `shift < 0`. From 21f4cf6a9c77a7999b91745b1ffedfd640c86c21 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Sun, 18 Oct 2020 17:42:21 -0700 Subject: [PATCH 548/745] [XFAIL] Disable validation-test/stdlib/CommandLine.swift (70423908) --- validation-test/stdlib/CommandLine.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/validation-test/stdlib/CommandLine.swift b/validation-test/stdlib/CommandLine.swift index a43defe135d37..a9a508732bcab 100644 --- a/validation-test/stdlib/CommandLine.swift +++ b/validation-test/stdlib/CommandLine.swift @@ -10,5 +10,7 @@ // REQUIRES: stress_test // UNSUPPORTED: single_threaded_runtime +// REQUIRES: rdar70423908 + // This file is an empty stub to call into the command line stress test which // houses `main`. From 8ecd884b626a98f7b84ececfc1cbe37dde8f5b59 Mon Sep 17 00:00:00 2001 From: Patrick Pijnappel Date: Mon, 19 Oct 2020 17:55:50 +1100 Subject: [PATCH 549/745] [stdlib] Expand integer conversion comment --- stdlib/public/core/Integers.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stdlib/public/core/Integers.swift b/stdlib/public/core/Integers.swift index 1c07a8156619b..cc5341b71692c 100644 --- a/stdlib/public/core/Integers.swift +++ b/stdlib/public/core/Integers.swift @@ -3010,7 +3010,10 @@ extension FixedWidthInteger { let minBitWidth = source.significandWidth let isExact = (minBitWidth <= exponent) let bitPattern = source.significandBitPattern - // Determine the number of meaningful bits in the significand bit pattern. + // Determine the actual number of fractional significand bits. + // `Source.significandBitCount` would not reflect the actual number of + // fractional significand bits if `Source` is not a fixed-width floating-point + // type; we can compute this value as follows if `source` is finite: let bitWidth = minBitWidth &+ bitPattern.trailingZeroBitCount let shift = exponent - Source.Exponent(bitWidth) // Use `Self.Magnitude` to prevent sign extension if `shift < 0`. From 4c0ea5512d0be520a212d1f8de641ff1b27f607b Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Mon, 19 Oct 2020 05:36:34 -0700 Subject: [PATCH 550/745] Test requires x86_64 --- test/SILOptimizer/specialize_opaque_result_types2.sil | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/SILOptimizer/specialize_opaque_result_types2.sil b/test/SILOptimizer/specialize_opaque_result_types2.sil index 43827bb12136c..d304e6b3410b9 100644 --- a/test/SILOptimizer/specialize_opaque_result_types2.sil +++ b/test/SILOptimizer/specialize_opaque_result_types2.sil @@ -2,6 +2,8 @@ // RUN: %target-swift-frontend -disable-availability-checking -primary-file %S/Inputs/specialize_opaque_result_types.swift -enable-library-evolution -module-name A -emit-sib -o %t/A.sib // RUN: %target-swift-frontend -emit-sil -primary-file %s -enable-library-evolution -O -module-name A %t/A.sib -o - | %FileCheck %s +// REQUIRES: CPU=x86_64 + sil_stage canonical import Builtin From 5d30503894e1fc460fe98ba604ad21381a87637a Mon Sep 17 00:00:00 2001 From: tbkka Date: Mon, 19 Oct 2020 06:44:57 -0700 Subject: [PATCH 551/745] When parsing floating-point from String, underflow to 0, overflow to infinity (#34339) Previously, overflow and underflow both caused this to return `nil`, which causes several problems: * It does not distinguish between a large but valid input and a malformed input. `Float("3.402824e+38")` is perfectly well-formed but returns nil * It differs from how the compiler handles literals. As a result, `Float(3.402824e+38)` is very different from `Float("3.402824e+38")` * It's inconsistent with Foundation Scanner() * It's inconsistent with other programming languages This is exactly the same as #25313 Fixes rdar://problem/36990878 --- stdlib/public/SwiftShims/RuntimeShims.h | 11 ++++------- stdlib/public/stubs/Stubs.cpp | 4 ---- test/stdlib/NumericParsing.swift.gyb | 16 +++++----------- 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/stdlib/public/SwiftShims/RuntimeShims.h b/stdlib/public/SwiftShims/RuntimeShims.h index 69fd8ee83036c..b5ffb04c81958 100644 --- a/stdlib/public/SwiftShims/RuntimeShims.h +++ b/stdlib/public/SwiftShims/RuntimeShims.h @@ -31,22 +31,19 @@ SWIFT_RUNTIME_STDLIB_API void *_swift_objCMirrorSummary(const void * nsObject); /// Call strtold_l with the C locale, swapping argument and return -/// types so we can operate on Float80. Return NULL on overflow. +/// types so we can operate on Float80. SWIFT_RUNTIME_STDLIB_API const char *_swift_stdlib_strtold_clocale(const char *nptr, void *outResult); /// Call strtod_l with the C locale, swapping argument and return -/// types so we can operate consistently on Float80. Return NULL on -/// overflow. +/// types so we can operate consistently on Float80. SWIFT_RUNTIME_STDLIB_API const char *_swift_stdlib_strtod_clocale(const char *nptr, double *outResult); /// Call strtof_l with the C locale, swapping argument and return -/// types so we can operate consistently on Float80. Return NULL on -/// overflow. +/// types so we can operate consistently on Float80. SWIFT_RUNTIME_STDLIB_API const char *_swift_stdlib_strtof_clocale(const char *nptr, float *outResult); /// Call strtof_l with the C locale, swapping argument and return -/// types so we can operate consistently on Float80. Return NULL on -/// overflow. +/// types so we can operate consistently on Float80. SWIFT_RUNTIME_STDLIB_API const char *_swift_stdlib_strtof16_clocale(const char *nptr, __fp16 *outResult); diff --git a/stdlib/public/stubs/Stubs.cpp b/stdlib/public/stubs/Stubs.cpp index 15338277fb1d2..7d54fd20543ad 100644 --- a/stdlib/public/stubs/Stubs.cpp +++ b/stdlib/public/stubs/Stubs.cpp @@ -452,10 +452,6 @@ static const char *_swift_stdlib_strtoX_clocale_impl( _swift_set_errno(0); const auto result = posixImpl(nptr, &EndPtr, getCLocale()); *outResult = result; - if (result == huge || result == -huge || result == 0.0 || result == -0.0) { - if (errno == ERANGE) - EndPtr = nullptr; - } return EndPtr; } diff --git a/test/stdlib/NumericParsing.swift.gyb b/test/stdlib/NumericParsing.swift.gyb index c592fbbeab482..223cf4fe045aa 100644 --- a/test/stdlib/NumericParsing.swift.gyb +++ b/test/stdlib/NumericParsing.swift.gyb @@ -181,17 +181,11 @@ tests.test("${Self}/Basics") { expectNil(${Self}("0 ")) // Trailing whitespace expectNil(${Self}("\u{1D7FF}")) // MATHEMATICAL MONOSPACE DIGIT NINE - // Overflow and underflow. Interleave with other checks to make - // sure we're not abusing errno - expectEqual(0.0, ${Self}("0")) - expectNil(${Self}("2e99999999999999")) - expectEqual(0.0, ${Self}("0")) - expectNil(${Self}("2e-99999999999999")) - expectEqual(0.0, ${Self}("0")) - expectNil(${Self}("-2e99999999999999")) - expectEqual(0.0, ${Self}("0")) - expectNil(${Self}("-2e-99999999999999")) - expectEqual(0.0, ${Self}("0")) + // Overflow to infinity, underflow to zero. + expectEqual(.infinity, ${Self}("2e99999999999999")) + expectEqual(0.0, ${Self}("2e-99999999999999")) + expectEqual(-.infinity, ${Self}("-2e99999999999999")) + expectEqual(0.0, ${Self}("-2e-99999999999999")) } % if Self == 'Float80': From cd745340527dfb39a713db601e59ee31084b5b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=85=B7=E9=85=B7=E7=9A=84=E5=93=80=E6=AE=BF?= Date: Mon, 19 Oct 2020 21:54:50 +0800 Subject: [PATCH 552/745] [Docs] Clarifies `ObjectIdentifier` guarantees (#31472) [SR-13564](https://bugs.swift.org/browse/SR-13564) rdar://69169351 --- stdlib/public/core/ObjectIdentifier.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/stdlib/public/core/ObjectIdentifier.swift b/stdlib/public/core/ObjectIdentifier.swift index e34a404ca45b7..d9517abe56976 100644 --- a/stdlib/public/core/ObjectIdentifier.swift +++ b/stdlib/public/core/ObjectIdentifier.swift @@ -15,6 +15,11 @@ /// In Swift, only class instances and metatypes have unique identities. There /// is no notion of identity for structs, enums, functions, or tuples. @frozen // trivial-implementation +/// +/// `ObjectIdentifier` is only guaranteed to remain unique for the +/// lifetime of an object. If an object has a stronger notion of identity, it +/// may be appropriate to provide a custom implementation. + public struct ObjectIdentifier { @usableFromInline // trivial-implementation internal let _value: Builtin.RawPointer From e6fbb4b247173bea19691a445642ba6841bc6d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=85=B7=E9=85=B7=E7=9A=84=E5=93=80=E6=AE=BF?= Date: Mon, 19 Oct 2020 22:06:19 +0800 Subject: [PATCH 553/745] Update ObjectIdentifier.swift --- stdlib/public/core/ObjectIdentifier.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/stdlib/public/core/ObjectIdentifier.swift b/stdlib/public/core/ObjectIdentifier.swift index d9517abe56976..a32d99fdb787f 100644 --- a/stdlib/public/core/ObjectIdentifier.swift +++ b/stdlib/public/core/ObjectIdentifier.swift @@ -17,9 +17,11 @@ @frozen // trivial-implementation /// /// `ObjectIdentifier` is only guaranteed to remain unique for the -/// lifetime of an object. If an object has a stronger notion of identity, it -/// may be appropriate to provide a custom implementation. - +/// lifetime of an object. When the instance gets deallocated, its object +/// identifier may be reused for a different object. (Internally, objects are +/// identified by their memory location.) +/// If you need an object identifier over the lifetime of an objec, it may +/// be appropriate to provide a custom implementation of `Identifiable`. public struct ObjectIdentifier { @usableFromInline // trivial-implementation internal let _value: Builtin.RawPointer From b1633fdc361c847d0d22fa2ca1a674bad480200e Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Mon, 19 Oct 2020 11:29:22 -0400 Subject: [PATCH 554/745] [Test] Remove `as AnyObject` cast from OS_objects.swift. This workaround is no longer needed. rdar://problem/27526994 --- test/Interpreter/SDK/OS_objects.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/Interpreter/SDK/OS_objects.swift b/test/Interpreter/SDK/OS_objects.swift index 260ccde0ca87a..2f228111ddc1e 100644 --- a/test/Interpreter/SDK/OS_objects.swift +++ b/test/Interpreter/SDK/OS_objects.swift @@ -16,9 +16,8 @@ import DispatchObjects // CHECK: Get current queue print("Get current queue") -// TODO: Properly implement generalized dynamic casts from Any to -// runtime-visible classes. `as AnyObject` should be unnecessary here. -let obj = dispatch_get_current_queue() as AnyObject + +let obj = dispatch_get_current_queue() // CHECK-NEXT: Object is a dispatch queue if let q = obj as? OS_dispatch_queue { From 3c4ffcf0eee2d02d109fe1a18d94777f78362150 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Mon, 19 Oct 2020 09:13:59 -0700 Subject: [PATCH 555/745] Fix python lint --- utils/build-script | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/utils/build-script b/utils/build-script index d7b6720944ac3..639f790fe3543 100755 --- a/utils/build-script +++ b/utils/build-script @@ -62,6 +62,7 @@ _SUPPORTED_XCODE_BUILDS = [ # ----------------------------------------------------------------------------- # Helpers + def print_note(message, stream=sys.stdout): """Writes a diagnostic message to the given stream. By default this function outputs to stdout. @@ -152,6 +153,7 @@ def print_xcodebuild_versions(file=sys.stdout): print(fmt.format(version=version, sdks=sdks), file=file) file.flush() + def validate_xcode_compatibility(): if sys.platform != 'darwin': return @@ -168,11 +170,14 @@ def validate_xcode_compatibility(): valid_versions_string = "\n".join( "{} ({})".format(*x) for x in _SUPPORTED_XCODE_BUILDS) raise SystemExit( - "error: using unsupported Xcode version:\n\n{}\n\nInstall one of:\n{}\n\nOr set 'SKIP_XCODE_VERSION_CHECK=1' in the environment".format( + "error: using unsupported Xcode version:\n\n{}\n\n" + "Install one of:\n{}\n\n" + "Or set 'SKIP_XCODE_VERSION_CHECK=1' in the environment".format( version, valid_versions_string ) ) + def tar(source, destination): """ Create a gzip archive of the file at 'source' at the given From f2f7cc6fb2494074c6ce9cbfe8869f819bc5934a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Harck=20T=C3=B8nning?= Date: Mon, 19 Oct 2020 20:10:55 +0200 Subject: [PATCH 556/745] [docs] Fix a typo in CppInteroperabilityManifesto.md (#34345) The "bridging-std-string" section contained a typo in the C++ header example, compared to the imported Swift header. --- docs/CppInteroperabilityManifesto.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CppInteroperabilityManifesto.md b/docs/CppInteroperabilityManifesto.md index 3ea8d9e85a347..a03580202ab63 100644 --- a/docs/CppInteroperabilityManifesto.md +++ b/docs/CppInteroperabilityManifesto.md @@ -3197,7 +3197,7 @@ UTF-8 data. class Employee { public: std::string DebugDescription() const; - [[swift::import_as_std_string]] std::string SeriaziledAsProtobuf() const; + [[swift::import_as_std_string]] std::string SerializedAsProtobuf() const; }; ``` From 15f5222bbdf357f3c23c12bd07434b64f4d2500a Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Wed, 14 Oct 2020 11:05:28 -0700 Subject: [PATCH 557/745] [CodeCompletion][Sema] Allow missing args when solving if the completion location indicates the user may intend to write them later. func foo(a: Int, b: Int) {} func foo(a: String) {} // Int and String should both be valid, despite the missing argument for the // first overload since the second arg may just have not been written yet. foo(a: func bar(a: (Int) -> ()) {} func bar(a: (String, Int) -> ()) {} // $0 being of type String should be valid, rather than just Int, since $1 may // just have not been written yet. bar { $0. } --- include/swift/Sema/CSFix.h | 25 ++++-- include/swift/Sema/ConstraintSystem.h | 5 +- lib/Sema/CSDiagnostics.cpp | 20 ++--- lib/Sema/CSDiagnostics.h | 6 +- lib/Sema/CSFix.cpp | 4 +- lib/Sema/CSGen.cpp | 7 ++ lib/Sema/CSSimplify.cpp | 90 ++++++++++++++++---- lib/Sema/TypeCheckCodeCompletion.cpp | 80 +++++++++++++++++- test/IDE/complete_ambiguous.swift | 113 +++++++++++++++++++++++++- 9 files changed, 306 insertions(+), 44 deletions(-) diff --git a/include/swift/Sema/CSFix.h b/include/swift/Sema/CSFix.h index 80a6b7535401f..e83e5ca066ef3 100644 --- a/include/swift/Sema/CSFix.h +++ b/include/swift/Sema/CSFix.h @@ -1178,18 +1178,21 @@ class AllowClosureParamDestructuring final : public ConstraintFix { ConstraintLocator *locator); }; +struct SynthesizedArg { + unsigned paramIdx; + AnyFunctionType::Param param; +}; + class AddMissingArguments final : public ConstraintFix, private llvm::TrailingObjects< - AddMissingArguments, std::pair> { + AddMissingArguments, SynthesizedArg> { friend TrailingObjects; - using SynthesizedParam = std::pair; - unsigned NumSynthesized; AddMissingArguments(ConstraintSystem &cs, - ArrayRef synthesizedArgs, + ArrayRef synthesizedArgs, ConstraintLocator *locator) : ConstraintFix(cs, FixKind::AddMissingArguments, locator), NumSynthesized(synthesizedArgs.size()) { @@ -1200,8 +1203,8 @@ class AddMissingArguments final public: std::string getName() const override { return "synthesize missing argument(s)"; } - ArrayRef getSynthesizedArguments() const { - return {getTrailingObjects(), NumSynthesized}; + ArrayRef getSynthesizedArguments() const { + return {getTrailingObjects(), NumSynthesized}; } bool diagnose(const Solution &solution, bool asNote = false) const override; @@ -1211,12 +1214,16 @@ class AddMissingArguments final } static AddMissingArguments *create(ConstraintSystem &cs, - ArrayRef synthesizedArgs, + ArrayRef synthesizedArgs, ConstraintLocator *locator); + static bool classof(const ConstraintFix *fix) { + return fix->getKind() == FixKind::AddMissingArguments; + } + private: - MutableArrayRef getSynthesizedArgumentsBuf() { - return {getTrailingObjects(), NumSynthesized}; + MutableArrayRef getSynthesizedArgumentsBuf() { + return {getTrailingObjects(), NumSynthesized}; } }; diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 17f804123a6c3..b43c827f20041 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -5393,7 +5393,10 @@ class MatchCallArgumentListener { /// indices. /// /// \param paramIdx The index of the parameter that is missing an argument. - virtual Optional missingArgument(unsigned paramIdx); + /// \param argInsertIdx The index in the argument list where this argument was + /// expected. + virtual Optional missingArgument(unsigned paramIdx, + unsigned argInsertIdx); /// Indicate that there was no label given when one was expected by parameter. /// diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 27f2cc1487f0f..ac67fde1b76d1 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -4097,9 +4097,9 @@ bool MissingArgumentsFailure::diagnoseAsError() { interleave( SynthesizedArgs, - [&](const std::pair &e) { - const auto paramIdx = e.first; - const auto &arg = e.second; + [&](const SynthesizedArg &e) { + const auto paramIdx = e.paramIdx; + const auto &arg = e.param; if (arg.hasLabel()) { arguments << "'" << arg.getLabel().str() << "'"; @@ -4126,8 +4126,8 @@ bool MissingArgumentsFailure::diagnoseAsError() { llvm::raw_svector_ostream fixIt(scratch); interleave( SynthesizedArgs, - [&](const std::pair &arg) { - forFixIt(fixIt, arg.second); + [&](const SynthesizedArg &arg) { + forFixIt(fixIt, arg.param); }, [&] { fixIt << ", "; }); @@ -4176,8 +4176,8 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { return false; const auto &argument = SynthesizedArgs.front(); - auto position = argument.first; - auto label = argument.second.getLabel(); + auto position = argument.paramIdx; + auto label = argument.param.getLabel(); Expr *fnExpr = nullptr; Expr *argExpr = nullptr; @@ -4192,7 +4192,7 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { } // Will the parameter accept a trailing closure? - Type paramType = resolveType(argument.second.getPlainType()); + Type paramType = resolveType(argument.param.getPlainType()); bool paramAcceptsTrailingClosure = paramType ->lookThroughAllOptionalTypes()->is(); @@ -4208,7 +4208,7 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { else if (position != 0) insertText << ", "; - forFixIt(insertText, argument.second); + forFixIt(insertText, argument.param); if (position == 0 && numArgs > 0 && (!firstTrailingClosure || position < *firstTrailingClosure)) @@ -6036,7 +6036,7 @@ bool ArgumentMismatchFailure::diagnoseMisplacedMissingArgument() const { auto anchor = getRawAnchor(); MissingArgumentsFailure failure( - solution, {std::make_pair(0, param)}, + solution, {SynthesizedArg{0, param}}, getConstraintLocator(anchor, ConstraintLocator::ApplyArgument)); return failure.diagnoseSingleMissingArgument(); diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 1a2f1b4c99da1..c57da9befe1d4 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1271,13 +1271,11 @@ class ImplicitInitOnNonConstMetatypeFailure final }; class MissingArgumentsFailure final : public FailureDiagnostic { - using SynthesizedParam = std::pair; - - SmallVector SynthesizedArgs; + SmallVector SynthesizedArgs; public: MissingArgumentsFailure(const Solution &solution, - ArrayRef synthesizedArgs, + ArrayRef synthesizedArgs, ConstraintLocator *locator) : FailureDiagnostic(solution, locator), SynthesizedArgs(synthesizedArgs.begin(), synthesizedArgs.end()) { diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index aba584a600148..da0e1296427f3 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -748,9 +748,9 @@ bool AddMissingArguments::diagnose(const Solution &solution, AddMissingArguments * AddMissingArguments::create(ConstraintSystem &cs, - ArrayRef synthesizedArgs, + ArrayRef synthesizedArgs, ConstraintLocator *locator) { - unsigned size = totalSizeToAlloc(synthesizedArgs.size()); + unsigned size = totalSizeToAlloc(synthesizedArgs.size()); void *mem = cs.getAllocator().Allocate(size, alignof(AddMissingArguments)); return new (mem) AddMissingArguments(cs, synthesizedArgs, locator); } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 2c3e9bbbd5c53..eaccb486df849 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -589,6 +589,13 @@ namespace { // Determine whether the given declaration is favored. auto isFavoredDecl = [&](ValueDecl *value, Type type) -> bool { + // We want to consider all options for calls that might contain the code + // completion location, as missing arguments after the completion + // location are valid (since it might be that they just haven't been + // written yet). + if (CS.isForCodeCompletion()) + return false; + if (!type->is()) return false; diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index f56558cdf3cc6..7fb2fd408e045 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -40,7 +40,8 @@ MatchCallArgumentListener::~MatchCallArgumentListener() { } bool MatchCallArgumentListener::extraArgument(unsigned argIdx) { return true; } Optional -MatchCallArgumentListener::missingArgument(unsigned paramIdx) { +MatchCallArgumentListener::missingArgument(unsigned paramIdx, + unsigned argInsertIdx) { return None; } @@ -701,11 +702,14 @@ static bool matchCallArgumentsImpl( } // If we have any unfulfilled parameters, check them now. + Optional prevArgIdx; if (haveUnfulfilledParams) { for (auto paramIdx : indices(params)) { // If we have a binding for this parameter, we're done. - if (!parameterBindings[paramIdx].empty()) + if (!parameterBindings[paramIdx].empty()) { + prevArgIdx = parameterBindings[paramIdx].back(); continue; + } const auto ¶m = params[paramIdx]; @@ -717,7 +721,8 @@ static bool matchCallArgumentsImpl( if (paramInfo.hasDefaultArgument(paramIdx)) continue; - if (auto newArgIdx = listener.missingArgument(paramIdx)) { + unsigned argInsertIdx = prevArgIdx ? *prevArgIdx + 1 : 0; + if (auto newArgIdx = listener.missingArgument(paramIdx, argInsertIdx)) { parameterBindings[paramIdx].push_back(*newArgIdx); continue; } @@ -980,14 +985,47 @@ constraints::matchCallArguments( }; } +static Optional +getCompletionArgIndex(ASTNode anchor, SourceManager &SM) { + Expr *arg = nullptr; + if (auto *CE = getAsExpr(anchor)) + arg = CE->getArg(); + if (auto *SE = getAsExpr(anchor)) + arg = SE->getIndex(); + if (auto *OLE = getAsExpr(anchor)) + arg = OLE->getArg(); + + if (!arg) + return None; + + auto containsCompletion = [&](Expr *elem) { + if (!elem) + return false; + SourceRange range = elem->getSourceRange(); + return range.isValid() && SM.rangeContainsCodeCompletionLoc(range); + }; + + if (auto *TE = dyn_cast(arg)) { + auto elems = TE->getElements(); + auto idx = llvm::find_if(elems, containsCompletion); + if (idx != elems.end()) + return std::distance(elems.begin(), idx); + } else if (auto *PE = dyn_cast(arg)) { + if (containsCompletion(PE->getSubExpr())) + return 0; + } + return None; +} + class ArgumentFailureTracker : public MatchCallArgumentListener { ConstraintSystem &CS; SmallVectorImpl &Arguments; ArrayRef Parameters; ConstraintLocatorBuilder Locator; - SmallVector, 4> MissingArguments; + SmallVector MissingArguments; SmallVector, 4> ExtraArguments; + Optional CompletionArgIdx; public: ArgumentFailureTracker(ConstraintSystem &cs, @@ -1006,7 +1044,8 @@ class ArgumentFailureTracker : public MatchCallArgumentListener { } } - Optional missingArgument(unsigned paramIdx) override { + Optional missingArgument(unsigned paramIdx, + unsigned argInsertIdx) override { if (!CS.shouldAttemptFixes()) return None; @@ -1023,10 +1062,22 @@ class ArgumentFailureTracker : public MatchCallArgumentListener { TVO_CanBindToNoEscape | TVO_CanBindToHole); auto synthesizedArg = param.withType(argType); - - MissingArguments.push_back(std::make_pair(paramIdx, synthesizedArg)); Arguments.push_back(synthesizedArg); + if (CS.isForCodeCompletion()) { + // When solving for code completion, if any argument contains the + // completion location, later arguments shouldn't be considered missing + // (causing the solution to have a worse score) as the user just hasn't + // written them yet. Early exit to avoid recording them in this case. + SourceManager &SM = CS.getASTContext().SourceMgr; + if (!CompletionArgIdx) + CompletionArgIdx = getCompletionArgIndex(Locator.getAnchor(), SM); + if (CompletionArgIdx && *CompletionArgIdx < argInsertIdx) + return newArgIdx; + } + + MissingArguments.push_back(SynthesizedArg{paramIdx, synthesizedArg}); + return newArgIdx; } @@ -1190,12 +1241,11 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( argsWithLabels.pop_back(); // Let's make sure that labels associated with tuple elements // line up with what is expected by argument list. - SmallVector, 4> - synthesizedArgs; + SmallVector synthesizedArgs; for (unsigned i = 0, n = argTuple->getNumElements(); i != n; ++i) { const auto &elt = argTuple->getElement(i); AnyFunctionType::Param argument(elt.getType(), elt.getName()); - synthesizedArgs.push_back(std::make_pair(i, argument)); + synthesizedArgs.push_back(SynthesizedArg{i, argument}); argsWithLabels.push_back(argument); } @@ -1771,15 +1821,27 @@ static bool fixMissingArguments(ConstraintSystem &cs, ASTNode anchor, cs.createTypeVariable(argLoc, TVO_CanBindToNoEscape))); } - SmallVector, 4> synthesizedArgs; + SmallVector synthesizedArgs; synthesizedArgs.reserve(numMissing); for (unsigned i = args.size() - numMissing, n = args.size(); i != n; ++i) { - synthesizedArgs.push_back(std::make_pair(i, args[i])); + synthesizedArgs.push_back(SynthesizedArg{i, args[i]}); + } + + // Treat missing anonymous arguments as valid in closures containing the + // code completion location, since they may have just not been written yet. + if (cs.isForCodeCompletion()) { + if (auto *closure = getAsExpr(anchor)) { + SourceManager &SM = closure->getASTContext().SourceMgr; + SourceRange range = closure->getSourceRange(); + if (range.isValid() && SM.rangeContainsCodeCompletionLoc(range) && + (closure->hasAnonymousClosureVars() || + (args.empty() && closure->getInLoc().isInvalid()))) + return false; + } } auto *fix = AddMissingArguments::create(cs, synthesizedArgs, cs.getConstraintLocator(locator)); - if (cs.recordFix(fix)) return true; @@ -3966,7 +4028,7 @@ bool ConstraintSystem::repairFailures( // to diagnose this as a missing argument which can't be ignored. if (arg != getTypeVariables().end()) { conversionsOrFixes.push_back(AddMissingArguments::create( - *this, {std::make_pair(0, AnyFunctionType::Param(*arg))}, + *this, {SynthesizedArg{0, AnyFunctionType::Param(*arg)}}, getConstraintLocator(anchor, path))); break; } diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index 5df6785449ef6..663d0d75c7478 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -754,6 +754,76 @@ class CompletionContextFinder : public ASTWalker { } // end namespace +// Determine if the target expression is the implicit BinaryExpr generated for +// pattern-matching in a switch/if/guard case ( ~= matchValue). +static bool isForPatternMatch(SolutionApplicationTarget &target) { + if (target.getExprContextualTypePurpose() != CTP_Condition) + return false; + Expr *condition = target.getAsExpr(); + if (!condition->isImplicit()) + return false; + if (auto *BE = dyn_cast(condition)) { + Identifier id; + if (auto *ODRE = dyn_cast(BE->getFn())) { + id = ODRE->getDecls().front()->getBaseIdentifier(); + } else if (auto *DRE = dyn_cast(BE->getFn())) { + id = DRE->getDecl()->getBaseIdentifier(); + } + if (id != target.getDeclContext()->getASTContext().Id_MatchOperator) + return false; + return isa(BE->getArg()->getElement(0)); + } + return false; +} + +/// Remove any solutions from the provided vector with a score less than the best solution's. +static void filterSolutions(SolutionApplicationTarget &target, + SmallVectorImpl &solutions) { + // FIXME: this is only needed because in pattern matching position, the + // code completion expression always becomes an expression pattern, which + // requires the ~= operator to be defined on the type being matched against. + // Pattern matching against an enum doesn't require that however, so valid + // solutions always end up having fixes. This is a problem because there will + // always be a valid solution as well. Optional defines ~= between Optional + // and _OptionalNilComparisonType (which defines a nilLiteral initializer), + // and the matched-against value can implicitly be made Optional if it isn't + // already, so _OptionalNilComparisonType is always a valid solution for the + // completion. That only generates the 'nil' completion, which is rarely what + // the user intends to write in this position and shouldn't be preferred over + // the other formed solutions (which require fixes). We should generate enum + // pattern completions separately. + if (isForPatternMatch(target)) + return; + + if (solutions.size() <= 1) + return; + + auto min = std::min_element(solutions.begin(), solutions.end(), + [](const Solution &a, const Solution &b) { + return a.getFixedScore() < b.getFixedScore(); + }); + auto fixStart = llvm::partition(solutions, [&](const Solution &S) { + return S.getFixedScore() == min->getFixedScore(); + }); + + if (fixStart != solutions.begin() && fixStart != solutions.end()) + solutions.erase(fixStart, solutions.end()); +} + +/// When solving for code completion we still consider solutions with holes as +/// valid. Regular type-checking does not. This is intended to return true only +/// if regular type-checking would consider this solution viable. +static bool isViableForReTypeCheck(const Solution &S) { + if (llvm::any_of(S.Fixes, [&](const ConstraintFix *CF) { + return !CF->isWarning(); + })) + return false; + using Binding = std::pair; + return llvm::none_of(S.typeBindings, [&](const Binding& binding) { + return binding.second->hasHole(); + }); +} + bool TypeChecker::typeCheckForCodeCompletion( SolutionApplicationTarget &target, llvm::function_ref callback) { @@ -828,6 +898,10 @@ bool TypeChecker::typeCheckForCodeCompletion( if (!cs.solveForCodeCompletion(target, solutions)) return CompletionResult::Fallback; + // FIXME: instead of filtering, expose the score and viability to clients. + // Only keep the solution(s) with the best score. + filterSolutions(target, solutions); + // Similarly, if the type-check didn't produce any solutions, fall back // to type-checking a sub-expression in isolation. if (solutions.empty()) @@ -846,9 +920,9 @@ bool TypeChecker::typeCheckForCodeCompletion( // At this point we know the code completion expression wasn't checked // with the closure's surrounding context. If a single valid solution - // was formed we can wait until body of the closure is type-checked and - // gather completions then. - if (solutions.size() == 1 && solution.Fixes.empty()) + // was formed we can wait until the body of the closure is type-checked + // and gather completions then. + if (solutions.size() == 1 && isViableForReTypeCheck(solution)) return CompletionResult::NotApplicable; // Otherwise, it's unlikely the body will ever be type-checked, so fall diff --git a/test/IDE/complete_ambiguous.swift b/test/IDE/complete_ambiguous.swift index 2a2028c44f38d..f1f535ad53c05 100644 --- a/test/IDE/complete_ambiguous.swift +++ b/test/IDE/complete_ambiguous.swift @@ -6,6 +6,16 @@ // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=RELATED_INERROREXPR | %FileCheck %s --check-prefix=RELATED // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=NOCALLBACK_FALLBACK | %FileCheck %s --check-prefix=RELATED // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=MULTICLOSURE_FALLBACK | %FileCheck %s --check-prefix=MULTICLOSURE_FALLBACK +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=UNAMBIGUOUSCLOSURE_ARG | %FileCheck %s --check-prefix=UNAMBIGUOUSCLOSURE_ARG +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=AMBIGUOUSCLOSURE_ARG | %FileCheck %s --check-prefix=AMBIGUOUSCLOSURE_ARG +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=AMBIGUOUSCLOSURE_ARG_RETURN | %FileCheck %s --check-prefix=AMBIGUOUSCLOSURE_ARG_RETURN +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_FOO | %FileCheck %s --check-prefix=OVERLOADEDFUNC_FOO +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_BAR | %FileCheck %s --check-prefix=OVERLOADEDFUNC_BAR +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_MISSINGLABEL | %FileCheck %s --check-prefix=OVERLOADEDFUNC_BOTH +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_MISSINGARG_AFTER | %FileCheck %s --check-prefix=OVERLOADEDFUNC_BOTH +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDMEMBER_MISSINGARG_AFTER | %FileCheck %s --check-prefix=OVERLOADEDFUNC_BOTH +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_MISSINGARG_BEFORE | %FileCheck %s --check-prefix=OVERLOADEDFUNC_BAR +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADEDFUNC_MISSINGARG_BEFOREANDAFTER | %FileCheck %s --check-prefix=OVERLOADEDFUNC_BAR // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=ERROR_IN_BASE | %FileCheck %s --check-prefix=SIMPLE // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC | %FileCheck %s --check-prefix=GENERIC // RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_MISSINGARG | %FileCheck %s --check-prefix=NORESULTS @@ -69,7 +79,6 @@ switch undefined { break } - func takesClosureA(_ arg: (A) -> ()) {} func takesClosureB(_ arg: (B) -> ()) {} @@ -85,6 +94,108 @@ takesClosureA { arg in // MULTICLOSURE_FALLBACK-DAG: Decl[InstanceMethod]/CurrNominal: doBThings()[#Void#]{{; name=.+$}} // MULTICLOSURE_FALLBACK: End completions +func takesAnonClosure(_ x: (A) -> A) { return A() } +func takesAnonClosure(_ x: (B, A) -> B { return B() } +func takesAnonClosure(_ x: () -> (A, B) { return (A(), B()) } + +struct TestRelations { + static let a = A() + static let b = B() + static let ab = (A(), B()) +} + +// test we consider both overloads as $0 or $1 may have just not been written yet +takesAnonClosure { $1.#^UNAMBIGUOUSCLOSURE_ARG^# } +// UNAMBIGUOUSCLOSURE_ARG: Begin completions, 2 items +// UNAMBIGUOUSCLOSURE_ARG-DAG: Keyword[self]/CurrNominal: self[#A#]{{; name=.+$}} +// UNAMBIGUOUSCLOSURE_ARG-DAG: Decl[InstanceMethod]/CurrNominal: doAThings()[#A#]{{; name=.+$}} +// UNAMBIGUOUSCLOSURE_ARG: End completions + +takesAnonClosure { $0.#^AMBIGUOUSCLOSURE_ARG^# } +// AMBIGUOUSCLOSURE_ARG: Begin completions, 4 items +// AMBIGUOUSCLOSURE_ARG-DAG: Keyword[self]/CurrNominal: self[#A#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: doAThings()[#A#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG-DAG: Keyword[self]/CurrNominal: self[#B#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG-DAG: Decl[InstanceMethod]/CurrNominal: doBThings()[#Void#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG: End completions + +takesAnonClosure { TestRelations.#^AMBIGUOUSCLOSURE_ARG_RETURN^# } +// AMBIGUOUSCLOSURE_ARG_RETURN: Begin completions, 6 items +// AMBIGUOUSCLOSURE_ARG_RETURN-DAG: Keyword[self]/CurrNominal: self[#TestRelations.Type#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG_RETURN-DAG: Keyword/CurrNominal: Type[#TestRelations.Type#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG_RETURN-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: a[#A#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG_RETURN-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: b[#B#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG_RETURN-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: ab[#(A, B)#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG_RETURN-DAG: Decl[Constructor]/CurrNominal: init()[#TestRelations#]{{; name=.+$}} +// AMBIGUOUSCLOSURE_ARG_RETURN: End completions + + +func testMissingArgs() { + enum Foo { case foo } + enum Bar { case bar } + + struct Test { + static let foo = Foo.foo + static let bar = Bar.bar + } + + func test(foo: Foo) {} + func test(bar: Bar) {} + + func test2(first: Bar, second: Int) {} + func test2(first: Foo) {} + + func test3(skipMe: Int, after: Foo) {} + func test3(after: Bar) {} + + func test4(skipMe: Int, both: Foo, skipMeToo: Int) {} + func test4(both: Bar, skipMeTo: Int) {} + + + test(foo: Test.#^OVERLOADEDFUNC_FOO^#) + // OVERLOADEDFUNC_FOO: Begin completions, 5 items + // OVERLOADEDFUNC_FOO-DAG: Keyword[self]/CurrNominal: self[#Test.Type#]{{; name=.+$}} + // OVERLOADEDFUNC_FOO-DAG: Keyword/CurrNominal: Type[#Test.Type#]{{; name=.+$}} + // OVERLOADEDFUNC_FOO-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: foo[#Foo#]{{; name=.+$}} + // OVERLOADEDFUNC_FOO-DAG: Decl[StaticVar]/CurrNominal: bar[#Bar#]{{; name=.+$}} + // OVERLOADEDFUNC_FOO-DAG: Decl[Constructor]/CurrNominal: init()[#Test#]{{; name=.+$}} + // OVERLOADEDFUNC_FOO: End completions + + test(bar: Test.#^OVERLOADEDFUNC_BAR^#) + // OVERLOADEDFUNC_BAR: Begin completions, 5 items + // OVERLOADEDFUNC_BAR-DAG: Keyword[self]/CurrNominal: self[#Test.Type#]{{; name=.+$}} + // OVERLOADEDFUNC_BAR-DAG: Keyword/CurrNominal: Type[#Test.Type#]{{; name=.+$}} + // OVERLOADEDFUNC_BAR-DAG: Decl[StaticVar]/CurrNominal: foo[#Foo#]{{; name=.+$}} + // OVERLOADEDFUNC_BAR-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: bar[#Bar#]{{; name=.+$}} + // OVERLOADEDFUNC_BAR-DAG: Decl[Constructor]/CurrNominal: init()[#Test#]{{; name=.+$}} + // OVERLOADEDFUNC_BAR: End completions + + test(Test.#^OVERLOADEDFUNC_MISSINGLABEL^#, extraArg: 2) + test2(first: Test.#^OVERLOADEDFUNC_MISSINGARG_AFTER^#) + + // Also check ambiguous member functions + struct TestStruct { + func test2(first: Bar, second: Int) {} + func test2(first: Foo) {} + } + + TestStruct().test2(first: Test.#^OVERLOADEDMEMBER_MISSINGARG_AFTER^#) + + // TODO: Should we insert the missing label in the completion text for OVERLOADEDFUNC_MISSINGLABEL? + // OVERLOADEDFUNC_BOTH: Begin completions, 5 items + // OVERLOADEDFUNC_BOTH-DAG: Keyword[self]/CurrNominal: self[#Test.Type#]{{; name=.+$}} + // OVERLOADEDFUNC_BOTH-DAG: Keyword/CurrNominal: Type[#Test.Type#]{{; name=.+$}} + // OVERLOADEDFUNC_BOTH-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: foo[#Foo#]{{; name=.+$}} + // OVERLOADEDFUNC_BOTH-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: bar[#Bar#]{{; name=.+$}} + // OVERLOADEDFUNC_BOTH-DAG: Decl[Constructor]/CurrNominal: init()[#Test#]{{; name=.+$}} + // OVERLOADEDFUNC_BOTH: End completions + + test3(after: Test.#^OVERLOADEDFUNC_MISSINGARG_BEFORE^#); + test4(both: Test.#^OVERLOADEDFUNC_MISSINGARG_BEFOREANDAFTER^#) +} + + + protocol C { associatedtype Element func getCElem() -> Element From 6fc78d5fbf9b51d31a0ae944d444b6e94e5f2ad8 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 12 Oct 2020 15:16:38 -0700 Subject: [PATCH 558/745] Switch To target-swiftc_driver Try to pass an explicit target to these tests so more exotic hosts do the right thing. rdar://70175753 --- .../CrossModule/external-cascade.swift | 27 +++++++------------ test/Incremental/CrossModule/linear.swift | 24 +++++++---------- test/Incremental/CrossModule/transitive.swift | 24 +++++++---------- 3 files changed, 28 insertions(+), 47 deletions(-) diff --git a/test/Incremental/CrossModule/external-cascade.swift b/test/Incremental/CrossModule/external-cascade.swift index 993493a092157..c3c68d9d4d75d 100644 --- a/test/Incremental/CrossModule/external-cascade.swift +++ b/test/Incremental/CrossModule/external-cascade.swift @@ -1,12 +1,6 @@ // RUN: %empty-directory(%t) // RUN: cp -r %S/Inputs/external-cascade/* %t -// rdar://problem/70012853 -// XFAIL: OS=windows-msvc - -// rdar://70175753 -// REQUIRES: rdar70175753 - // // This test establishes a chain of modules that all depend on a set of // bridging headers. This test ensures that changes to external dependencies - @@ -22,19 +16,18 @@ // Set up a clean incremental build of all three modules // -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle %t/C.swift -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle %t/C.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift // // Now change a header and ensure that the rebuild cascades outwards // -// RUN: rm %t/another-header.h -// RUN: cp %S/Inputs/external-cascade/another-header.h %t/another-header.h -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s +// RUN: touch -t 201401240006 %t/another-header.h +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s // MODULE-C: Job finished: {generate-pch: bridging-header-[[BRIDGING_HEADER:.*]].pch <= bridging-header.h} // MODULE-C: Job finished: {compile: C.o <= C.swift bridging-header-[[BRIDGING_HEADER]].pch} @@ -53,9 +46,9 @@ // And ensure that the null build really is null. // -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s // MODULE-C-NULL: Job finished: {generate-pch: bridging-header-[[BRIDGING_HEADER:.*]].pch <= bridging-header.h} // MODULE-C-NULL: Job skipped: {compile: C.o <= C.swift bridging-header-[[BRIDGING_HEADER]].pch} diff --git a/test/Incremental/CrossModule/linear.swift b/test/Incremental/CrossModule/linear.swift index 084392d1c88c8..4f4445a462d50 100644 --- a/test/Incremental/CrossModule/linear.swift +++ b/test/Incremental/CrossModule/linear.swift @@ -1,12 +1,6 @@ // RUN: %empty-directory(%t) // RUN: cp -r %S/Inputs/linear/* %t -// rdar://problem/70012853 -// XFAIL: OS=windows-msvc - -// rdar://70175753 -// REQUIRES: rdar70175753 - // // This test establishes a "linear" chain of modules that import one another // and ensures that a cross-module incremental build does not needlessly @@ -22,17 +16,17 @@ // Set up a clean incremental build of all three modules // -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DOLD %t/C.swift -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DOLD %t/C.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift // // Now change C and ensure that B rebuilds but A does not // -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s // MODULE-C: Incremental compilation has been disabled @@ -47,9 +41,9 @@ // And ensure that the null build really is null. // -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s // MODULE-C-NULL: Job skipped: {compile: C.o <= C.swift} // MODULE-C-NULL: Job skipped: {merge-module: C.swiftmodule <= C.o} diff --git a/test/Incremental/CrossModule/transitive.swift b/test/Incremental/CrossModule/transitive.swift index 8c5d29b096c01..8b1255245a569 100644 --- a/test/Incremental/CrossModule/transitive.swift +++ b/test/Incremental/CrossModule/transitive.swift @@ -1,12 +1,6 @@ // RUN: %empty-directory(%t) // RUN: cp -r %S/Inputs/transitive/* %t -// rdar://problem/70012853 -// XFAIL: OS=windows-msvc - -// rdar://70175753 -// REQUIRES: rdar70175753 - // // This test establishes a "transitive" chain of modules that import one another // and ensures that a cross-module incremental build rebuilds all modules @@ -23,18 +17,18 @@ // Set up a clean incremental build of all three modules // -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DOLD %t/C.swift -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/B.swift -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/A.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DOLD %t/C.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/B.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/A.swift // // Now change C and ensure that B and A rebuild because of the change to // an incremental external dependency. // -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s // MODULE-C: Incremental compilation has been disabled @@ -50,9 +44,9 @@ // And ensure that the null build really is null. // -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s -// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s // MODULE-C-NULL: Job skipped: {compile: C.o <= C.swift} // MODULE-C-NULL: Job skipped: {merge-module: C.swiftmodule <= C.o} From 17e31d89d2491708aaaab2fd535c229327c979c1 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Mon, 19 Oct 2020 15:53:52 -0400 Subject: [PATCH 559/745] [Runtime] Restore objc_addLoadImageFunc in ImageInspectionMacho.cpp. The conditional use of objc_addLoadImageFunc was accidentally removed in b5759c9fd93ea9d09613c018c48217e7e03f30bd. Put it back. rdar://problem/70452221 --- stdlib/public/runtime/ImageInspectionMachO.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/stdlib/public/runtime/ImageInspectionMachO.cpp b/stdlib/public/runtime/ImageInspectionMachO.cpp index b233608308e99..54c4d17a52a5e 100644 --- a/stdlib/public/runtime/ImageInspectionMachO.cpp +++ b/stdlib/public/runtime/ImageInspectionMachO.cpp @@ -122,7 +122,11 @@ void addImageCallback2Sections(const mach_header *mh, intptr_t vmaddr_slide) { #if OBJC_ADDLOADIMAGEFUNC_DEFINED && SWIFT_OBJC_INTEROP #define REGISTER_FUNC(...) \ - _dyld_register_func_for_add_image(__VA_ARGS__); + if (__builtin_available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)) { \ + objc_addLoadImageFunc(__VA_ARGS__); \ + } else { \ + _dyld_register_func_for_add_image(__VA_ARGS__); \ + } #else #define REGISTER_FUNC(...) _dyld_register_func_for_add_image(__VA_ARGS__) #endif From 6a5684a76c424af9a86ef3c6893ed7428899092f Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 19 Oct 2020 15:21:57 -0400 Subject: [PATCH 560/745] Sema: Allow duplicate internal parameter names on protocol requirements This fixes a recent source compatibility regression. Fixes . --- lib/Sema/TypeCheckDeclPrimary.cpp | 22 +++++++++++++++------- lib/Sema/TypeCheckStmt.cpp | 2 +- lib/Sema/TypeChecker.h | 2 +- test/Sema/redeclaration-checking.swift | 13 +++++++++++++ 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 8f2a6ff804fad..6a5da452ac077 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1318,13 +1318,21 @@ static void maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl) { diagnoseClassWithoutInitializers(classDecl); } -void TypeChecker::checkParameterList(ParameterList *params) { +void TypeChecker::checkParameterList(ParameterList *params, + DeclContext *owner) { for (auto param: *params) { checkDeclAttributes(param); } - // Check for duplicate parameter names. - diagnoseDuplicateDecls(*params); + // For source compatibilty, allow duplicate internal parameter names + // on protocol requirements. + // + // FIXME: Consider turning this into a warning or error if we do + // another -swift-version. + if (!isa(owner->getParent())) { + // Check for duplicate parameter names. + diagnoseDuplicateDecls(*params); + } } void TypeChecker::diagnoseDuplicateBoundVars(Pattern *pattern) { @@ -1769,7 +1777,7 @@ class DeclChecker : public DeclVisitor { (void) SD->isSetterMutating(); (void) SD->getImplInfo(); - TypeChecker::checkParameterList(SD->getIndices()); + TypeChecker::checkParameterList(SD->getIndices(), SD); checkDefaultArguments(SD->getIndices()); @@ -2329,7 +2337,7 @@ class DeclChecker : public DeclVisitor { checkAccessControl(FD); - TypeChecker::checkParameterList(FD->getParameters()); + TypeChecker::checkParameterList(FD->getParameters(), FD); } TypeChecker::checkDeclAttributes(FD); @@ -2440,7 +2448,7 @@ class DeclChecker : public DeclVisitor { TypeChecker::checkDeclAttributes(EED); if (auto *PL = EED->getParameterList()) { - TypeChecker::checkParameterList(PL); + TypeChecker::checkParameterList(PL, EED); checkDefaultArguments(PL); } @@ -2596,7 +2604,7 @@ class DeclChecker : public DeclVisitor { } TypeChecker::checkDeclAttributes(CD); - TypeChecker::checkParameterList(CD->getParameters()); + TypeChecker::checkParameterList(CD->getParameters(), CD); // Check whether this initializer overrides an initializer in its // superclass. diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 250d762a69da0..c6ffc17b2b1f5 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -2061,7 +2061,7 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator, } bool TypeChecker::typeCheckClosureBody(ClosureExpr *closure) { - TypeChecker::checkParameterList(closure->getParameters()); + TypeChecker::checkParameterList(closure->getParameters(), closure); BraceStmt *body = closure->getBody(); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 685a3c54a38bb..e384ae6fc9dae 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -448,7 +448,7 @@ void typeCheckDecl(Decl *D); void addImplicitDynamicAttribute(Decl *D); void checkDeclAttributes(Decl *D); -void checkParameterList(ParameterList *params); +void checkParameterList(ParameterList *params, DeclContext *owner); void diagnoseDuplicateBoundVars(Pattern *pattern); diff --git a/test/Sema/redeclaration-checking.swift b/test/Sema/redeclaration-checking.swift index 558c23faac717..fce87481f8783 100644 --- a/test/Sema/redeclaration-checking.swift +++ b/test/Sema/redeclaration-checking.swift @@ -98,4 +98,17 @@ func stmtTest() { func fullNameTest() { let x = 123 // expected-warning {{never used}} func x() {} +} + +// For source compatibility, allow duplicate parameter labels on +// protocol requirements. +protocol SillyProtocol { + init(x: Int, x: Int) + init(a x: Int, b x: Int) + + func foo(x: Int, x: Int) + func foo(a x: Int, b x: Int) + + subscript(x: Int, x: Int) -> Int { get } + subscript(a x: Int, b x: Int) -> Int { get } } \ No newline at end of file From c39bb8970a85c5e5a12c69ff52d4b7216b81aea8 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 16 Oct 2020 19:12:44 -0400 Subject: [PATCH 561/745] Add regression test for https://bugs.swift.org/browse/SR-8456 --- validation-test/compiler_crashers_2_fixed/sr8456.swift | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 validation-test/compiler_crashers_2_fixed/sr8456.swift diff --git a/validation-test/compiler_crashers_2_fixed/sr8456.swift b/validation-test/compiler_crashers_2_fixed/sr8456.swift new file mode 100644 index 0000000000000..d2176c094ccbb --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr8456.swift @@ -0,0 +1,7 @@ +// RUN: not %target-swift-frontend -typecheck %s + +enum Foo { + case BigA { + } + case littleA(BigA) +} From a5034bc22bb825dad61fcd8d1b1a65f87f019bf0 Mon Sep 17 00:00:00 2001 From: Michelle Casbon Date: Mon, 19 Oct 2020 20:23:50 +0000 Subject: [PATCH 562/745] Correct path in Windows build guide --- docs/WindowsBuild.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/WindowsBuild.md b/docs/WindowsBuild.md index c988b3759646c..ef1683e6881cc 100644 --- a/docs/WindowsBuild.md +++ b/docs/WindowsBuild.md @@ -139,7 +139,7 @@ ninja -C S:\b\1 ```cmd path S:\Library\icu-67\usr\bin;S:\b\1\bin;S:\b\1\tools\swift\libdispatch-prefix\bin;%PATH%;%ProgramFiles%\Git\usr\bin -ninja -C S:\b\toolchain check-swift +ninja -C S:\b\1 check-swift ``` ## Build swift-corelibs-libdispatch From 340d1e843a43663c71119019e05fa0691034be1a Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 19 Oct 2020 13:55:33 -0700 Subject: [PATCH 563/745] [ConstraintSystem] Record trailing choice match choice when arguments/result are equivalent to applied function --- lib/Sema/CSSimplify.cpp | 21 +++++---- unittests/Sema/CMakeLists.txt | 3 +- .../Sema/ConstraintSimplificationTests.cpp | 43 +++++++++++++++++++ 3 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 unittests/Sema/ConstraintSimplificationTests.cpp diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index f56558cdf3cc6..50e6c0ef9d57d 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -8802,14 +8802,6 @@ ConstraintSystem::simplifyApplicableFnConstraint( return false; }; - // If the types are obviously equivalent, we're done. This optimization - // is not valid for operators though, where an inout parameter does not - // have an explicit inout argument. - if (type1.getPointer() == desugar2) { - if (!isOperator || !hasInOut()) - return SolutionKind::Solved; - } - // Local function to form an unsolved result. auto formUnsolved = [&] { if (flags.contains(TMF_GenerateConstraints)) { @@ -8838,6 +8830,19 @@ ConstraintSystem::simplifyApplicableFnConstraint( ConstraintLocatorBuilder outerLocator = getConstraintLocator(anchor, parts, locator.getSummaryFlags()); + // If the types are obviously equivalent, we're done. This optimization + // is not valid for operators though, where an inout parameter does not + // have an explicit inout argument. + if (type1.getPointer() == desugar2) { + if (!isOperator || !hasInOut()) { + recordTrailingClosureMatch( + getConstraintLocator( + outerLocator.withPathElement(ConstraintLocator::ApplyArgument)), + TrailingClosureMatching::Forward); + return SolutionKind::Solved; + } + } + // Handle applications of types with `callAsFunction` methods. // Do this before stripping optional types below, when `shouldAttemptFixes()` // is true. diff --git a/unittests/Sema/CMakeLists.txt b/unittests/Sema/CMakeLists.txt index 1257490d9183e..95346cde1dc1e 100644 --- a/unittests/Sema/CMakeLists.txt +++ b/unittests/Sema/CMakeLists.txt @@ -1,7 +1,8 @@ add_swift_unittest(swiftSemaTests SemaFixture.cpp - BindingInferenceTests.cpp) + BindingInferenceTests.cpp + ConstraintSimplificationTests.cpp) target_link_libraries(swiftSemaTests PRIVATE diff --git a/unittests/Sema/ConstraintSimplificationTests.cpp b/unittests/Sema/ConstraintSimplificationTests.cpp new file mode 100644 index 0000000000000..9b93450884e66 --- /dev/null +++ b/unittests/Sema/ConstraintSimplificationTests.cpp @@ -0,0 +1,43 @@ +//===--- ConstraintSimplificationTests.cpp --------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "SemaFixture.h" +#include "swift/Sema/ConstraintSystem.h" + +using namespace swift; +using namespace swift::unittest; +using namespace swift::constraints; + +TEST_F(SemaTest, TestTrailingClosureMatchRecordingForIdenticalFunctions) { + ConstraintSystem cs(DC, ConstraintSystemOptions()); + + auto intType = getStdlibType("Int"); + auto floatType = getStdlibType("Float"); + + auto func = FunctionType::get({FunctionType::Param(intType)}, floatType); + + cs.addConstraint( + ConstraintKind::ApplicableFunction, func, func, + cs.getConstraintLocator({}, ConstraintLocator::ApplyFunction)); + + SmallVector solutions; + cs.solve(solutions); + + ASSERT_EQ(solutions.size(), (unsigned)1); + + const auto &solution = solutions.front(); + + auto *locator = cs.getConstraintLocator({}, ConstraintLocator::ApplyArgument); + auto choice = solution.trailingClosureMatchingChoices.find(locator); + ASSERT_TRUE(choice != solution.trailingClosureMatchingChoices.end()); + ASSERT_EQ(choice->second, TrailingClosureMatching::Forward); +} From 27b152c93fb10631d999fda743c5404867a1ec69 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 19 Oct 2020 14:06:12 -0700 Subject: [PATCH 564/745] [Gardening] Drop Trailing Commas --- test/Incremental/CrossModule/Inputs/external-cascade/A.json | 2 +- test/Incremental/CrossModule/Inputs/external-cascade/B.json | 2 +- test/Incremental/CrossModule/Inputs/external-cascade/C.json | 2 +- test/Incremental/CrossModule/Inputs/linear/A.json | 2 +- test/Incremental/CrossModule/Inputs/linear/B.json | 2 +- test/Incremental/CrossModule/Inputs/linear/C.json | 2 +- test/Incremental/CrossModule/Inputs/transitive/A.json | 2 +- test/Incremental/CrossModule/Inputs/transitive/B.json | 2 +- test/Incremental/CrossModule/Inputs/transitive/C.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/A.json b/test/Incremental/CrossModule/Inputs/external-cascade/A.json index 50005aae33f05..c332d75dc3eb6 100644 --- a/test/Incremental/CrossModule/Inputs/external-cascade/A.json +++ b/test/Incremental/CrossModule/Inputs/external-cascade/A.json @@ -3,7 +3,7 @@ "object": "./A.o", "swift-dependencies": "./A.swiftdeps", "swiftmodule": "./A~partial.swiftmodule", - "swiftdoc": "./A.swiftdoc", + "swiftdoc": "./A.swiftdoc" }, "": { "swift-dependencies": "./A~buildrecord.swiftdeps" diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/B.json b/test/Incremental/CrossModule/Inputs/external-cascade/B.json index 30c08e2ae8a00..d367596bbde2c 100644 --- a/test/Incremental/CrossModule/Inputs/external-cascade/B.json +++ b/test/Incremental/CrossModule/Inputs/external-cascade/B.json @@ -3,7 +3,7 @@ "object": "./B.o", "swift-dependencies": "./B.swiftdeps", "swiftmodule": "./B~partial.swiftmodule", - "swiftdoc": "./B.swiftdoc", + "swiftdoc": "./B.swiftdoc" }, "": { "swift-dependencies": "./B~buildrecord.swiftdeps" diff --git a/test/Incremental/CrossModule/Inputs/external-cascade/C.json b/test/Incremental/CrossModule/Inputs/external-cascade/C.json index 34775828548e8..a0b5b832851a2 100644 --- a/test/Incremental/CrossModule/Inputs/external-cascade/C.json +++ b/test/Incremental/CrossModule/Inputs/external-cascade/C.json @@ -3,7 +3,7 @@ "object": "./C.o", "swift-dependencies": "./C.swiftdeps", "swiftmodule": "./C~partial.swiftmodule", - "swiftdoc": "./C.swiftdoc", + "swiftdoc": "./C.swiftdoc" }, "": { "swift-dependencies": "./C~buildrecord.swiftdeps" diff --git a/test/Incremental/CrossModule/Inputs/linear/A.json b/test/Incremental/CrossModule/Inputs/linear/A.json index 50005aae33f05..c332d75dc3eb6 100644 --- a/test/Incremental/CrossModule/Inputs/linear/A.json +++ b/test/Incremental/CrossModule/Inputs/linear/A.json @@ -3,7 +3,7 @@ "object": "./A.o", "swift-dependencies": "./A.swiftdeps", "swiftmodule": "./A~partial.swiftmodule", - "swiftdoc": "./A.swiftdoc", + "swiftdoc": "./A.swiftdoc" }, "": { "swift-dependencies": "./A~buildrecord.swiftdeps" diff --git a/test/Incremental/CrossModule/Inputs/linear/B.json b/test/Incremental/CrossModule/Inputs/linear/B.json index 30c08e2ae8a00..d367596bbde2c 100644 --- a/test/Incremental/CrossModule/Inputs/linear/B.json +++ b/test/Incremental/CrossModule/Inputs/linear/B.json @@ -3,7 +3,7 @@ "object": "./B.o", "swift-dependencies": "./B.swiftdeps", "swiftmodule": "./B~partial.swiftmodule", - "swiftdoc": "./B.swiftdoc", + "swiftdoc": "./B.swiftdoc" }, "": { "swift-dependencies": "./B~buildrecord.swiftdeps" diff --git a/test/Incremental/CrossModule/Inputs/linear/C.json b/test/Incremental/CrossModule/Inputs/linear/C.json index 34775828548e8..a0b5b832851a2 100644 --- a/test/Incremental/CrossModule/Inputs/linear/C.json +++ b/test/Incremental/CrossModule/Inputs/linear/C.json @@ -3,7 +3,7 @@ "object": "./C.o", "swift-dependencies": "./C.swiftdeps", "swiftmodule": "./C~partial.swiftmodule", - "swiftdoc": "./C.swiftdoc", + "swiftdoc": "./C.swiftdoc" }, "": { "swift-dependencies": "./C~buildrecord.swiftdeps" diff --git a/test/Incremental/CrossModule/Inputs/transitive/A.json b/test/Incremental/CrossModule/Inputs/transitive/A.json index 50005aae33f05..c332d75dc3eb6 100644 --- a/test/Incremental/CrossModule/Inputs/transitive/A.json +++ b/test/Incremental/CrossModule/Inputs/transitive/A.json @@ -3,7 +3,7 @@ "object": "./A.o", "swift-dependencies": "./A.swiftdeps", "swiftmodule": "./A~partial.swiftmodule", - "swiftdoc": "./A.swiftdoc", + "swiftdoc": "./A.swiftdoc" }, "": { "swift-dependencies": "./A~buildrecord.swiftdeps" diff --git a/test/Incremental/CrossModule/Inputs/transitive/B.json b/test/Incremental/CrossModule/Inputs/transitive/B.json index 30c08e2ae8a00..d367596bbde2c 100644 --- a/test/Incremental/CrossModule/Inputs/transitive/B.json +++ b/test/Incremental/CrossModule/Inputs/transitive/B.json @@ -3,7 +3,7 @@ "object": "./B.o", "swift-dependencies": "./B.swiftdeps", "swiftmodule": "./B~partial.swiftmodule", - "swiftdoc": "./B.swiftdoc", + "swiftdoc": "./B.swiftdoc" }, "": { "swift-dependencies": "./B~buildrecord.swiftdeps" diff --git a/test/Incremental/CrossModule/Inputs/transitive/C.json b/test/Incremental/CrossModule/Inputs/transitive/C.json index 34775828548e8..a0b5b832851a2 100644 --- a/test/Incremental/CrossModule/Inputs/transitive/C.json +++ b/test/Incremental/CrossModule/Inputs/transitive/C.json @@ -3,7 +3,7 @@ "object": "./C.o", "swift-dependencies": "./C.swiftdeps", "swiftmodule": "./C~partial.swiftmodule", - "swiftdoc": "./C.swiftdoc", + "swiftdoc": "./C.swiftdoc" }, "": { "swift-dependencies": "./C~buildrecord.swiftdeps" From bcd0cb6a0ab16f64759c72a81edce1ad9afaa588 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 19 Oct 2020 14:09:36 -0700 Subject: [PATCH 565/745] Use Relative Path to Test Inputs Unfortunately, OutputFileMap does not normalize input names when it is constructing keys. Instead, keys to the map are formed by appending the entry's value directly onto the working directory with the test host's path separator convention. On Windows, the tests here were using UNIX-style path separators when passing inputs to the Driver, so the key was "C:\Path\To\File\A.swift" But the input path to the driver was "C:\Path\To\File/A.swift" To work around this - and to ultimately make the test slightly more portable - just use relative paths to the input files since we've already changed directories to the working directory before we run these driver commands. --- .../CrossModule/external-cascade.swift | 21 ++++++++++--------- test/Incremental/CrossModule/linear.swift | 18 ++++++++-------- test/Incremental/CrossModule/transitive.swift | 18 ++++++++-------- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/test/Incremental/CrossModule/external-cascade.swift b/test/Incremental/CrossModule/external-cascade.swift index c3c68d9d4d75d..bd6d907d55808 100644 --- a/test/Incremental/CrossModule/external-cascade.swift +++ b/test/Incremental/CrossModule/external-cascade.swift @@ -16,18 +16,19 @@ // Set up a clean incremental build of all three modules // -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle %t/C.swift -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle C.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle B.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle A.swift // // Now change a header and ensure that the rebuild cascades outwards // -// RUN: touch -t 201401240006 %t/another-header.h -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s +// RUN: rm %t/another-header.h +// RUN: cp %S/Inputs/external-cascade/another-header.h %t/another-header.h +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s // MODULE-C: Job finished: {generate-pch: bridging-header-[[BRIDGING_HEADER:.*]].pch <= bridging-header.h} // MODULE-C: Job finished: {compile: C.o <= C.swift bridging-header-[[BRIDGING_HEADER]].pch} @@ -46,9 +47,9 @@ // And ensure that the null build really is null. // -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -import-objc-header %t/bridging-header.h -Xfrontend -validate-tbd-against-ir=none -driver-show-incremental -driver-show-job-lifecycle C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s // MODULE-C-NULL: Job finished: {generate-pch: bridging-header-[[BRIDGING_HEADER:.*]].pch <= bridging-header.h} // MODULE-C-NULL: Job skipped: {compile: C.o <= C.swift bridging-header-[[BRIDGING_HEADER]].pch} diff --git a/test/Incremental/CrossModule/linear.swift b/test/Incremental/CrossModule/linear.swift index 4f4445a462d50..1679cd5213496 100644 --- a/test/Incremental/CrossModule/linear.swift +++ b/test/Incremental/CrossModule/linear.swift @@ -16,17 +16,17 @@ // Set up a clean incremental build of all three modules // -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DOLD %t/C.swift -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DOLD C.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle B.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle A.swift // // Now change C and ensure that B rebuilds but A does not // -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DNEW C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s // MODULE-C: Incremental compilation has been disabled @@ -41,9 +41,9 @@ // And ensure that the null build really is null. // -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DNEW C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s // MODULE-C-NULL: Job skipped: {compile: C.o <= C.swift} // MODULE-C-NULL: Job skipped: {merge-module: C.swiftmodule <= C.o} diff --git a/test/Incremental/CrossModule/transitive.swift b/test/Incremental/CrossModule/transitive.swift index 8b1255245a569..6c585d1fbf63d 100644 --- a/test/Incremental/CrossModule/transitive.swift +++ b/test/Incremental/CrossModule/transitive.swift @@ -17,18 +17,18 @@ // Set up a clean incremental build of all three modules // -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DOLD %t/C.swift -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/B.swift -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/A.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DOLD C.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC B.swift +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC A.swift // // Now change C and ensure that B and A rebuild because of the change to // an incremental external dependency. // -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DNEW C.swift 2>&1 | %FileCheck -check-prefix MODULE-C %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC B.swift 2>&1 | %FileCheck -check-prefix MODULE-B %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC A.swift 2>&1 | %FileCheck -check-prefix MODULE-A %s // MODULE-C: Incremental compilation has been disabled @@ -44,9 +44,9 @@ // And ensure that the null build really is null. // -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s -// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DNEW C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s +// RUN: cd %t && %target-swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s // MODULE-C-NULL: Job skipped: {compile: C.o <= C.swift} // MODULE-C-NULL: Job skipped: {merge-module: C.swiftmodule <= C.o} From f0a59a25eae63bc140563c4044e580b7e9be0339 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Fri, 16 Oct 2020 22:00:39 -0700 Subject: [PATCH 566/745] [silgen] Ensure that cleanup cloner clones formal access cleanups to formal access cleanups. This has been a long standing issue that we have been hacking around in various points in SILGen. Now CleanupCloner just does the right thing. I was unable to cause any issues to pop up in tree (since I believe we hacked around this and converged on correctness). But it does come up in combination with new code in https://github.com/apple/swift/pull/31779. rdar://63514765 --- lib/SILGen/Cleanup.cpp | 18 ++++++++++++++---- lib/SILGen/Cleanup.h | 17 +++++++++++++---- lib/SILGen/SILGenDecl.cpp | 6 ++++-- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/lib/SILGen/Cleanup.cpp b/lib/SILGen/Cleanup.cpp index 20eb0cc3c8e7e..868a0f1c6ecab 100644 --- a/lib/SILGen/Cleanup.cpp +++ b/lib/SILGen/Cleanup.cpp @@ -359,14 +359,16 @@ void CleanupStateRestorationScope::pop() && { popImpl(); } //===----------------------------------------------------------------------===// CleanupCloner::CleanupCloner(SILGenFunction &SGF, const ManagedValue &mv) - : SGF(SGF), hasCleanup(mv.hasCleanup()), isLValue(mv.isLValue()), - writebackBuffer(None) { + : SGF(SGF), writebackBuffer(None), hasCleanup(mv.hasCleanup()), + isLValue(mv.isLValue()), isFormalAccess(false) { if (hasCleanup) { auto handle = mv.getCleanup(); auto state = SGF.Cleanups.getFlagsAndWritebackBuffer(handle); - if (SILValue value = std::get<1>(state).getValueOr(SILValue())) { + using RawTy = std::underlying_type::type; + if (RawTy(std::get<0>(state)) & RawTy(Cleanup::Flags::FormalAccessCleanup)) + isFormalAccess = true; + if (SILValue value = std::get<1>(state).getValueOr(SILValue())) writebackBuffer = value; - } } } @@ -405,8 +407,16 @@ ManagedValue CleanupCloner::clone(SILValue value) const { } if (value->getType().isAddress()) { + if (isFormalAccess) { + auto loc = RegularLocation::getAutoGeneratedLocation(); + return SGF.emitFormalAccessManagedBufferWithCleanup(loc, value); + } return SGF.emitManagedBufferWithCleanup(value); } + if (isFormalAccess) { + auto loc = RegularLocation::getAutoGeneratedLocation(); + return SGF.emitFormalAccessManagedRValueWithCleanup(loc, value); + } return SGF.emitManagedRValueWithCleanup(value); } diff --git a/lib/SILGen/Cleanup.h b/lib/SILGen/Cleanup.h index cf8b98df606e3..e2361797d79d6 100644 --- a/lib/SILGen/Cleanup.h +++ b/lib/SILGen/Cleanup.h @@ -84,9 +84,9 @@ class LLVM_LIBRARY_VISIBILITY Cleanup { // // Example: Distinguishing in between @owned cleanups with a writeback buffer // (ExclusiveBorrowCleanup) or ones that involve formal access cleanups. - enum class Flags : uint8_t { + enum Flags : uint8_t { None = 0, - ExclusiveBorrowCleanup = 1, + FormalAccessCleanup = 1, }; private: @@ -95,7 +95,7 @@ class LLVM_LIBRARY_VISIBILITY Cleanup { Flags flags : 8; protected: - Cleanup() {} + Cleanup() : flags(Flags::None) {} virtual ~Cleanup() {} public: @@ -123,6 +123,14 @@ class LLVM_LIBRARY_VISIBILITY Cleanup { virtual bool getWritebackBuffer(function_ref func) { return false; } + + bool isFormalAccess() const { + return getFlags() & Flags::FormalAccessCleanup; + } + + void setIsFormalAccess() { + flags = Flags(flags | Flags::FormalAccessCleanup); + } }; /// A cleanup depth is generally used to denote the set of cleanups @@ -310,9 +318,10 @@ class CleanupStateRestorationScope { /// writeback buffers. class CleanupCloner { SILGenFunction &SGF; + Optional writebackBuffer; bool hasCleanup; bool isLValue; - Optional writebackBuffer; + bool isFormalAccess; public: CleanupCloner(SILGenFunction &SGF, const ManagedValue &mv); diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index 41de4a930cd88..6d89e8e192733 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -1431,10 +1431,12 @@ SILGenFunction::enterDormantTemporaryCleanup(SILValue addr, namespace { -struct FormalAccessReleaseValueCleanup : Cleanup { +struct FormalAccessReleaseValueCleanup final : Cleanup { FormalEvaluationContext::stable_iterator Depth; - FormalAccessReleaseValueCleanup() : Depth() {} + FormalAccessReleaseValueCleanup() : Cleanup(), Depth() { + setIsFormalAccess(); + } void setState(SILGenFunction &SGF, CleanupState newState) override { if (newState == CleanupState::Dead) { From 849e9d660f502fd67352305220ef83170e47a21d Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Mon, 12 Oct 2020 16:23:38 -0700 Subject: [PATCH 567/745] fix diagnostic messages that said '@actorIsolated' for @actorIndependent --- include/swift/AST/DiagnosticsSema.def | 20 ++++++++++---------- lib/Sema/TypeCheckAttr.cpp | 10 +++++----- test/attr/actorindependent.swift | 12 ++++++------ 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 6feed045acaf1..328a7fc06ba72 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4207,22 +4207,22 @@ ERROR(global_actor_isolated_requirement_witness_conflict,none, "requirement from protocol %3 isolated to global actor %4", (DescriptiveDeclKind, DeclName, Type, Identifier, Type)) -ERROR(actorisolated_let,none, - "'@actorIsolated' is meaningless on 'let' declarations because " +ERROR(actorindependent_let,none, + "'@actorIndependent' is meaningless on 'let' declarations because " "they are immutable", ()) -ERROR(actorisolated_mutable_storage,none, - "'@actorIsolated' can not be applied to stored properties", +ERROR(actorindependent_mutable_storage,none, + "'@actorIndependent' can not be applied to stored properties", ()) -ERROR(actorisolated_local_var,none, - "'@actorIsolated' can not be applied to local variables", +ERROR(actorindependent_local_var,none, + "'@actorIndependent' can not be applied to local variables", ()) -ERROR(actorisolated_not_actor_member,none, - "'@actorIsolated' can only be applied to actor members and " +ERROR(actorindependent_not_actor_member,none, + "'@actorIndependent' can only be applied to actor members and " "global/static variables", ()) -ERROR(actorisolated_not_actor_instance_member,none, - "'@actorIsolated' can only be applied to instance members of actors", +ERROR(actorindependent_not_actor_instance_member,none, + "'@actorIndependent' can only be applied to instance members of actors", ()) ERROR(concurrency_lib_missing,none, diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 6bc7a1e2babb5..111a28cf17f3c 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -286,19 +286,19 @@ class AttributeChecker : public AttributeVisitor { if (auto var = dyn_cast(D)) { // @actorIndependent is meaningless on a `let`. if (var->isLet()) { - diagnoseAndRemoveAttr(attr, diag::actorisolated_let); + diagnoseAndRemoveAttr(attr, diag::actorindependent_let); return; } // @actorIndependent can not be applied to stored properties. if (var->hasStorage()) { - diagnoseAndRemoveAttr(attr, diag::actorisolated_mutable_storage); + diagnoseAndRemoveAttr(attr, diag::actorindependent_mutable_storage); return; } // @actorIndependent can not be applied to local properties. if (dc->isLocalContext()) { - diagnoseAndRemoveAttr(attr, diag::actorisolated_local_var); + diagnoseAndRemoveAttr(attr, diag::actorindependent_local_var); return; } @@ -315,14 +315,14 @@ class AttributeChecker : public AttributeVisitor { // @actorIndependent only makes sense on an actor instance member. if (!dc->getSelfClassDecl() || !dc->getSelfClassDecl()->isActor()) { - diagnoseAndRemoveAttr(attr, diag::actorisolated_not_actor_member); + diagnoseAndRemoveAttr(attr, diag::actorindependent_not_actor_member); return; } auto VD = cast(D); if (!VD->isInstanceMember()) { diagnoseAndRemoveAttr( - attr, diag::actorisolated_not_actor_instance_member); + attr, diag::actorindependent_not_actor_instance_member); return; } diff --git a/test/attr/actorindependent.swift b/test/attr/actorindependent.swift index 4cc8d3ef45acb..84abf990d6c33 100644 --- a/test/attr/actorindependent.swift +++ b/test/attr/actorindependent.swift @@ -1,6 +1,6 @@ // RUN: %target-swift-frontend -typecheck -verify %s -enable-experimental-concurrency -// expected-error@+1{{'@actorIsolated' can only be applied to actor members and global/static variables}} +// expected-error@+1{{'@actorIndependent' can only be applied to actor members and global/static variables}} @actorIndependent func globalFunction() { } @actorIndependent var globalComputedProperty1: Int { 17 } @@ -10,7 +10,7 @@ set { } } -// expected-error@+1{{'@actorIsolated' can not be applied to stored properties}} +// expected-error@+1{{'@actorIndependent' can not be applied to stored properties}} @actorIndependent var globalStoredProperty: Int = 17 struct X { @@ -25,13 +25,13 @@ struct X { set { } } - // expected-error@+1{{'@actorIsolated' can not be applied to stored properties}} + // expected-error@+1{{'@actorIndependent' can not be applied to stored properties}} @actorIndependent static var storedStaticProperty: Int = 17 } class C { - // expected-error@+1{{'@actorIsolated' can only be applied to actor members and global/static variables}} + // expected-error@+1{{'@actorIndependent' can only be applied to actor members and global/static variables}} @actorIndependent var property3: Int { 5 } } @@ -39,7 +39,7 @@ class C { actor class A { var property: Int = 5 - // expected-error@+1{{'@actorIsolated' can not be applied to stored properties}} + // expected-error@+1{{'@actorIndependent' can not be applied to stored properties}} @actorIndependent var property2: Int = 5 @@ -72,6 +72,6 @@ actor class A { @actorIndependent subscript(index: Int) -> String { "\(index)" } - // expected-error@+1{{'@actorIsolated' can only be applied to instance members of actors}} + // expected-error@+1{{'@actorIndependent' can only be applied to instance members of actors}} @actorIndependent static func staticFunc() { } } From 34d22105b88236ea0a37b89c4dd0580eb11ac526 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Mon, 12 Oct 2020 18:44:37 -0700 Subject: [PATCH 568/745] implemented parsing and typechecking for @actorIndependent(unsafe) [broken] first impl of @actorIndependent in the type checker. [broken] fixed mistake in my parsing code wrt invalid source range [broken] found another spot where ActorIndependent needs custom handling [broken] incomplete set of @actorIndependent(unsafe) tests updates to ActorIndependentUnsafe [fixed] add FIXME plus simple handling of IndependentUnsafe context finished @actorIndependent(unsafe) regression tests added wip serialization / deserialization test focus test to just one actor class round-trip serialize/deserialize test for @actorIndependent serialize -> deserialize -> serialize -> compare to original most of doug's comments addressed robert's comments fix printing bug; add module printing to regression test [nfc] update comment for ActorIsolation::IndependentUnsafe --- include/swift/AST/ActorIsolation.h | 20 +- include/swift/AST/Attr.def | 2 +- include/swift/AST/Attr.h | 23 ++ include/swift/AST/AttrKind.h | 11 + include/swift/AST/DiagnosticsParse.def | 14 +- lib/AST/Attr.cpp | 10 + lib/AST/DiagnosticEngine.cpp | 4 + lib/AST/TypeCheckRequests.cpp | 6 + lib/Parse/ParseDecl.cpp | 53 ++++- lib/Sema/DerivedConformanceActor.cpp | 7 +- lib/Sema/TypeCheckAttr.cpp | 13 +- lib/Sema/TypeCheckConcurrency.cpp | 15 +- lib/Sema/TypeCheckProtocol.cpp | 1 + lib/Serialization/Deserialization.cpp | 8 + lib/Serialization/ModuleFormat.h | 7 +- lib/Serialization/Serialization.cpp | 8 + .../Serialization/attr-actorindependent.swift | 43 ++++ test/attr/actorindependent_unsafe.swift | 211 ++++++++++++++++++ 18 files changed, 425 insertions(+), 31 deletions(-) create mode 100644 test/Serialization/attr-actorindependent.swift create mode 100644 test/attr/actorindependent_unsafe.swift diff --git a/include/swift/AST/ActorIsolation.h b/include/swift/AST/ActorIsolation.h index c5ba69ba3f156..f819345615a60 100644 --- a/include/swift/AST/ActorIsolation.h +++ b/include/swift/AST/ActorIsolation.h @@ -48,6 +48,11 @@ class ActorIsolation { /// meaning that it can be used from any actor but is also unable to /// refer to the isolated state of any given actor. Independent, + /// The declaration is explicitly specified to be independent of any actor, + /// but the programmer promises to protect the declaration from concurrent + /// accesses manually. Thus, it is okay if this declaration is a mutable + /// variable that creates storage. + IndependentUnsafe, /// The declaration is isolated to a global actor. It can refer to other /// entities with the same global actor. GlobalActor, @@ -70,8 +75,18 @@ class ActorIsolation { return ActorIsolation(Unspecified, nullptr); } - static ActorIsolation forIndependent() { - return ActorIsolation(Independent, nullptr); + static ActorIsolation forIndependent(ActorIndependentKind indepKind) { + ActorIsolation::Kind isoKind; + switch (indepKind) { + case ActorIndependentKind::Safe: + isoKind = Independent; + break; + + case ActorIndependentKind::Unsafe: + isoKind = IndependentUnsafe; + break; + } + return ActorIsolation(isoKind, nullptr); } static ActorIsolation forActorInstance(ClassDecl *actor) { @@ -112,6 +127,7 @@ class ActorIsolation { switch (lhs.kind) { case Independent: + case IndependentUnsafe: case Unspecified: return true; diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 20427b356c81b..ef0eef652711d 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -571,7 +571,7 @@ CONTEXTUAL_SIMPLE_DECL_ATTR(actor, Actor, APIBreakingToAdd | APIBreakingToRemove, 102) -SIMPLE_DECL_ATTR(actorIndependent, ActorIndependent, +DECL_ATTR(actorIndependent, ActorIndependent, OnFunc | OnVar | OnSubscript | ConcurrencyOnly | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove, diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index b442cb8cf934a..709c1d62b36e8 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -290,6 +290,10 @@ class DeclAttribute : public AttributeBase { kind : NumInlineKindBits ); + SWIFT_INLINE_BITFIELD(ActorIndependentAttr, DeclAttribute, NumActorIndependentKindBits, + kind : NumActorIndependentKindBits + ); + SWIFT_INLINE_BITFIELD(OptimizeAttr, DeclAttribute, NumOptimizationModeBits, mode : NumOptimizationModeBits ); @@ -1329,6 +1333,25 @@ class ReferenceOwnershipAttr : public DeclAttribute { } }; +/// Represents an actorIndependent/actorIndependent(unsafe) decl attribute. +class ActorIndependentAttr : public DeclAttribute { +public: + ActorIndependentAttr(SourceLoc atLoc, SourceRange range, ActorIndependentKind kind) + : DeclAttribute(DAK_ActorIndependent, atLoc, range, /*Implicit=*/false) { + Bits.ActorIndependentAttr.kind = unsigned(kind); + } + + ActorIndependentAttr(ActorIndependentKind kind, bool IsImplicit=false) + : ActorIndependentAttr(SourceLoc(), SourceRange(), kind) { + setImplicit(IsImplicit); + } + + ActorIndependentKind getKind() const { return ActorIndependentKind(Bits.ActorIndependentAttr.kind); } + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_ActorIndependent; + } +}; + /// Defines the attribute that we use to model documentation comments. class RawDocCommentAttr : public DeclAttribute { /// Source range of the attached comment. This comment is located before diff --git a/include/swift/AST/AttrKind.h b/include/swift/AST/AttrKind.h index be74f2a3b472e..7ca1a8153fdd0 100644 --- a/include/swift/AST/AttrKind.h +++ b/include/swift/AST/AttrKind.h @@ -79,6 +79,17 @@ enum class InlineKind : uint8_t { enum : unsigned { NumInlineKindBits = countBitsUsed(static_cast(InlineKind::Last_InlineKind)) }; + +/// Indicates whether an actorIndependent decl is unsafe or not +enum class ActorIndependentKind : uint8_t { + Safe = 0, + Unsafe = 1, + Last_InlineKind = Unsafe +}; + +enum : unsigned { NumActorIndependentKindBits = + countBitsUsed(static_cast(ActorIndependentKind::Last_InlineKind)) }; + /// This enum represents the possible values of the @_effects attribute. /// These values are ordered from the strongest guarantee to the weakest, /// so please do not reorder existing values. diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index be7c1d83ee465..afc99fd256b2b 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1358,6 +1358,12 @@ ERROR(attr_expected_comma,none, ERROR(attr_expected_string_literal,none, "expected string literal in '%0' attribute", (StringRef)) +ERROR(attr_expected_option_such_as,none, + "expected '%0' option such as '%1'", (StringRef, StringRef)) + +ERROR(attr_unknown_option,none, + "unknown option '%0' for attribute '%1'", (StringRef, StringRef)) + ERROR(attr_missing_label,PointsToFirstBadToken, "missing label '%0:' in '@%1' attribute", (StringRef, StringRef)) ERROR(attr_expected_label,none, @@ -1533,17 +1539,9 @@ ERROR(opened_attribute_id_value,none, ERROR(opened_attribute_expected_rparen,none, "expected ')' after id value for 'opened' attribute", ()) -// inline, optimize -ERROR(optimization_attribute_expect_option,none, - "expected '%0' option such as '%1'", (StringRef, StringRef)) -ERROR(optimization_attribute_unknown_option,none, - "unknown option '%0' for attribute '%1'", (StringRef, StringRef)) - // effects ERROR(effects_attribute_expect_option,none, "expected '%0' option (readnone, readonly, readwrite)", (StringRef)) -ERROR(effects_attribute_unknown_option,none, - "unknown option '%0' for attribute '%1'", (StringRef, StringRef)) // unowned ERROR(attr_unowned_invalid_specifier,none, diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 8920fd7c6c59c..0730b4929b447 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -769,6 +769,7 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, case DAK_ReferenceOwnership: case DAK_Effects: case DAK_Optimize: + case DAK_ActorIndependent: if (DeclAttribute::isDeclModifier(getKind())) { Printer.printKeyword(getAttrName(), Options); } else { @@ -1136,6 +1137,15 @@ StringRef DeclAttribute::getAttrName() const { } llvm_unreachable("Invalid inline kind"); } + case DAK_ActorIndependent: { + switch (cast(this)->getKind()) { + case ActorIndependentKind::Safe: + return "actorIndependent"; + case ActorIndependentKind::Unsafe: + return "actorIndependent(unsafe)"; + } + llvm_unreachable("Invalid actorIndependent kind"); + } case DAK_Optimize: { switch (cast(this)->getMode()) { case OptimizationMode::NoOptimization: diff --git a/lib/AST/DiagnosticEngine.cpp b/lib/AST/DiagnosticEngine.cpp index 7116f3517ac3f..4da5833525b55 100644 --- a/lib/AST/DiagnosticEngine.cpp +++ b/lib/AST/DiagnosticEngine.cpp @@ -676,6 +676,10 @@ static void formatDiagnosticArgument(StringRef Modifier, Out << "actor-independent"; break; + case ActorIsolation::IndependentUnsafe: + Out << "actor-independent-unsafe"; + break; + case ActorIsolation::Unspecified: Out << "non-actor-isolated"; break; diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 5626e803ce416..7ea74ca376f4c 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1495,6 +1495,7 @@ bool ActorIsolation::requiresSubstitution() const { switch (kind) { case ActorInstance: case Independent: + case IndependentUnsafe: case Unspecified: return false; @@ -1508,6 +1509,7 @@ ActorIsolation ActorIsolation::subst(SubstitutionMap subs) const { switch (kind) { case ActorInstance: case Independent: + case IndependentUnsafe: case Unspecified: return *this; @@ -1528,6 +1530,10 @@ void swift::simple_display( out << "actor-independent"; break; + case ActorIsolation::IndependentUnsafe: + out << "actor-independent (unsafe)"; + break; + case ActorIsolation::Unspecified: out << "unspecified actor isolation"; break; diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index f4a7b9632070f..cd719bc0beafc 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1618,7 +1618,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, else if (Tok.getText() == "releasenone") kind = EffectsKind::ReleaseNone; else { - diagnose(Loc, diag::effects_attribute_unknown_option, + diagnose(Loc, diag::attr_unknown_option, Tok.getText(), AttrName); return false; } @@ -1644,8 +1644,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, } if (Tok.isNot(tok::identifier)) { - diagnose(Loc, diag::optimization_attribute_expect_option, AttrName, - "none"); + diagnose(Loc, diag::attr_expected_option_such_as, AttrName, "none"); return false; } @@ -1655,8 +1654,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, else if (Tok.getText() == "__always") kind = InlineKind::Always; else { - diagnose(Loc, diag::optimization_attribute_unknown_option, - Tok.getText(), AttrName); + diagnose(Loc, diag::attr_unknown_option, Tok.getText(), AttrName); return false; } consumeToken(tok::identifier); @@ -1674,6 +1672,45 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, break; } + case DAK_ActorIndependent: { + // if no option is provided, then it's the 'safe' version. + if (!consumeIf(tok::l_paren)) { + if (!DiscardAttribute) { + AttrRange = SourceRange(Loc, Tok.getRange().getStart()); + Attributes.add(new (Context) ActorIndependentAttr(AtLoc, AttrRange, + ActorIndependentKind::Safe)); + } + break; + } + + // otherwise, make sure it looks like an identifier. + if (Tok.isNot(tok::identifier)) { + diagnose(Loc, diag::attr_expected_option_such_as, AttrName, "unsafe"); + return false; + } + + // make sure the identifier is 'unsafe' + if (Tok.getText() != "unsafe") { + diagnose(Loc, diag::attr_unknown_option, Tok.getText(), AttrName); + return false; + } + + consumeToken(tok::identifier); + AttrRange = SourceRange(Loc, Tok.getRange().getStart()); + + if (!consumeIf(tok::r_paren)) { + diagnose(Loc, diag::attr_expected_rparen, AttrName, + DeclAttribute::isDeclModifier(DK)); + return false; + } + + if (!DiscardAttribute) + Attributes.add(new (Context) ActorIndependentAttr(AtLoc, AttrRange, + ActorIndependentKind::Unsafe)); + + break; + } + case DAK_Optimize: { if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, @@ -1682,8 +1719,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, } if (Tok.isNot(tok::identifier)) { - diagnose(Loc, diag::optimization_attribute_expect_option, AttrName, - "speed"); + diagnose(Loc, diag::attr_expected_option_such_as, AttrName, "speed"); return false; } @@ -1695,8 +1731,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, else if (Tok.getText() == "size") optMode = OptimizationMode::ForSize; else { - diagnose(Loc, diag::optimization_attribute_unknown_option, - Tok.getText(), AttrName); + diagnose(Loc, diag::attr_unknown_option, Tok.getText(), AttrName); return false; } consumeToken(tok::identifier); diff --git a/lib/Sema/DerivedConformanceActor.cpp b/lib/Sema/DerivedConformanceActor.cpp index 27b26bfecc0d9..ad826a7734a01 100644 --- a/lib/Sema/DerivedConformanceActor.cpp +++ b/lib/Sema/DerivedConformanceActor.cpp @@ -236,10 +236,9 @@ static ValueDecl *deriveActor_enqueuePartialTask(DerivedConformance &derived) { func->copyFormalAccessFrom(derived.Nominal); func->setBodySynthesizer(deriveBodyActor_enqueuePartialTask); func->setSynthesized(); - - // FIXME: This function should be "actor-unsafe", not "actor-independent", but - // the latter is all we have at the moment. - func->getAttrs().add(new (ctx) ActorIndependentAttr(/*IsImplicit=*/true)); + // mark as @actorIndependent(unsafe) + func->getAttrs().add(new (ctx) ActorIndependentAttr( + ActorIndependentKind::Unsafe, /*IsImplicit=*/true)); // Actor storage property and its initialization. auto actorStorage = new (ctx) VarDecl( diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 111a28cf17f3c..78814b1a5da46 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -290,10 +290,17 @@ class AttributeChecker : public AttributeVisitor { return; } - // @actorIndependent can not be applied to stored properties. + // @actorIndependent can not be applied to stored properties, unless if + // the 'unsafe' option was specified if (var->hasStorage()) { - diagnoseAndRemoveAttr(attr, diag::actorindependent_mutable_storage); - return; + switch (attr->getKind()) { + case ActorIndependentKind::Safe: + diagnoseAndRemoveAttr(attr, diag::actorindependent_mutable_storage); + return; + + case ActorIndependentKind::Unsafe: + break; + } } // @actorIndependent can not be applied to local properties. diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index db405f7095e0c..379add233a823 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -501,6 +501,7 @@ ActorIsolationRestriction ActorIsolationRestriction::forDeclaration( } case ActorIsolation::Independent: + case ActorIsolation::IndependentUnsafe: // Actor-independent have no restrictions on their access. return forUnrestricted(); @@ -758,6 +759,7 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { switch (auto isolation = swift::getActorIsolation(value)) { case ActorIsolation::ActorInstance: case ActorIsolation::Independent: + case ActorIsolation::IndependentUnsafe: case ActorIsolation::Unspecified: return isolation; @@ -803,7 +805,8 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { return ActorIsolation::forUnspecified(); } - return ActorIsolation::forIndependent(); + // At module scope, actor independence with safety is assumed. + return ActorIsolation::forIndependent(ActorIndependentKind::Safe); } /// Check a reference to an entity within a global actor. @@ -833,6 +836,7 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { } case ActorIsolation::Independent: + case ActorIsolation::IndependentUnsafe: ctx.Diags.diagnose( loc, diag::global_actor_from_independent_context, value->getDescriptiveKind(), value->getName(), globalActor); @@ -933,6 +937,7 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) { } break; + case ActorIsolation::IndependentUnsafe: case ActorIsolation::Unspecified: // Okay break; @@ -1028,7 +1033,7 @@ static Optional getIsolationFromAttributes(Decl *decl) { // If the declaration is explicitly marked @actorIndependent, report it as // independent. if (independentAttr) { - return ActorIsolation::forIndependent(); + return ActorIsolation::forIndependent(independentAttr->getKind()); } // If the declaration is marked with a global actor, report it as being @@ -1105,6 +1110,7 @@ static Optional getIsolationFromWitnessedRequirements( llvm_unreachable("protocol requirements cannot be actor instances"); case ActorIsolation::Independent: + case ActorIsolation::IndependentUnsafe: // We only need one @actorIndependent. if (sawActorIndependent) return true; @@ -1168,8 +1174,11 @@ ActorIsolation ActorIsolationRequest::evaluate( // inferred, so that (e.g.) it will be printed and serialized. ASTContext &ctx = value->getASTContext(); switch (inferred) { + // FIXME: if the context is 'unsafe', is it fine to infer the 'safe' one? + case ActorIsolation::IndependentUnsafe: case ActorIsolation::Independent: - value->getAttrs().add(new (ctx) ActorIndependentAttr(true)); + value->getAttrs().add(new (ctx) ActorIndependentAttr( + ActorIndependentKind::Safe, /*IsImplicit=*/true)); break; case ActorIsolation::GlobalActor: { diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 01cf489a25a6b..7873b0ad7696d 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -2715,6 +2715,7 @@ bool ConformanceChecker::checkActorIsolation( } case ActorIsolation::Independent: + case ActorIsolation::IndependentUnsafe: case ActorIsolation::Unspecified: break; } diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 0d853f04053b9..e9a2b2d030b14 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -4136,6 +4136,14 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { break; } + case decls_block::ActorIndependent_DECL_ATTR: { + unsigned kind; + serialization::decls_block::ActorIndependentDeclAttrLayout::readRecord( + scratch, kind); + Attr = new (ctx) ActorIndependentAttr((ActorIndependentKind)kind); + break; + } + case decls_block::Optimize_DECL_ATTR: { unsigned kind; serialization::decls_block::OptimizeDeclAttrLayout::readRecord( diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 5c35093f2a9b8..d7257e3f490d4 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 582; // sil specialize attribute module parameter +const uint16_t SWIFTMODULE_VERSION_MINOR = 583; // @actorIndependent safe/unsafe attribute /// A standard hash seed used for all string hashes in a serialized module. /// @@ -1807,6 +1807,11 @@ namespace decls_block { BCFixed<2> // inline value >; + using ActorIndependentDeclAttrLayout = BCRecordLayout< + ActorIndependent_DECL_ATTR, + BCFixed<1> // unsafe flag + >; + using OptimizeDeclAttrLayout = BCRecordLayout< Optimize_DECL_ATTR, BCFixed<2> // optimize value diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 5c81c6b5855d6..dc881ddb61732 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2294,6 +2294,14 @@ class Serializer::DeclSerializer : public DeclVisitor { return; } + case DAK_ActorIndependent: { + auto *theAttr = cast(DA); + auto abbrCode = S.DeclTypeAbbrCodes[ActorIndependentDeclAttrLayout::Code]; + ActorIndependentDeclAttrLayout::emitRecord(S.Out, S.ScratchRecord, + abbrCode, (unsigned)theAttr->getKind()); + return; + } + case DAK_Optimize: { auto *theAttr = cast(DA); auto abbrCode = S.DeclTypeAbbrCodes[OptimizeDeclAttrLayout::Code]; diff --git a/test/Serialization/attr-actorindependent.swift b/test/Serialization/attr-actorindependent.swift new file mode 100644 index 0000000000000..a7a26c7d6ad6f --- /dev/null +++ b/test/Serialization/attr-actorindependent.swift @@ -0,0 +1,43 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -enable-experimental-concurrency -emit-module-path %t/a.swiftmodule -module-name a %s +// RUN: llvm-bcanalyzer -dump %t/a.swiftmodule | %FileCheck -check-prefix BC-CHECK %s +// RUN: %target-swift-ide-test -print-module -module-to-print a -source-filename x -I %t | %FileCheck -check-prefix MODULE-CHECK %s +// RUN: %target-swift-frontend -enable-experimental-concurrency -emit-module-path %t/b.swiftmodule -module-name a %t/a.swiftmodule +// RUN: cmp -s %t/a.swiftmodule %t/b.swiftmodule + +/////////// +// This test checks for correct serialization & deserialization of +// @actorIndependent and @actorIndependent(unsafe) + +// look for correct annotation after first deserialization's module print: + +// MODULE-CHECK: actor class UnsafeCounter { +// MODULE-CHECK-NEXT: @actorIndependent(unsafe) var storage: Int +// MODULE-CHECK-NEXT: @actorIndependent var count: Int +// MODULE-CHECK-NEXT: var actorCount: Int +// MODULE-CHECK-NEXT: init() +// MODULE-CHECK-NEXT: } + +// and look for unsafe and safe versions of decl in BC dump: + +// BC-CHECK-NOT: UnknownCode +// BC-CHECK: +// BC-CHECK: + + +actor class UnsafeCounter { + + @actorIndependent(unsafe) + private var storage : Int = 0 + + @actorIndependent + var count : Int { + get { storage } + set { storage = newValue } + } + + var actorCount : Int { + get { storage } + set { storage = newValue } + } +} diff --git a/test/attr/actorindependent_unsafe.swift b/test/attr/actorindependent_unsafe.swift new file mode 100644 index 0000000000000..b33da2ab0165e --- /dev/null +++ b/test/attr/actorindependent_unsafe.swift @@ -0,0 +1,211 @@ +// RUN: %target-swift-frontend -typecheck -verify %s -enable-experimental-concurrency + +////////////////////////// +/// Cases that only work because of @actorIndependent(unsafe) +////////////////////////// + +////////////////////////// +// 1 -- basic unsafe methods / properties accessing var member without annotation +actor class Actor1 { + var counter : Int = 42 + + @actorIndependent(unsafe) + func aMethod() { + counter += 1 + } + + @actorIndependent(unsafe) + var computedProp : Int { counter / 2 } +} + +let a1 = Actor1() +let _ = a1.aMethod() +let _ = a1.computedProp == a1.computedProp + + +////////////////////////// +// 2 -- more unsafe methods / properties accessing var member without annotation +actor class WeatherActor1 { + var tempCelsius : Double = 5.0 + + var tempFahrenheit : Double { + get { (1.8 * tempCelsius) + 32.0 } + set { tempCelsius = (newValue - 32.0) / 1.8 } + } + + @actorIndependent(unsafe) + var tempCelsiusUnsafe : Double { + get { tempCelsius } + set { tempCelsius = newValue } + } + + @actorIndependent + var tempCelsiusPretendSafe : Double { + get { tempCelsiusUnsafe } + set { tempCelsiusUnsafe = newValue } + } + + @actorIndependent(unsafe) + var tempCelsiusUnsafe2 : Double { + get { tempCelsius } + set { tempCelsius = newValue } + } + + @actorIndependent(unsafe) + func getTempFahrenheitUnsafe() -> Double { + return tempFahrenheit + } + + @actorIndependent(unsafe) + func setTempFahrenheitUnsafe(_ newValue : Double) { + tempFahrenheit = newValue + } +} + +let wa1 = WeatherActor1() +let _ = wa1.setTempFahrenheitUnsafe(wa1.getTempFahrenheitUnsafe()) +wa1.tempCelsiusUnsafe = wa1.tempCelsiusUnsafe + 1 +wa1.tempCelsiusPretendSafe = wa1.tempCelsiusUnsafe +wa1.tempCelsiusUnsafe2 = wa1.tempCelsiusUnsafe2 + 2 + + +////////////////////////// +// 3 -- basic actorIndependent accessing actorIndependent(unsafe) member +actor class Actor2 { + + @actorIndependent(unsafe) + var counter : Int = 42 + + @actorIndependent + func aMethod() { + counter += 1 + } + + @actorIndependent + var computedProp : Int { counter / 2 } +} + +let a2 = Actor2() +let _ = a2.aMethod() +let _ = a2.computedProp + + +////////////////////////// +// 4 -- more actorIndependent accessing actorIndependent(unsafe) member +actor class WeatherActor2 { + @actorIndependent(unsafe) + var tempCelsius : Double = 5.0 + + @actorIndependent + var tempFahrenheit : Double { + get { (1.8 * tempCelsius) + 32.0 } + set { tempCelsius = (newValue - 32.0) / 1.8 } + } + + @actorIndependent + var tempCelsiusUnsafe : Double { + get { tempCelsius } + set { tempCelsius = newValue } + } + + @actorIndependent + var tempCelsiusUnsafe2 : Double { + get { tempCelsiusUnsafe } + set { tempCelsiusUnsafe = newValue } + } + + @actorIndependent + func getTempFahrenheitUnsafe() -> Double { + return tempFahrenheit + } + + @actorIndependent + func setTempFahrenheitUnsafe(_ newValue : Double) { + tempFahrenheit = newValue + } +} + +let wa2 = WeatherActor2() +let _ = wa2.setTempFahrenheitUnsafe(wa2.getTempFahrenheitUnsafe()) +wa2.tempCelsiusUnsafe = wa2.tempCelsiusUnsafe + 1 +wa2.tempCelsiusUnsafe2 = wa2.tempCelsiusUnsafe2 + 2 + + +////////////////////////// +// 5 -- even more actorIndependent accessing actorIndependent(unsafe) member +actor class WeatherActor3 { + + @actorIndependent(unsafe) + var tempCelsius : Double = 5.0 + + @actorIndependent(unsafe) + var tempFahrenheit : Double { + get { (1.8 * tempCelsius) + 32.0 } + set { tempCelsius = (newValue - 32.0) / 1.8 } + } + + subscript(info : String) -> Double { + get { + switch info { + case "f", "fahrenheit": + return tempFahrenheit + + case "c", "celsius": + return tempCelsius + + default: + print("setter for unknown weather information: " + info) + return 0.0 + } + } + set { + switch info { + case "f", "fahrenheit": + tempFahrenheit = newValue + + case "c", "celsius": + tempCelsius = newValue + + default: + print("getter for unknown weather information: " + info) + } + } + } +} + +let wa3 = WeatherActor3() +wa3.tempFahrenheit += 3 +wa3.tempCelsius -= 1 + + +////////////////////////// +// 6 -- accesses to static members + +actor class Actor3 { + @actorIndependent(unsafe) + static var pi : Double = 3.0 + + @actorIndependent(unsafe) + static var e : Double = 2.0 + + // expected-error@+1{{'@actorIndependent' can not be applied to stored properties}} + @actorIndependent static var pi_2 : Double = 9.0 + + static var e_2 : Double = 4.0 +} + +let _ = Actor3.pi + Actor3.e + + +////////////////////////// +// 7 -- accesses to global vars + +@actorIndependent(unsafe) +var time = 1.12 + +actor class Actor4 { + var currentTime : Double { + get { time } + set { time = newValue } + } +} From 0752121cc5faa5c57739aebaf6ea57ab72959378 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Wed, 14 Oct 2020 12:27:04 -0700 Subject: [PATCH 569/745] add regression tests for SR-13735 --- test/attr/actorindependent.swift | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/attr/actorindependent.swift b/test/attr/actorindependent.swift index 84abf990d6c33..944355f331adc 100644 --- a/test/attr/actorindependent.swift +++ b/test/attr/actorindependent.swift @@ -75,3 +75,22 @@ actor class A { // expected-error@+1{{'@actorIndependent' can only be applied to instance members of actors}} @actorIndependent static func staticFunc() { } } + +actor class FromProperty { + // expected-note@+3{{mutable state is only available within the actor instance}} + // expected-note@+2{{mutable state is only available within the actor instance}} + // expected-note@+1{{mutable state is only available within the actor instance}} + var counter : Int = 0 + + // expected-error@+2{{actor-isolated property 'counter' can not be referenced from an '@actorIndependent' context}} + @actorIndependent + var halfCounter : Int { counter / 2 } + + @actorIndependent + var ticks : Int { + // expected-error@+1{{actor-isolated property 'counter' can not be referenced from an '@actorIndependent' context}} + get { counter } + // expected-error@+1{{actor-isolated property 'counter' can not be referenced from an '@actorIndependent' context}} + set { counter = newValue } + } +} \ No newline at end of file From d7a6ded7aa73611b3b9c2ec02b408f5c1651754c Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Mon, 19 Oct 2020 16:54:14 -0700 Subject: [PATCH 570/745] [Dependency Scanner] Do not escape strings in the scanner output When outputting strings for things like filenames, using `write_escaped` will result in Unicode characters being outputted with a full 3-character-octal or hex escape. Clients which expect a UTF-8 JSON output will not be able to parse such escape sequences. --- lib/FrontendTool/ScanDependencies.cpp | 5 +---- .../Inputs/unicode_fil\321\221nam\321\221.swift" | 3 +++ test/ScanDependencies/unicode_filename.swift | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 "test/ScanDependencies/Inputs/unicode_fil\321\221nam\321\221.swift" create mode 100644 test/ScanDependencies/unicode_filename.swift diff --git a/lib/FrontendTool/ScanDependencies.cpp b/lib/FrontendTool/ScanDependencies.cpp index f8267d9fd2b13..f40d9c44a6892 100644 --- a/lib/FrontendTool/ScanDependencies.cpp +++ b/lib/FrontendTool/ScanDependencies.cpp @@ -307,7 +307,7 @@ namespace { StringRef value, unsigned indentLevel) { out << "\""; - out.write_escaped(value); + out << value; out << "\""; } @@ -460,8 +460,6 @@ static void writeJSON(llvm::raw_ostream &out, writeJSONSingleField(out, "modulePath", modulePath, /*indentLevel=*/3, /*trailingComma=*/true); - // Artem Refactoring - { // Source files. if (swiftTextualDeps) { writeJSONSingleField(out, "sourceFiles", swiftTextualDeps->sourceFiles, 3, @@ -613,7 +611,6 @@ static void writeJSON(llvm::raw_ostream &out, out << ","; out << "\n"; } - } } static bool diagnoseCycle(CompilerInstance &instance, diff --git "a/test/ScanDependencies/Inputs/unicode_fil\321\221nam\321\221.swift" "b/test/ScanDependencies/Inputs/unicode_fil\321\221nam\321\221.swift" new file mode 100644 index 0000000000000..a77e5bfc5cec8 --- /dev/null +++ "b/test/ScanDependencies/Inputs/unicode_fil\321\221nam\321\221.swift" @@ -0,0 +1,3 @@ +func foo() { + print(1) +} diff --git a/test/ScanDependencies/unicode_filename.swift b/test/ScanDependencies/unicode_filename.swift new file mode 100644 index 0000000000000..d86fd78ac1b7c --- /dev/null +++ b/test/ScanDependencies/unicode_filename.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -scan-dependencies %s %S/Inputs/unicode_filёnamё.swift -o %t/deps.json + +// Check the contents of the JSON output +// RUN: %FileCheck %s < %t/deps.json + +print(foo()) + +// CHECK: "swift": "deps" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "modulePath": "deps.swiftmodule", +// CHECK-NEXT: "sourceFiles": [ +// CHECK-NEXT: "{{.*}}ScanDependencies{{/|\\}}unicode_filename.swift", +// CHECK-NEXT: "{{.*}}ScanDependencies{{/|\\}}Inputs{{/|\\}}unicode_filёnamё.swift" +// CHECK-NEXT: ], From cc542c0f10f9a0848bfe94d1040d779ad1d6ec67 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Mon, 19 Oct 2020 18:59:09 -0700 Subject: [PATCH 571/745] [Build System] Remove tvOS i386 slice from compiler-rt lib To fix this issue: ld: building for tvOS, but linking in object file built for tvOS Simulator, file '/tmp/strip.rgKCdx' for architecture i386 fatal error: /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/strip: internal link edit command failed rdar://70443440 --- utils/build-script-impl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index 3ba943866969d..7b746ce89af80 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -1406,7 +1406,12 @@ function copy_embedded_compiler_rt_builtins_from_darwin_host_toolchain() { # slices in Xcode that doesn't have the simulator .a, so # the link is still valid. echo "copying over faux-sim library ${HOST_LIB_PATH} to ${SIM_LIB_NAME}" - call cp "${HOST_LIB_PATH}" "${DEST_SIM_LIB_PATH}" + if [[ "$OS" == "tvos" ]]; then + echo "Remove i386 from tvOS ${DEST_SIM_LIB_PATH}" + call lipo -remove i386 "${HOST_LIB_PATH}" -output "${DEST_SIM_LIB_PATH}" + else + call cp "${HOST_LIB_PATH}" "${DEST_SIM_LIB_PATH}" + fi elif [[ "${VERBOSE_BUILD}" ]]; then echo "no file exists at ${HOST_SIM_LIB_PATH}" fi From 0c51dda34b3b24635c921459bec40175b4205040 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 15 Oct 2020 21:38:24 -0400 Subject: [PATCH 572/745] Sema: Pull @_implementationOnly override checking out of ExportabilityChecker --- lib/Sema/TypeCheckAccess.cpp | 37 ---------------------- lib/Sema/TypeCheckDecl.h | 1 + lib/Sema/TypeCheckDeclOverride.cpp | 50 ++++++++++++++++++++++++++++++ lib/Sema/TypeCheckDeclPrimary.cpp | 8 +++++ 4 files changed, 59 insertions(+), 37 deletions(-) diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index d1d1cdf79227b..163b7862664ac 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -1737,40 +1737,6 @@ class ExportabilityChecker : public DeclVisitor { return true; } - void checkOverride(const ValueDecl *VD) { - const ValueDecl *overridden = VD->getOverriddenDecl(); - if (!overridden) - return; - - auto *SF = VD->getDeclContext()->getParentSourceFile(); - assert(SF && "checking a non-source declaration?"); - - ModuleDecl *M = overridden->getModuleContext(); - if (SF->isImportedImplementationOnly(M)) { - VD->diagnose(diag::implementation_only_override_import_without_attr, - overridden->getDescriptiveKind()) - .fixItInsert(VD->getAttributeInsertionLoc(false), - "@_implementationOnly "); - overridden->diagnose(diag::overridden_here); - return; - } - - if (overridden->getAttrs().hasAttribute()) { - VD->diagnose(diag::implementation_only_override_without_attr, - overridden->getDescriptiveKind()) - .fixItInsert(VD->getAttributeInsertionLoc(false), - "@_implementationOnly "); - overridden->diagnose(diag::overridden_here); - return; - } - - // FIXME: Check storage decls where the setter is in a separate module from - // the getter, which is a thing Objective-C can do. The ClangImporter - // doesn't make this easy, though, because it just gives the setter the same - // DeclContext as the property or subscript, which means we've lost the - // information about whether its module was implementation-only imported. - } - void visit(Decl *D) { if (D->isInvalid() || D->isImplicit()) return; @@ -1778,7 +1744,6 @@ class ExportabilityChecker : public DeclVisitor { if (auto *VD = dyn_cast(D)) { if (shouldSkipChecking(VD)) return; - checkOverride(VD); } // Note: references to @_spi and @_implementationOnly declarations from @@ -1828,8 +1793,6 @@ class ExportabilityChecker : public DeclVisitor { if (shouldSkipChecking(theVar)) return; - checkOverride(theVar); - // Only check the type of individual variables if we didn't check an // enclosing TypedPattern. if (seenVars.count(theVar) || theVar->isInvalid()) diff --git a/lib/Sema/TypeCheckDecl.h b/lib/Sema/TypeCheckDecl.h index 5f3a01c8348fb..5bb835b9e3585 100644 --- a/lib/Sema/TypeCheckDecl.h +++ b/lib/Sema/TypeCheckDecl.h @@ -33,6 +33,7 @@ const ConstructorDecl *findNonImplicitRequiredInit(const ConstructorDecl *CD); // Implemented in TypeCheckDeclOverride.cpp bool checkOverrides(ValueDecl *decl); +void checkImplementationOnlyOverride(const ValueDecl *VD); // Implemented in TypeCheckStorage.cpp void setBoundVarsTypeError(Pattern *pattern, ASTContext &ctx); diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 11c7985877a54..5c330a6ead843 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -2156,3 +2156,53 @@ bool IsABICompatibleOverrideRequest::evaluate(Evaluator &evaluator, return derivedInterfaceTy->matches(overrideInterfaceTy, TypeMatchFlags::AllowABICompatible); } + +void swift::checkImplementationOnlyOverride(const ValueDecl *VD) { + if (VD->isImplicit()) + return; + + if (VD->getAttrs().hasAttribute()) + return; + + if (isa(VD)) + return; + + // Is this part of the module's API or ABI? + AccessScope accessScope = + VD->getFormalAccessScope(nullptr, + /*treatUsableFromInlineAsPublic*/true); + if (!accessScope.isPublic()) + return; + + const ValueDecl *overridden = VD->getOverriddenDecl(); + if (!overridden) + return; + + auto *SF = VD->getDeclContext()->getParentSourceFile(); + assert(SF && "checking a non-source declaration?"); + + ModuleDecl *M = overridden->getModuleContext(); + if (SF->isImportedImplementationOnly(M)) { + VD->diagnose(diag::implementation_only_override_import_without_attr, + overridden->getDescriptiveKind()) + .fixItInsert(VD->getAttributeInsertionLoc(false), + "@_implementationOnly "); + overridden->diagnose(diag::overridden_here); + return; + } + + if (overridden->getAttrs().hasAttribute()) { + VD->diagnose(diag::implementation_only_override_without_attr, + overridden->getDescriptiveKind()) + .fixItInsert(VD->getAttributeInsertionLoc(false), + "@_implementationOnly "); + overridden->diagnose(diag::overridden_here); + return; + } + + // FIXME: Check storage decls where the setter is in a separate module from + // the getter, which is a thing Objective-C can do. The ClangImporter + // doesn't make this easy, though, because it just gives the setter the same + // DeclContext as the property or subscript, which means we've lost the + // information about whether its module was implementation-only imported. +} diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 6a5da452ac077..fa1163a9036e9 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1551,6 +1551,8 @@ class DeclChecker : public DeclVisitor { } } + checkImplementationOnlyOverride(VD); + if (VD->getDeclContext()->getSelfClassDecl()) { if (VD->getValueInterfaceType()->hasDynamicSelfType()) { if (VD->hasStorage()) @@ -1772,6 +1774,8 @@ class DeclChecker : public DeclVisitor { } } + checkImplementationOnlyOverride(SD); + // Compute these requests in case they emit diagnostics. (void) SD->isGetterMutating(); (void) SD->isSetterMutating(); @@ -2364,6 +2368,8 @@ class DeclChecker : public DeclVisitor { } } + checkImplementationOnlyOverride(FD); + if (requiresDefinition(FD) && !FD->hasBody()) { // Complain if we should have a body. FD->diagnose(diag::func_decl_without_brace); @@ -2672,6 +2678,8 @@ class DeclChecker : public DeclVisitor { } } + checkImplementationOnlyOverride(CD); + // If this initializer overrides a 'required' initializer, it must itself // be marked 'required'. if (!CD->getAttrs().hasAttribute()) { From 0ea136738473696004aa7b9b14a449f4ba3bea26 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 15 Oct 2020 18:33:07 -0400 Subject: [PATCH 573/745] Sema: Refactoring in preparation for conformance availability checking Today, we check conformance exportability in two places: 1) For inlinable function bodies: in availability checking 2) For types in inlinable declaration signatures: in availability checking 3) For types in non-inlinable declaration signatures: in ExportabilityChecker I want to merge 2) and 3) together, and also generalize the conformance exportability check to check conformance availability as well. This is a preliminary refactoring towards achieving this goal. --- lib/Sema/ResilienceDiagnostics.cpp | 133 +++++++++++-------------- lib/Sema/TypeCheckAccess.cpp | 33 ++++--- lib/Sema/TypeCheckAccess.h | 14 +++ lib/Sema/TypeCheckAvailability.cpp | 154 ++++++++++++++++++++++------- lib/Sema/TypeCheckAvailability.h | 37 ++++++- lib/Sema/TypeChecker.h | 21 ++-- 6 files changed, 258 insertions(+), 134 deletions(-) diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index 3a26433ce59c3..14b9ba6c23098 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -27,13 +27,6 @@ using namespace swift; -/// A uniquely-typed boolean to reduce the chances of accidentally inverting -/// a check. -enum class DowngradeToWarning: bool { - No, - Yes -}; - bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc, ConcreteDeclRef declRef, const DeclContext *DC, @@ -55,16 +48,17 @@ bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc, // Skip this check for accessors because the associated property or subscript // will also be checked, and will provide a better error message. if (!isa(D)) - if (diagnoseDeclRefExportability(loc, declRef, DC, Kind)) + if (diagnoseDeclRefExportability(loc, declRef, DC, + None, Kind)) return true; return false; } bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, - const ValueDecl *D, - const DeclContext *DC, - FragileFunctionKind Kind) { + const ValueDecl *D, + const DeclContext *DC, + FragileFunctionKind Kind) { assert(Kind.kind != FragileFunctionKind::None); // Local declarations are OK. @@ -150,84 +144,79 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, static bool diagnoseDeclExportability(SourceLoc loc, const ValueDecl *D, const SourceFile &userSF, const DeclContext *userDC, + Optional reason, FragileFunctionKind fragileKind) { - assert(fragileKind.kind != FragileFunctionKind::None); + if (fragileKind.kind == FragileFunctionKind::None && !reason.hasValue()) + return false; auto definingModule = D->getModuleContext(); + auto downgradeToWarning = DowngradeToWarning::No; auto originKind = getDisallowedOriginKind( - D, userSF, userDC->getInnermostDeclarationDeclContext()); + D, userSF, userDC->getInnermostDeclarationDeclContext(), + downgradeToWarning); if (originKind == DisallowedOriginKind::None) return false; - // TODO: different diagnostics ASTContext &ctx = definingModule->getASTContext(); - ctx.Diags.diagnose(loc, diag::inlinable_decl_ref_from_hidden_module, - D->getDescriptiveKind(), D->getName(), - static_cast(fragileKind.kind), - definingModule->getName(), - static_cast(originKind)); - return true; -} -static bool -diagnoseGenericArgumentsExportability(SourceLoc loc, - SubstitutionMap subs, - const SourceFile &userSF, - const DeclContext *userDC) { - bool hadAnyIssues = false; - for (ProtocolConformanceRef conformance : subs.getConformances()) { - if (!conformance.isConcrete()) - continue; - const ProtocolConformance *concreteConf = conformance.getConcrete(); - - SubstitutionMap subConformanceSubs = - concreteConf->getSubstitutions(userSF.getParentModule()); - diagnoseGenericArgumentsExportability(loc, subConformanceSubs, userSF, userDC); - - const RootProtocolConformance *rootConf = - concreteConf->getRootConformance(); - ModuleDecl *M = rootConf->getDeclContext()->getParentModule(); - - auto originKind = getDisallowedOriginKind( - rootConf->getDeclContext()->getAsDecl(), - userSF, userDC->getInnermostDeclarationDeclContext()); - if (originKind == DisallowedOriginKind::None) - continue; - - ASTContext &ctx = M->getASTContext(); - ctx.Diags.diagnose(loc, diag::conformance_from_implementation_only_module, - rootConf->getType(), - rootConf->getProtocol()->getName(), 0, M->getName(), + if (fragileKind.kind == FragileFunctionKind::None) { + auto errorOrWarning = downgradeToWarning == DowngradeToWarning::Yes? + diag::decl_from_hidden_module_warn: + diag::decl_from_hidden_module; + ctx.Diags.diagnose(loc, errorOrWarning, + D->getDescriptiveKind(), + D->getName(), + static_cast(*reason), + definingModule->getName(), + static_cast(originKind)); + + D->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type); + } else { + ctx.Diags.diagnose(loc, diag::inlinable_decl_ref_from_hidden_module, + D->getDescriptiveKind(), D->getName(), + static_cast(fragileKind.kind), + definingModule->getName(), static_cast(originKind)); - hadAnyIssues = true; } - return hadAnyIssues; + return true; } -void TypeChecker::diagnoseGenericTypeExportability(SourceLoc Loc, Type T, - const DeclContext *DC) { - const SourceFile *SF = DC->getParentSourceFile(); - if (!SF) - return; - - // FIXME: It would be nice to highlight just the part of the type that's - // problematic, but unfortunately the TypeRepr doesn't have the - // information we need and the Type doesn't easily map back to it. - if (auto *BGT = dyn_cast(T.getPointer())) { - ModuleDecl *useModule = SF->getParentModule(); - auto subs = T->getContextSubstitutionMap(useModule, BGT->getDecl()); - (void)diagnoseGenericArgumentsExportability(Loc, subs, *SF, DC); - } else if (auto *TAT = dyn_cast(T.getPointer())) { - auto subs = TAT->getSubstitutionMap(); - (void)diagnoseGenericArgumentsExportability(Loc, subs, *SF, DC); - } +bool +TypeChecker::diagnoseConformanceExportability(SourceLoc loc, + const RootProtocolConformance *rootConf, + const SourceFile &userSF, + const DeclContext *userDC, + Optional reason, + FragileFunctionKind fragileKind) { + if (fragileKind.kind == FragileFunctionKind::None && !reason.hasValue()) + return false; + + auto originKind = getDisallowedOriginKind( + rootConf->getDeclContext()->getAsDecl(), + userSF, userDC->getInnermostDeclarationDeclContext()); + if (originKind == DisallowedOriginKind::None) + return false; + + if (!reason.hasValue()) + reason = ExportabilityReason::General; + + ModuleDecl *M = rootConf->getDeclContext()->getParentModule(); + ASTContext &ctx = M->getASTContext(); + ctx.Diags.diagnose(loc, diag::conformance_from_implementation_only_module, + rootConf->getType(), + rootConf->getProtocol()->getName(), + static_cast(*reason), + M->getName(), + static_cast(originKind)); + return true; } bool TypeChecker::diagnoseDeclRefExportability(SourceLoc loc, ConcreteDeclRef declRef, const DeclContext *DC, + Optional reason, FragileFunctionKind fragileKind) { // We're only interested in diagnosing uses from source files. auto userSF = DC->getParentSourceFile(); @@ -235,11 +224,7 @@ TypeChecker::diagnoseDeclRefExportability(SourceLoc loc, return false; const ValueDecl *D = declRef.getDecl(); - if (diagnoseDeclExportability(loc, D, *userSF, DC, fragileKind)) - return true; - if (diagnoseGenericArgumentsExportability(loc, declRef.getSubstitutions(), - *userSF, DC)) { + if (diagnoseDeclExportability(loc, D, *userSF, DC, reason, fragileKind)) return true; - } return false; } diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 163b7862664ac..99a65433b0643 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -35,15 +35,6 @@ using namespace swift; namespace { -/// A uniquely-typed boolean to reduce the chances of accidentally inverting -/// a check. -/// -/// \see checkTypeAccess -enum class DowngradeToWarning: bool { - No, - Yes -}; - /// Calls \p callback for each type in each requirement provided by /// \p source. static void forAllRequirementTypes( @@ -1464,15 +1455,17 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, } }; +} // end anonymous namespace + /// Returns the kind of origin, implementation-only import or SPI declaration, /// that restricts exporting \p decl from the given file and context. /// /// Local variant to swift::getDisallowedOriginKind for downgrade to warnings. DisallowedOriginKind -getDisallowedOriginKind(const Decl *decl, - const SourceFile &userSF, - const Decl *userContext, - DowngradeToWarning &downgradeToWarning) { +swift::getDisallowedOriginKind(const Decl *decl, + const SourceFile &userSF, + const Decl *userContext, + DowngradeToWarning &downgradeToWarning) { downgradeToWarning = DowngradeToWarning::No; ModuleDecl *M = decl->getModuleContext(); if (userSF.isImportedImplementationOnly(M)) { @@ -1493,6 +1486,8 @@ getDisallowedOriginKind(const Decl *decl, return DisallowedOriginKind::None; }; +namespace { + // Diagnose public APIs exposing types that are either imported as // implementation-only or declared as SPI. class ExportabilityChecker : public DeclVisitor { @@ -2015,6 +2010,8 @@ class ExportabilityChecker : public DeclVisitor { /// Diagnose declarations whose signatures refer to unavailable types. class DeclAvailabilityChecker : public DeclVisitor { DeclContext *DC; + Optional ExportReason; + FragileFunctionKind FragileKind; void checkType(Type type, const TypeRepr *typeRepr, const Decl *context, bool allowUnavailableProtocol=false) { @@ -2036,12 +2033,14 @@ class DeclAvailabilityChecker : public DeclVisitor { if (allowUnavailableProtocol) flags |= DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol; - diagnoseTypeReprAvailability(typeRepr, DC, flags); + diagnoseTypeReprAvailability(typeRepr, DC, ExportReason, FragileKind, + flags); } // Check the type for references to unavailable conformances. if (type) - diagnoseTypeAvailability(type, context->getLoc(), DC); + diagnoseTypeAvailability(type, context->getLoc(), DC, + ExportReason, FragileKind); } void checkGenericParams(const GenericContext *ownerCtx, @@ -2070,7 +2069,9 @@ class DeclAvailabilityChecker : public DeclVisitor { public: explicit DeclAvailabilityChecker(Decl *D) - : DC(D->getInnermostDeclContext()) {} + : DC(D->getInnermostDeclContext()) { + FragileKind = DC->getFragileFunctionKind(); + } void visit(Decl *D) { if (D->isImplicit()) diff --git a/lib/Sema/TypeCheckAccess.h b/lib/Sema/TypeCheckAccess.h index d29523ef39a8d..764603a85ac09 100644 --- a/lib/Sema/TypeCheckAccess.h +++ b/lib/Sema/TypeCheckAccess.h @@ -43,12 +43,26 @@ enum class DisallowedOriginKind : uint8_t { None }; +/// A uniquely-typed boolean to reduce the chances of accidentally inverting +/// a check. +/// +/// \see checkTypeAccess +enum class DowngradeToWarning: bool { + No, + Yes +}; + /// Returns the kind of origin, implementation-only import or SPI declaration, /// that restricts exporting \p decl from the given file and context. DisallowedOriginKind getDisallowedOriginKind(const Decl *decl, const SourceFile &userSF, const Decl *userContext); +DisallowedOriginKind getDisallowedOriginKind(const Decl *decl, + const SourceFile &userSF, + const Decl *userContext, + DowngradeToWarning &downgradeToWarning); + } // end namespace swift #endif diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 0b6386d97128d..92ca195d08d9a 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -22,6 +22,7 @@ #include "swift/AST/Initializer.h" #include "swift/AST/NameLookup.h" #include "swift/AST/Pattern.h" +#include "swift/AST/ProtocolConformance.h" #include "swift/AST/SourceFile.h" #include "swift/AST/TypeDeclFinder.h" #include "swift/AST/TypeRefinementContext.h" @@ -2344,6 +2345,7 @@ class AvailabilityWalker : public ASTWalker { DeclContext *DC; MemberAccessContext AccessContext = MemberAccessContext::Getter; SmallVector ExprStack; + Optional ExportReason; FragileFunctionKind FragileKind; /// Returns true if DC is an \c init(rawValue:) declaration and it is marked @@ -2360,9 +2362,11 @@ class AvailabilityWalker : public ASTWalker { } public: - AvailabilityWalker(DeclContext *DC) : Context(DC->getASTContext()), DC(DC) { - FragileKind = DC->getFragileFunctionKind(); - } + AvailabilityWalker(DeclContext *DC, + Optional ExportReason, + FragileFunctionKind FragileKind) + : Context(DC->getASTContext()), DC(DC), + ExportReason(ExportReason), FragileKind(FragileKind) {} bool shouldWalkIntoSeparatelyCheckedClosure(ClosureExpr *expr) override { return false; @@ -2430,16 +2434,20 @@ class AvailabilityWalker : public ASTWalker { if (auto T = dyn_cast(E)) { if (!T->isImplicit()) if (auto type = T->getType()) - diagnoseTypeAvailability(type, E->getLoc(), DC); + diagnoseTypeAvailability(type, E->getLoc(), DC, + ExportReason, FragileKind); } if (auto CE = dyn_cast(E)) { for (auto *param : *CE->getParameters()) { - diagnoseTypeAvailability(param->getInterfaceType(), E->getLoc(), DC); + diagnoseTypeAvailability(param->getInterfaceType(), E->getLoc(), DC, + ExportReason, FragileKind); } - diagnoseTypeAvailability(CE->getResultType(), E->getLoc(), DC); + diagnoseTypeAvailability(CE->getResultType(), E->getLoc(), DC, + ExportReason, FragileKind); } if (auto IE = dyn_cast(E)) { - diagnoseTypeAvailability(IE->getCastType(), E->getLoc(), DC); + diagnoseTypeAvailability(IE->getCastType(), E->getLoc(), DC, + ExportReason, FragileKind); } return visitChildren(); @@ -2453,7 +2461,7 @@ class AvailabilityWalker : public ASTWalker { } bool walkToTypeReprPre(TypeRepr *T) override { - diagnoseTypeReprAvailability(T, DC); + diagnoseTypeReprAvailability(T, DC, ExportReason, FragileKind); return false; } @@ -2660,10 +2668,23 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, return false; } - if (FragileKind.kind != FragileFunctionKind::None) + if (FragileKind.kind != FragileFunctionKind::None) { if (R.isValid()) if (TypeChecker::diagnoseInlinableDeclRef(R.Start, declRef, DC, FragileKind)) return true; + } else if (ExportReason.hasValue()) { + if (R.isValid()) + if (TypeChecker::diagnoseDeclRefExportability(R.Start, declRef, DC, + ExportReason, FragileKind)) + return true; + } + + if (R.isValid()) { + if (diagnoseSubstitutionMapAvailability(R.Start, declRef.getSubstitutions(), + DC, ExportReason, FragileKind)) { + return true; + } + } if (diagnoseExplicitUnavailability(D, R, DC, call, Flags)) return true; @@ -2856,7 +2877,9 @@ AvailabilityWalker::diagnoseMemoryLayoutMigration(const ValueDecl *D, /// Diagnose uses of unavailable declarations. void swift::diagAvailability(const Expr *E, DeclContext *DC) { - AvailabilityWalker walker(DC); + Optional reason = None; + FragileFunctionKind fragileKind = DC->getFragileFunctionKind(); + AvailabilityWalker walker(DC, reason, fragileKind); const_cast(E)->walk(walker); } @@ -2864,9 +2887,13 @@ namespace { class StmtAvailabilityWalker : public ASTWalker { DeclContext *DC; + Optional ExportReason; + FragileFunctionKind FragileKind; public: - explicit StmtAvailabilityWalker(DeclContext *DC) : DC(DC) {} + explicit StmtAvailabilityWalker(DeclContext *DC) : DC(DC) { + FragileKind = DC->getFragileFunctionKind(); + } /// We'll visit the expression from performSyntacticExprDiagnostics(). std::pair walkToExprPre(Expr *E) override { @@ -2874,14 +2901,15 @@ class StmtAvailabilityWalker : public ASTWalker { } bool walkToTypeReprPre(TypeRepr *T) override { - diagnoseTypeReprAvailability(T, DC); + diagnoseTypeReprAvailability(T, DC, ExportReason, FragileKind); return false; } std::pair walkToPatternPre(Pattern *P) override { if (auto *IP = dyn_cast(P)) if (auto T = IP->getCastType()) - diagnoseTypeAvailability(T, P->getLoc(), DC); + diagnoseTypeAvailability(T, P->getLoc(), DC, + ExportReason, FragileKind); return std::make_pair(true, P); } @@ -2902,12 +2930,16 @@ namespace { class TypeReprAvailabilityWalker : public ASTWalker { DeclContext *DC; + Optional reason; + FragileFunctionKind fragileKind; DeclAvailabilityFlags flags; bool checkComponentIdentTypeRepr(ComponentIdentTypeRepr *ITR) { if (auto *typeDecl = ITR->getBoundDecl()) { auto range = ITR->getNameLoc().getSourceRange(); - if (diagnoseDeclAvailability(typeDecl, DC, range, flags)) + if (diagnoseDeclAvailability(typeDecl, range, DC, + reason, fragileKind, + flags)) return true; } @@ -2918,7 +2950,9 @@ class TypeReprAvailabilityWalker : public ASTWalker { genericFlags -= DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol; for (auto *genericArg : GTR->getGenericArgs()) { - if (diagnoseTypeReprAvailability(genericArg, DC, genericFlags)) + if (diagnoseTypeReprAvailability(genericArg, DC, + reason, fragileKind, + genericFlags)) foundAnyIssues = true; } } @@ -2930,8 +2964,10 @@ class TypeReprAvailabilityWalker : public ASTWalker { bool foundAnyIssues = false; TypeReprAvailabilityWalker(DeclContext *DC, + Optional reason, + FragileFunctionKind fragileKind, DeclAvailabilityFlags flags) - : DC(DC), flags(flags) {} + : DC(DC), reason(reason), fragileKind(fragileKind), flags(flags) {} bool walkToTypeReprPre(TypeRepr *T) override { if (auto *ITR = dyn_cast(T)) { @@ -2965,8 +3001,10 @@ class TypeReprAvailabilityWalker : public ASTWalker { } bool swift::diagnoseTypeReprAvailability(const TypeRepr *T, DeclContext *DC, + Optional reason, + FragileFunctionKind fragileKind, DeclAvailabilityFlags flags) { - TypeReprAvailabilityWalker walker(DC, flags); + TypeReprAvailabilityWalker walker(DC, reason, fragileKind, flags); const_cast(T)->walk(walker); return walker.foundAnyIssues; } @@ -2974,31 +3012,38 @@ bool swift::diagnoseTypeReprAvailability(const TypeRepr *T, DeclContext *DC, namespace { class ProblematicTypeFinder : public TypeDeclFinder { + SourceLoc Loc; DeclContext *DC; + Optional ExportReason; FragileFunctionKind FragileKind; - SourceLoc Loc; public: - ProblematicTypeFinder(DeclContext *DC, SourceLoc Loc) - : DC(DC), Loc(Loc) { - FragileKind = DC->getFragileFunctionKind(); - } + ProblematicTypeFinder(SourceLoc Loc, DeclContext *DC, + Optional ExportReason, + FragileFunctionKind FragileKind) + : Loc(Loc), DC(DC), + ExportReason(ExportReason), + FragileKind(FragileKind) {} Action visitNominalType(NominalType *ty) override { - if (FragileKind.kind != FragileFunctionKind::None) - TypeChecker::diagnoseGenericTypeExportability(Loc, ty, DC); + /// FIXME return Action::Continue; } Action visitBoundGenericType(BoundGenericType *ty) override { - if (FragileKind.kind != FragileFunctionKind::None) - TypeChecker::diagnoseGenericTypeExportability(Loc, ty, DC); + ModuleDecl *useModule = DC->getParentModule(); + auto subs = ty->getContextSubstitutionMap(useModule, ty->getDecl()); + (void) diagnoseSubstitutionMapAvailability(Loc, subs, DC, + ExportReason, + FragileKind); return Action::Continue; } Action visitTypeAliasType(TypeAliasType *ty) override { - if (FragileKind.kind != FragileFunctionKind::None) - TypeChecker::diagnoseGenericTypeExportability(Loc, ty, DC); + auto subs = ty->getSubstitutionMap(); + (void) diagnoseSubstitutionMapAvailability(Loc, subs, DC, + ExportReason, + FragileKind); return Action::Continue; } @@ -3006,7 +3051,8 @@ class ProblematicTypeFinder : public TypeDeclFinder { // post-visitor so that we diagnose any unexportable component // types first. Action walkToTypePost(Type T) override { - if (FragileKind.kind != FragileFunctionKind::None) { + if (FragileKind.kind != FragileFunctionKind::None || + ExportReason.hasValue()) { if (auto fnType = T->getAs()) { if (auto clangType = fnType->getClangTypeInfo().getType()) { auto &ctx = DC->getASTContext(); @@ -3028,19 +3074,61 @@ class ProblematicTypeFinder : public TypeDeclFinder { } -void swift::diagnoseTypeAvailability(Type T, SourceLoc loc, DeclContext *DC) { - T.walk(ProblematicTypeFinder(DC, loc)); +void swift::diagnoseTypeAvailability(Type T, SourceLoc loc, DeclContext *DC, + Optional reason, + FragileFunctionKind fragileKind) { + T.walk(ProblematicTypeFinder(loc, DC, reason, fragileKind)); +} + +bool +swift::diagnoseConformanceAvailability(SourceLoc loc, + ProtocolConformanceRef conformance, + const DeclContext *DC, + Optional reason, + FragileFunctionKind fragileKind) { + if (!conformance.isConcrete()) + return false; + const ProtocolConformance *concreteConf = conformance.getConcrete(); + + SubstitutionMap subConformanceSubs = + concreteConf->getSubstitutions(DC->getParentModule()); + diagnoseSubstitutionMapAvailability(loc, subConformanceSubs, DC, + reason, fragileKind); + + const RootProtocolConformance *rootConf = + concreteConf->getRootConformance(); + + return TypeChecker::diagnoseConformanceExportability( + loc, rootConf, *DC->getParentSourceFile(), DC, + reason, fragileKind); +} + +bool +swift::diagnoseSubstitutionMapAvailability(SourceLoc loc, + SubstitutionMap subs, + const DeclContext *DC, + Optional reason, + FragileFunctionKind fragileKind) { + bool hadAnyIssues = false; + for (ProtocolConformanceRef conformance : subs.getConformances()) { + if (diagnoseConformanceAvailability(loc, conformance, DC, + reason, fragileKind)) + hadAnyIssues = true; + } + return hadAnyIssues; } /// Run the Availability-diagnostics algorithm otherwise used in an expr /// context, but for non-expr contexts such as TypeDecls referenced from /// TypeReprs. bool swift::diagnoseDeclAvailability(const ValueDecl *Decl, - DeclContext *DC, SourceRange R, + DeclContext *DC, + Optional reason, + FragileFunctionKind fragileKind, DeclAvailabilityFlags Flags) { - AvailabilityWalker AW(DC); + AvailabilityWalker AW(DC, reason, fragileKind); return AW.diagAvailability(const_cast(Decl), R, nullptr, Flags); } diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index a13151fa93fd0..e19a9fe25b61a 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -28,7 +28,10 @@ namespace swift { class Expr; class InFlightDiagnostic; class Decl; + struct FragileFunctionKind; + class ProtocolConformanceRef; class Stmt; + class SubstitutionMap; class Type; class TypeRepr; class ValueDecl; @@ -57,6 +60,16 @@ enum class DeclAvailabilityFlag : uint8_t { }; using DeclAvailabilityFlags = OptionSet; +// This enum must be kept in sync with +// diag::decl_from_hidden_module and +// diag::conformance_from_implementation_only_module. +enum class ExportabilityReason : unsigned { + General, + PropertyWrapper, + ExtensionWithPublicMembers, + ExtensionWithConditionalConformances +}; + /// Diagnose uses of unavailable declarations in expressions. void diagAvailability(const Expr *E, DeclContext *DC); @@ -66,17 +79,37 @@ void diagAvailability(const Stmt *S, DeclContext *DC); /// Diagnose uses of unavailable declarations in types. bool diagnoseTypeReprAvailability(const TypeRepr *T, DeclContext *DC, + Optional reason, + FragileFunctionKind fragileKind, DeclAvailabilityFlags flags = None); /// Diagnose uses of unavailable conformances in types. -void diagnoseTypeAvailability(Type T, SourceLoc loc, DeclContext *DC); +void diagnoseTypeAvailability(Type T, SourceLoc loc, DeclContext *DC, + Optional reason, + FragileFunctionKind fragileKind); + +bool +diagnoseConformanceAvailability(SourceLoc loc, + ProtocolConformanceRef conformance, + const DeclContext *DC, + Optional reason, + FragileFunctionKind fragileKind); + +bool +diagnoseSubstitutionMapAvailability(SourceLoc loc, + SubstitutionMap subs, + const DeclContext *DC, + Optional reason, + FragileFunctionKind fragileKind); /// Run the Availability-diagnostics algorithm otherwise used in an expr /// context, but for non-expr contexts such as TypeDecls referenced from /// TypeReprs. bool diagnoseDeclAvailability(const ValueDecl *Decl, - DeclContext *DC, SourceRange R, + DeclContext *DC, + Optional reason, + FragileFunctionKind fragileKind, DeclAvailabilityFlags Options); void diagnoseUnavailableOverride(ValueDecl *override, diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index e384ae6fc9dae..beb69dda5b82b 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -40,11 +40,13 @@ namespace swift { class GenericSignatureBuilder; class NominalTypeDecl; class NormalProtocolConformance; +class RootProtocolConformance; class TypeResolution; class TypeResolutionOptions; class TypoCorrectionResults; class ExprPattern; enum class TypeResolutionStage : uint8_t; +enum class ExportabilityReason : unsigned; namespace constraints { enum class ConstraintKind : char; @@ -959,17 +961,18 @@ bool diagnoseInlinableDeclRefAccess(SourceLoc loc, const ValueDecl *D, /// reasonably be shared. bool diagnoseDeclRefExportability(SourceLoc loc, ConcreteDeclRef declRef, const DeclContext *DC, + Optional exportability, FragileFunctionKind fragileKind); -/// Given that a type is used from a particular context which -/// exposes it in the interface of the current module, diagnose if its -/// generic arguments require the use of conformances that cannot reasonably -/// be shared. -/// -/// This method \e only checks how generic arguments are used; it is assumed -/// that the declarations involved have already been checked elsewhere. -void diagnoseGenericTypeExportability(SourceLoc loc, Type type, - const DeclContext *DC); +/// Given that a conformance is used from a particular context which +/// exposes it in the interface of the current module, diagnose if the +/// conformance is SPI or visible via an implementation-only import. +bool diagnoseConformanceExportability(SourceLoc loc, + const RootProtocolConformance *rootConf, + const SourceFile &userSF, + const DeclContext *userDC, + Optional reason, + FragileFunctionKind fragileKind); /// \name Availability checking /// From 50c44c202634136a82a3534aad7651f974709d28 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 19 Oct 2020 15:57:03 -0400 Subject: [PATCH 574/745] Sema: Don't desugar the type when diagnosing unexportable Clang function types --- lib/Sema/TypeCheckAvailability.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 92ca195d08d9a..79d4728d1ec95 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -3061,8 +3061,7 @@ class ProblematicTypeFinder : public TypeDeclFinder { // but we need the canonical type to be serializable or else // canonicalization (e.g. in SIL) might break things. if (!loader->isSerializable(clangType, /*check canonical*/ true)) { - ctx.Diags.diagnose(Loc, diag::unexportable_clang_function_type, - fnType); + ctx.Diags.diagnose(Loc, diag::unexportable_clang_function_type, T); } } } From f48aa518842d9fc27b352a227868e98fa5f369a4 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 16 Oct 2020 00:42:32 -0400 Subject: [PATCH 575/745] Sema: Remove ExportabilityChecker Availability checking already knows how to check exportability for types and conformances referenced from inlinable function signatures and bodies. Let's generalize this to work on non-inlinable function signatures as well, allowing us to get rid of the ExportabilityChecker altogether. --- lib/Sema/TypeCheckAccess.cpp | 684 ++++++----------------------- lib/Sema/TypeCheckAvailability.cpp | 75 +++- lib/Sema/TypeCheckAvailability.h | 13 +- test/SPI/local_spi_decls.swift | 7 +- test/Sema/spi-in-decls.swift | 2 +- 5 files changed, 212 insertions(+), 569 deletions(-) diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 99a65433b0643..d5d662d794de4 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -20,14 +20,11 @@ #include "TypeAccessScopeChecker.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" -#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Pattern.h" #include "swift/AST/ParameterList.h" -#include "swift/AST/ProtocolConformance.h" #include "swift/AST/TypeCheckRequests.h" -#include "swift/AST/TypeDeclFinder.h" using namespace swift; @@ -1486,139 +1483,93 @@ swift::getDisallowedOriginKind(const Decl *decl, return DisallowedOriginKind::None; }; -namespace { +static bool isExported(const ValueDecl *VD) { + if (VD->getAttrs().hasAttribute()) + return false; -// Diagnose public APIs exposing types that are either imported as -// implementation-only or declared as SPI. -class ExportabilityChecker : public DeclVisitor { - class Diagnoser; + // Is this part of the module's API or ABI? + AccessScope accessScope = + VD->getFormalAccessScope(nullptr, + /*treatUsableFromInlineAsPublic*/true); + if (accessScope.isPublic()) + return true; - void checkTypeImpl( - Type type, const TypeRepr *typeRepr, const SourceFile &SF, - const Decl *context, - const Diagnoser &diagnoser) { - // Don't bother checking errors. - if (type && type->hasError()) - return; + // Is this a stored property in a non-resilient struct or class? + auto *property = dyn_cast(VD); + if (!property || !property->hasStorage() || property->isStatic()) + return false; + auto *parentNominal = dyn_cast(property->getDeclContext()); + if (!parentNominal || parentNominal->isResilient()) + return false; - bool foundAnyIssues = false; - - // Check the TypeRepr first (if present), because that will give us a - // better diagnostic. - if (typeRepr) { - const_cast(typeRepr)->walk(TypeReprIdentFinder( - [&](const ComponentIdentTypeRepr *component) { - TypeDecl *typeDecl = component->getBoundDecl(); - auto downgradeToWarning = DowngradeToWarning::No; - auto originKind = getDisallowedOriginKind(typeDecl, SF, context, downgradeToWarning); - if (originKind != DisallowedOriginKind::None) { - diagnoser.diagnoseType(typeDecl, component, originKind, downgradeToWarning); - foundAnyIssues = true; - } + // Is that struct or class part of the module's API or ABI? + AccessScope parentAccessScope = parentNominal->getFormalAccessScope( + nullptr, /*treatUsableFromInlineAsPublic*/true); + if (parentAccessScope.isPublic()) + return true; + + return false; +} - // We still continue even in the diagnostic case to report multiple - // violations. - return true; - })); +static bool isExported(Decl *D) { + if (auto *VD = dyn_cast(D)) { + return isExported(VD); + } + if (auto *PBD = dyn_cast(D)) { + for (unsigned i = 0, e = PBD->getNumPatternEntries(); i < e; ++i) { + if (auto *VD = PBD->getAnchoringVarDecl(i)) + return isExported(VD); } - // Note that if we have a type, we can't skip checking it even if the - // TypeRepr is okay, because that's how we check what conformances are - // being used. - // - // We still don't want to do this if we found issues with the TypeRepr, - // though, because that would result in some issues being reported twice. - if (foundAnyIssues || !type) - return; + return false; + } + if (auto *ED = dyn_cast(D)) { + if (auto *NTD = ED->getExtendedNominal()) + return isExported(NTD); - class ProblematicTypeFinder : public TypeDeclFinder { - const SourceFile &SF; - const Decl *context; - const Diagnoser &diagnoser; - public: - ProblematicTypeFinder(const SourceFile &SF, const Decl *context, const Diagnoser &diagnoser) - : SF(SF), context(context), diagnoser(diagnoser) {} - - void visitTypeDecl(const TypeDecl *typeDecl) { - auto downgradeToWarning = DowngradeToWarning::No; - auto originKind = getDisallowedOriginKind(typeDecl, SF, context, downgradeToWarning); - if (originKind != DisallowedOriginKind::None) - diagnoser.diagnoseType(typeDecl, /*typeRepr*/nullptr, originKind, downgradeToWarning); - } + return false; + } - void visitSubstitutionMap(SubstitutionMap subs) { - for (ProtocolConformanceRef conformance : subs.getConformances()) { - if (!conformance.isConcrete()) - continue; - const ProtocolConformance *concreteConf = conformance.getConcrete(); - - SubstitutionMap subConformanceSubs = - concreteConf->getSubstitutions(SF.getParentModule()); - visitSubstitutionMap(subConformanceSubs); - - const RootProtocolConformance *rootConf = - concreteConf->getRootConformance(); - auto originKind = getDisallowedOriginKind( - rootConf->getDeclContext()->getAsDecl(), - SF, context); - if (originKind == DisallowedOriginKind::None) - continue; - diagnoser.diagnoseConformance(rootConf, originKind); - } - } + return true; +} - Action visitNominalType(NominalType *ty) override { - visitTypeDecl(ty->getDecl()); - return Action::Continue; - } +namespace { - Action visitBoundGenericType(BoundGenericType *ty) override { - visitTypeDecl(ty->getDecl()); - SubstitutionMap subs = - ty->getContextSubstitutionMap(SF.getParentModule(), ty->getDecl()); - visitSubstitutionMap(subs); - return Action::Continue; - } +/// Diagnose declarations whose signatures refer to unavailable types. +class DeclAvailabilityChecker : public DeclVisitor { + DeclContext *DC; + FragileFunctionKind FragileKind; + bool Exported; - Action visitTypeAliasType(TypeAliasType *ty) override { - visitTypeDecl(ty->getDecl()); - visitSubstitutionMap(ty->getSubstitutionMap()); - return Action::Continue; - } + void checkType(Type type, const TypeRepr *typeRepr, const Decl *context, + ExportabilityReason reason=ExportabilityReason::General, + bool allowUnavailableProtocol=false) { + // Don't bother checking errors. + if (type && type->hasError()) + return; - // We diagnose unserializable Clang function types in the - // post-visitor so that we diagnose any unexportable component - // types first. - Action walkToTypePost(Type T) override { - if (auto fnType = T->getAs()) { - if (auto clangType = fnType->getClangTypeInfo().getType()) { - auto loader = T->getASTContext().getClangModuleLoader(); - // Serialization will serialize the sugared type if it can, - // but we need the canonical type to be serializable or else - // canonicalization (e.g. in SIL) might break things. - if (!loader->isSerializable(clangType, /*check canonical*/ true)) { - diagnoser.diagnoseClangFunctionType(T, clangType); - } - } - } - return TypeDeclFinder::walkToTypePost(T); - } - }; + Optional optReason; + if (Exported) + optReason = reason; - type.walk(ProblematicTypeFinder(SF, context, diagnoser)); - } + DeclAvailabilityFlags flags = None; - void checkType( - Type type, const TypeRepr *typeRepr, const Decl *context, - const Diagnoser &diagnoser) { - auto *SF = context->getDeclContext()->getParentSourceFile(); - assert(SF && "checking a non-source declaration?"); - return checkTypeImpl(type, typeRepr, *SF, context, diagnoser); - } + // We allow a type to conform to a protocol that is less available than + // the type itself. This enables a type to retroactively model or directly + // conform to a protocol only available on newer OSes and yet still be used on + // older OSes. + // + // To support this, inside inheritance clauses we allow references to + // protocols that are unavailable in the current type refinement context. + if (allowUnavailableProtocol) + flags |= DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol; + + auto loc = context->getLoc(); + if (auto *varDecl = dyn_cast(context)) + loc = varDecl->getNameLoc(); - void checkType( - const TypeLoc &TL, const Decl *context, const Diagnoser &diagnoser) { - checkType(TL.getType(), TL.getTypeRepr(), context, diagnoser); + diagnoseTypeAvailability(typeRepr, type, loc, DC, + optReason, FragileKind, flags); } void checkGenericParams(const GenericContext *ownerCtx, @@ -1631,8 +1582,8 @@ class ExportabilityChecker : public DeclVisitor { if (param->getInherited().empty()) continue; assert(param->getInherited().size() == 1); - checkType(param->getInherited().front(), ownerDecl, - getDiagnoser(ownerDecl)); + auto inherited = param->getInherited().front(); + checkType(inherited.getType(), inherited.getTypeRepr(), ownerDecl); } } @@ -1640,112 +1591,16 @@ class ExportabilityChecker : public DeclVisitor { forAllRequirementTypes(WhereClauseOwner( const_cast(ownerCtx)), [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, ownerDecl, getDiagnoser(ownerDecl)); + checkType(type, typeRepr, ownerDecl); }); } } - // This enum must be kept in sync with - // diag::decl_from_hidden_module and - // diag::conformance_from_implementation_only_module. - enum class Reason : unsigned { - General, - PropertyWrapper, - ExtensionWithPublicMembers, - ExtensionWithConditionalConformances - }; - - class Diagnoser { - const Decl *D; - Reason reason; - public: - Diagnoser(const Decl *D, Reason reason) : D(D), reason(reason) {} - - void diagnoseType(const TypeDecl *offendingType, - const TypeRepr *complainRepr, - DisallowedOriginKind originKind, - DowngradeToWarning downgradeToWarning) const { - ModuleDecl *M = offendingType->getModuleContext(); - auto errorOrWarning = downgradeToWarning == DowngradeToWarning::Yes? - diag::decl_from_hidden_module_warn: - diag::decl_from_hidden_module; - auto diag = D->diagnose(errorOrWarning, - offendingType->getDescriptiveKind(), - offendingType->getName(), - static_cast(reason), M->getName(), - static_cast(originKind)); - highlightOffendingType(diag, complainRepr); - } - - void diagnoseConformance(const ProtocolConformance *offendingConformance, - DisallowedOriginKind originKind) const { - ModuleDecl *M = offendingConformance->getDeclContext()->getParentModule(); - D->diagnose(diag::conformance_from_implementation_only_module, - offendingConformance->getType(), - offendingConformance->getProtocol()->getName(), - static_cast(reason), M->getName(), - static_cast(originKind)); - } - - void diagnoseClangFunctionType(Type fnType, const clang::Type *type) const { - D->diagnose(diag::unexportable_clang_function_type, fnType); - } - }; - - Diagnoser getDiagnoser(const Decl *D, Reason reason = Reason::General) { - return Diagnoser(D, reason); - } - public: - ExportabilityChecker() {} - - static bool shouldSkipChecking(const ValueDecl *VD) { - if (VD->getAttrs().hasAttribute()) - return true; - - // Accessors are handled as part of their Var or Subscript, and we don't - // want to redo extension signature checking for them. - if (isa(VD)) - return true; - - // Is this part of the module's API or ABI? - AccessScope accessScope = - VD->getFormalAccessScope(nullptr, - /*treatUsableFromInlineAsPublic*/true); - if (accessScope.isPublic()) - return false; - - // Is this a stored property in a non-resilient struct or class? - auto *property = dyn_cast(VD); - if (!property || !property->hasStorage() || property->isStatic()) - return true; - auto *parentNominal = dyn_cast(property->getDeclContext()); - if (!parentNominal || parentNominal->isResilient()) - return true; - - // Is that struct or class part of the module's API or ABI? - AccessScope parentAccessScope = parentNominal->getFormalAccessScope( - nullptr, /*treatUsableFromInlineAsPublic*/true); - if (parentAccessScope.isPublic()) - return false; - - return true; - } - - void visit(Decl *D) { - if (D->isInvalid() || D->isImplicit()) - return; - - if (auto *VD = dyn_cast(D)) { - if (shouldSkipChecking(VD)) - return; - } - - // Note: references to @_spi and @_implementationOnly declarations from - // @inlinable code are diagnosed by DeclAvailabilityChecker below. - auto *DC = D->getInnermostDeclContext(); - if (DC->getFragileFunctionKind().kind == FragileFunctionKind::None) - DeclVisitor::visit(D); + explicit DeclAvailabilityChecker(Decl *D) + : DC(D->getInnermostDeclContext()) { + FragileKind = DC->getFragileFunctionKind(); + Exported = isExported(D); } // Force all kinds to be handled at a lower level. @@ -1785,20 +1640,18 @@ class ExportabilityChecker : public DeclVisitor { void checkNamedPattern(const NamedPattern *NP, const llvm::DenseSet &seenVars) { const VarDecl *theVar = NP->getDecl(); - if (shouldSkipChecking(theVar)) - return; // Only check the type of individual variables if we didn't check an // enclosing TypedPattern. - if (seenVars.count(theVar) || theVar->isInvalid()) + if (seenVars.count(theVar)) return; - checkType(theVar->getInterfaceType(), /*typeRepr*/nullptr, theVar, - getDiagnoser(theVar)); + checkType(theVar->getValueInterfaceType(), /*typeRepr*/nullptr, theVar); } /// \see visitPatternBindingDecl - void checkTypedPattern(const TypedPattern *TP, + void checkTypedPattern(PatternBindingDecl *PBD, + const TypedPattern *TP, llvm::DenseSet &seenVars) { // FIXME: We need to figure out if this is a stored or computed property, // so we pull out some random VarDecl in the pattern. They're all going to @@ -1808,18 +1661,15 @@ class ExportabilityChecker : public DeclVisitor { seenVars.insert(V); anyVar = V; }); - if (!anyVar) - return; - if (shouldSkipChecking(anyVar)) - return; checkType(TP->hasType() ? TP->getType() : Type(), - TP->getTypeRepr(), anyVar, getDiagnoser(anyVar)); + TP->getTypeRepr(), anyVar ? (Decl *)anyVar : (Decl *)PBD); // Check the property wrapper types. - for (auto attr : anyVar->getAttachedPropertyWrappers()) - checkType(attr->getType(), attr->getTypeRepr(), anyVar, - getDiagnoser(anyVar, Reason::PropertyWrapper)); + if (anyVar) + for (auto attr : anyVar->getAttachedPropertyWrappers()) + checkType(attr->getType(), attr->getTypeRepr(), anyVar, + ExportabilityReason::PropertyWrapper); } void visitPatternBindingDecl(PatternBindingDecl *PBD) { @@ -1834,7 +1684,7 @@ class ExportabilityChecker : public DeclVisitor { auto *TP = dyn_cast(P); if (!TP) return; - checkTypedPattern(TP, seenVars); + checkTypedPattern(PBD, TP, seenVars); }); seenVars.clear(); } @@ -1843,22 +1693,22 @@ class ExportabilityChecker : public DeclVisitor { void visitTypeAliasDecl(TypeAliasDecl *TAD) { checkGenericParams(TAD, TAD); checkType(TAD->getUnderlyingType(), - TAD->getUnderlyingTypeRepr(), TAD, getDiagnoser(TAD)); + TAD->getUnderlyingTypeRepr(), TAD); } void visitAssociatedTypeDecl(AssociatedTypeDecl *assocType) { llvm::for_each(assocType->getInherited(), [&](TypeLoc requirement) { - checkType(requirement, assocType, getDiagnoser(assocType)); + checkType(requirement.getType(), requirement.getTypeRepr(), + assocType); }); checkType(assocType->getDefaultDefinitionType(), - assocType->getDefaultDefinitionTypeRepr(), assocType, - getDiagnoser(assocType)); + assocType->getDefaultDefinitionTypeRepr(), assocType); if (assocType->getTrailingWhereClause()) { forAllRequirementTypes(assocType, [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, assocType, getDiagnoser(assocType)); + checkType(type, typeRepr, assocType); }); } } @@ -1867,20 +1717,24 @@ class ExportabilityChecker : public DeclVisitor { checkGenericParams(nominal, nominal); llvm::for_each(nominal->getInherited(), - [&](TypeLoc nextInherited) { - checkType(nextInherited, nominal, getDiagnoser(nominal)); + [&](TypeLoc inherited) { + checkType(inherited.getType(), inherited.getTypeRepr(), + nominal, ExportabilityReason::General, + /*allowUnavailableProtocol=*/true); }); } void visitProtocolDecl(ProtocolDecl *proto) { llvm::for_each(proto->getInherited(), [&](TypeLoc requirement) { - checkType(requirement, proto, getDiagnoser(proto)); + checkType(requirement.getType(), requirement.getTypeRepr(), proto, + ExportabilityReason::General, + /*allowUnavailableProtocol=*/false); }); if (proto->getTrailingWhereClause()) { forAllRequirementTypes(proto, [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, proto, getDiagnoser(proto)); + checkType(type, typeRepr, proto); }); } } @@ -1889,76 +1743,82 @@ class ExportabilityChecker : public DeclVisitor { checkGenericParams(SD, SD); for (auto &P : *SD->getIndices()) { - checkType(P->getInterfaceType(), P->getTypeRepr(), SD, - getDiagnoser(SD)); + checkType(P->getInterfaceType(), P->getTypeRepr(), SD); } - checkType(SD->getElementInterfaceType(), SD->getElementTypeRepr(), SD, - getDiagnoser(SD)); + checkType(SD->getElementInterfaceType(), SD->getElementTypeRepr(), SD); } void visitAbstractFunctionDecl(AbstractFunctionDecl *fn) { checkGenericParams(fn, fn); for (auto *P : *fn->getParameters()) - checkType(P->getInterfaceType(), P->getTypeRepr(), fn, - getDiagnoser(fn)); + checkType(P->getInterfaceType(), P->getTypeRepr(), fn); } void visitFuncDecl(FuncDecl *FD) { visitAbstractFunctionDecl(FD); - checkType(FD->getResultInterfaceType(), FD->getResultTypeRepr(), FD, - getDiagnoser(FD)); + checkType(FD->getResultInterfaceType(), FD->getResultTypeRepr(), FD); } void visitEnumElementDecl(EnumElementDecl *EED) { if (!EED->hasAssociatedValues()) return; for (auto &P : *EED->getParameterList()) - checkType(P->getInterfaceType(), P->getTypeRepr(), EED, - getDiagnoser(EED)); + checkType(P->getInterfaceType(), P->getTypeRepr(), EED); } void checkConstrainedExtensionRequirements(ExtensionDecl *ED, - Reason reason) { + bool hasExportedMembers) { if (!ED->getTrailingWhereClause()) return; + + ExportabilityReason reason = + hasExportedMembers ? ExportabilityReason::ExtensionWithPublicMembers + : ExportabilityReason::ExtensionWithConditionalConformances; + forAllRequirementTypes(ED, [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, ED, getDiagnoser(ED, reason)); + checkType(type, typeRepr, ED, reason); }); } void visitExtensionDecl(ExtensionDecl *ED) { auto extendedType = ED->getExtendedNominal(); assert(extendedType && "valid extension with no extended type?"); - if (!extendedType || shouldSkipChecking(extendedType)) + if (!extendedType) return; - // FIXME: We should allow conforming to implementation-only protocols, - // but just hide that from interfaces. + // The rules here are tricky. + // + // 1) If the extension defines conformances, the conformed-to protocols + // must be exported. llvm::for_each(ED->getInherited(), - [&](TypeLoc nextInherited) { - checkType(nextInherited, ED, getDiagnoser(ED)); + [&](TypeLoc inherited) { + checkType(inherited.getType(), inherited.getTypeRepr(), + ED, ExportabilityReason::General, + /*allowUnavailableProtocol=*/true); }); - bool hasPublicMembers = llvm::any_of(ED->getMembers(), - [](const Decl *member) -> bool { + bool wasExported = Exported; + + // 2) If the extension contains exported members, the as-written + // extended type should be exportable. + bool hasExportedMembers = llvm::any_of(ED->getMembers(), + [](const Decl *member) -> bool { auto *valueMember = dyn_cast(member); if (!valueMember) return false; - return !shouldSkipChecking(valueMember); + return isExported(valueMember); }); - if (hasPublicMembers) { - checkType(ED->getExtendedType(), ED->getExtendedTypeRepr(), ED, - getDiagnoser(ED, Reason::ExtensionWithPublicMembers)); - } + Exported = wasExported && hasExportedMembers; + checkType(ED->getExtendedType(), ED->getExtendedTypeRepr(), ED, + ExportabilityReason::ExtensionWithPublicMembers); - if (hasPublicMembers || !ED->getInherited().empty()) { - Reason reason = - hasPublicMembers ? Reason::ExtensionWithPublicMembers - : Reason::ExtensionWithConditionalConformances; - checkConstrainedExtensionRequirements(ED, reason); - } + // 3) If the extension contains exported members or defines conformances, + // the 'where' clause must only name exported types. + Exported = wasExported && (hasExportedMembers || + !ED->getInherited().empty()); + checkConstrainedExtensionRequirements(ED, hasExportedMembers); } void checkPrecedenceGroup(const PrecedenceGroupDecl *PGD, @@ -1973,7 +1833,7 @@ class ExportabilityChecker : public DeclVisitor { auto diag = DE.diagnose(diagLoc, diag::decl_from_hidden_module, PGD->getDescriptiveKind(), PGD->getName(), - static_cast(Reason::General), M->getName(), + static_cast(ExportabilityReason::General), M->getName(), static_cast(DisallowedOriginKind::ImplementationOnly) ); if (refRange.isValid()) @@ -2007,267 +1867,6 @@ class ExportabilityChecker : public DeclVisitor { } }; -/// Diagnose declarations whose signatures refer to unavailable types. -class DeclAvailabilityChecker : public DeclVisitor { - DeclContext *DC; - Optional ExportReason; - FragileFunctionKind FragileKind; - - void checkType(Type type, const TypeRepr *typeRepr, const Decl *context, - bool allowUnavailableProtocol=false) { - // Don't bother checking errors. - if (type && type->hasError()) - return; - - // Check the TypeRepr for references to unavailable declarations. - if (typeRepr) { - DeclAvailabilityFlags flags = None; - - // We allow a type to conform to a protocol that is less available than - // the type itself. This enables a type to retroactively model or directly - // conform to a protocol only available on newer OSes and yet still be used on - // older OSes. - // - // To support this, inside inheritance clauses we allow references to - // protocols that are unavailable in the current type refinement context. - if (allowUnavailableProtocol) - flags |= DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol; - - diagnoseTypeReprAvailability(typeRepr, DC, ExportReason, FragileKind, - flags); - } - - // Check the type for references to unavailable conformances. - if (type) - diagnoseTypeAvailability(type, context->getLoc(), DC, - ExportReason, FragileKind); - } - - void checkGenericParams(const GenericContext *ownerCtx, - const ValueDecl *ownerDecl) { - if (!ownerCtx->isGenericContext()) - return; - - if (auto params = ownerCtx->getGenericParams()) { - for (auto param : *params) { - if (param->getInherited().empty()) - continue; - assert(param->getInherited().size() == 1); - auto inherited = param->getInherited().front(); - checkType(inherited.getType(), inherited.getTypeRepr(), ownerDecl); - } - } - - if (ownerCtx->getTrailingWhereClause()) { - forAllRequirementTypes(WhereClauseOwner( - const_cast(ownerCtx)), - [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, ownerDecl); - }); - } - } - -public: - explicit DeclAvailabilityChecker(Decl *D) - : DC(D->getInnermostDeclContext()) { - FragileKind = DC->getFragileFunctionKind(); - } - - void visit(Decl *D) { - if (D->isImplicit()) - return; - - DeclVisitor::visit(D); - } - - // Force all kinds to be handled at a lower level. - void visitDecl(Decl *D) = delete; - void visitValueDecl(ValueDecl *D) = delete; - -#define UNREACHABLE(KIND, REASON) \ - void visit##KIND##Decl(KIND##Decl *D) { \ - llvm_unreachable(REASON); \ - } - UNREACHABLE(Import, "not applicable") - UNREACHABLE(TopLevelCode, "not applicable") - UNREACHABLE(Module, "not applicable") - - UNREACHABLE(Param, "handled by the enclosing declaration") - UNREACHABLE(GenericTypeParam, "handled by the enclosing declaration") - UNREACHABLE(MissingMember, "handled by the enclosing declaration") -#undef UNREACHABLE - -#define UNINTERESTING(KIND) \ - void visit##KIND##Decl(KIND##Decl *D) {} - - UNINTERESTING(PrefixOperator) // Does not reference other decls. - UNINTERESTING(PostfixOperator) // Does not reference other decls. - UNINTERESTING(InfixOperator) // Does not reference other decls. - UNINTERESTING(IfConfig) // Not applicable. - UNINTERESTING(PoundDiagnostic) // Not applicable. - UNINTERESTING(EnumCase) // Handled at the EnumElement level. - UNINTERESTING(Destructor) // Always correct. - UNINTERESTING(Accessor) // Handled by the Var or Subscript. - UNINTERESTING(OpaqueType) // TODO - UNINTERESTING(PrecedenceGroup) // Doesn't reference anything with availability. - - // Handled at the PatternBinding level; if the pattern has a simple - // "name: TheType" form, we can get better results by diagnosing the TypeRepr. - UNINTERESTING(Var) - - /// \see visitPatternBindingDecl - void checkNamedPattern(const NamedPattern *NP, - const llvm::DenseSet &seenVars) { - const VarDecl *theVar = NP->getDecl(); - - // Only check the type of individual variables if we didn't check an - // enclosing TypedPattern. - if (seenVars.count(theVar)) - return; - - checkType(theVar->getValueInterfaceType(), /*typeRepr*/nullptr, theVar); - } - - /// \see visitPatternBindingDecl - void checkTypedPattern(PatternBindingDecl *PBD, - const TypedPattern *TP, - llvm::DenseSet &seenVars) { - // FIXME: We need to figure out if this is a stored or computed property, - // so we pull out some random VarDecl in the pattern. They're all going to - // be the same, but still, ick. - const VarDecl *anyVar = nullptr; - TP->forEachVariable([&](VarDecl *V) { - seenVars.insert(V); - anyVar = V; - }); - - checkType(TP->hasType() ? TP->getType() : Type(), - TP->getTypeRepr(), PBD); - - // Check the property wrapper types. - if (anyVar) - for (auto attr : anyVar->getAttachedPropertyWrappers()) - checkType(attr->getType(), attr->getTypeRepr(), anyVar); - } - - void visitPatternBindingDecl(PatternBindingDecl *PBD) { - llvm::DenseSet seenVars; - for (auto idx : range(PBD->getNumPatternEntries())) { - PBD->getPattern(idx)->forEachNode([&](const Pattern *P) { - if (auto *NP = dyn_cast(P)) { - checkNamedPattern(NP, seenVars); - return; - } - - auto *TP = dyn_cast(P); - if (!TP) - return; - checkTypedPattern(PBD, TP, seenVars); - }); - seenVars.clear(); - } - } - - void visitTypeAliasDecl(TypeAliasDecl *TAD) { - checkGenericParams(TAD, TAD); - checkType(TAD->getUnderlyingType(), - TAD->getUnderlyingTypeRepr(), TAD); - } - - void visitAssociatedTypeDecl(AssociatedTypeDecl *assocType) { - llvm::for_each(assocType->getInherited(), - [&](TypeLoc requirement) { - checkType(requirement.getType(), requirement.getTypeRepr(), - assocType); - }); - checkType(assocType->getDefaultDefinitionType(), - assocType->getDefaultDefinitionTypeRepr(), assocType); - - if (assocType->getTrailingWhereClause()) { - forAllRequirementTypes(assocType, - [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, assocType); - }); - } - } - - void visitNominalTypeDecl(const NominalTypeDecl *nominal) { - checkGenericParams(nominal, nominal); - - llvm::for_each(nominal->getInherited(), - [&](TypeLoc inherited) { - checkType(inherited.getType(), inherited.getTypeRepr(), - nominal, /*allowUnavailableProtocol=*/true); - }); - } - - void visitProtocolDecl(ProtocolDecl *proto) { - llvm::for_each(proto->getInherited(), - [&](TypeLoc requirement) { - checkType(requirement.getType(), requirement.getTypeRepr(), proto, - /*allowUnavailableProtocol=*/false); - }); - - if (proto->getTrailingWhereClause()) { - forAllRequirementTypes(proto, [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, proto); - }); - } - } - - void visitSubscriptDecl(SubscriptDecl *SD) { - checkGenericParams(SD, SD); - - for (auto &P : *SD->getIndices()) { - checkType(P->getInterfaceType(), P->getTypeRepr(), SD); - } - checkType(SD->getElementInterfaceType(), SD->getElementTypeRepr(), SD); - } - - void visitAbstractFunctionDecl(AbstractFunctionDecl *fn) { - checkGenericParams(fn, fn); - - for (auto *P : *fn->getParameters()) - checkType(P->getInterfaceType(), P->getTypeRepr(), fn); - } - - void visitFuncDecl(FuncDecl *FD) { - visitAbstractFunctionDecl(FD); - checkType(FD->getResultInterfaceType(), FD->getResultTypeRepr(), FD); - } - - void visitEnumElementDecl(EnumElementDecl *EED) { - if (!EED->hasAssociatedValues()) - return; - for (auto &P : *EED->getParameterList()) - checkType(P->getInterfaceType(), P->getTypeRepr(), EED); - } - - void checkConstrainedExtensionRequirements(ExtensionDecl *ED) { - if (!ED->getTrailingWhereClause()) - return; - forAllRequirementTypes(ED, [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, ED); - }); - } - - void visitExtensionDecl(ExtensionDecl *ED) { - auto extendedType = ED->getExtendedNominal(); - assert(extendedType && "valid extension with no extended type?"); - if (!extendedType) - return; - - llvm::for_each(ED->getInherited(), - [&](TypeLoc inherited) { - checkType(inherited.getType(), inherited.getTypeRepr(), - ED, /*allowUnavailableProtocol=*/true); - }); - - checkType(ED->getExtendedType(), ED->getExtendedTypeRepr(), ED); - checkConstrainedExtensionRequirements(ED); - } -}; - } // end anonymous namespace static void checkExtensionGenericParamAccess(const ExtensionDecl *ED) { @@ -2318,6 +1917,9 @@ void swift::checkAccessControl(Decl *D) { checkExtensionGenericParamAccess(ED); } - ExportabilityChecker().visit(D); + //ExportabilityChecker().visit(D); + if (D->isImplicit() || isa(D)) + return; + DeclAvailabilityChecker(D).visit(D); } diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 79d4728d1ec95..904916c4f5adb 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -2432,22 +2432,25 @@ class AvailabilityWalker : public ASTWalker { return skipChildren(); } if (auto T = dyn_cast(E)) { - if (!T->isImplicit()) - if (auto type = T->getType()) - diagnoseTypeAvailability(type, E->getLoc(), DC, - ExportReason, FragileKind); + if (!T->isImplicit()) { + diagnoseTypeAvailability(T->getTypeRepr(), T->getType(), E->getLoc(), + DC, ExportReason, FragileKind); + } } if (auto CE = dyn_cast(E)) { for (auto *param : *CE->getParameters()) { - diagnoseTypeAvailability(param->getInterfaceType(), E->getLoc(), DC, - ExportReason, FragileKind); + diagnoseTypeAvailability(param->getTypeRepr(), param->getInterfaceType(), + E->getLoc(), DC, ExportReason, FragileKind); } - diagnoseTypeAvailability(CE->getResultType(), E->getLoc(), DC, + diagnoseTypeAvailability(CE->hasExplicitResultType() + ? CE->getExplicitResultTypeRepr() + : nullptr, + CE->getResultType(), E->getLoc(), DC, ExportReason, FragileKind); } - if (auto IE = dyn_cast(E)) { - diagnoseTypeAvailability(IE->getCastType(), E->getLoc(), DC, - ExportReason, FragileKind); + if (auto CE = dyn_cast(E)) { + diagnoseTypeAvailability(CE->getCastTypeRepr(), CE->getCastType(), + E->getLoc(), DC, ExportReason, FragileKind); } return visitChildren(); @@ -2460,11 +2463,6 @@ class AvailabilityWalker : public ASTWalker { return E; } - bool walkToTypeReprPre(TypeRepr *T) override { - diagnoseTypeReprAvailability(T, DC, ExportReason, FragileKind); - return false; - } - bool diagAvailability(ConcreteDeclRef declRef, SourceRange R, const ApplyExpr *call = nullptr, DeclAvailabilityFlags flags = None) const; @@ -2907,9 +2905,8 @@ class StmtAvailabilityWalker : public ASTWalker { std::pair walkToPatternPre(Pattern *P) override { if (auto *IP = dyn_cast(P)) - if (auto T = IP->getCastType()) - diagnoseTypeAvailability(T, P->getLoc(), DC, - ExportReason, FragileKind); + diagnoseTypeAvailability(IP->getCastType(), P->getLoc(), DC, + ExportReason, FragileKind); return std::make_pair(true, P); } @@ -3004,6 +3001,8 @@ bool swift::diagnoseTypeReprAvailability(const TypeRepr *T, DeclContext *DC, Optional reason, FragileFunctionKind fragileKind, DeclAvailabilityFlags flags) { + if (!T) + return false; TypeReprAvailabilityWalker walker(DC, reason, fragileKind, flags); const_cast(T)->walk(walker); return walker.foundAnyIssues; @@ -3016,21 +3015,38 @@ class ProblematicTypeFinder : public TypeDeclFinder { DeclContext *DC; Optional ExportReason; FragileFunctionKind FragileKind; + DeclAvailabilityFlags Flags; public: ProblematicTypeFinder(SourceLoc Loc, DeclContext *DC, Optional ExportReason, - FragileFunctionKind FragileKind) + FragileFunctionKind FragileKind, + DeclAvailabilityFlags Flags) : Loc(Loc), DC(DC), ExportReason(ExportReason), - FragileKind(FragileKind) {} + FragileKind(FragileKind), + Flags(Flags) {} + + void visitTypeDecl(TypeDecl *decl) { + // We only need to diagnose exportability here. Availability was + // already checked on the TypeRepr. + if (FragileKind.kind != FragileFunctionKind::None || + ExportReason.hasValue()) { + TypeChecker::diagnoseDeclRefExportability(Loc, decl, DC, + ExportReason, FragileKind); + } + } Action visitNominalType(NominalType *ty) override { + visitTypeDecl(ty->getDecl()); + /// FIXME return Action::Continue; } Action visitBoundGenericType(BoundGenericType *ty) override { + visitTypeDecl(ty->getDecl()); + ModuleDecl *useModule = DC->getParentModule(); auto subs = ty->getContextSubstitutionMap(useModule, ty->getDecl()); (void) diagnoseSubstitutionMapAvailability(Loc, subs, DC, @@ -3040,6 +3056,8 @@ class ProblematicTypeFinder : public TypeDeclFinder { } Action visitTypeAliasType(TypeAliasType *ty) override { + visitTypeDecl(ty->getDecl()); + auto subs = ty->getSubstitutionMap(); (void) diagnoseSubstitutionMapAvailability(Loc, subs, DC, ExportReason, @@ -3075,8 +3093,21 @@ class ProblematicTypeFinder : public TypeDeclFinder { void swift::diagnoseTypeAvailability(Type T, SourceLoc loc, DeclContext *DC, Optional reason, - FragileFunctionKind fragileKind) { - T.walk(ProblematicTypeFinder(loc, DC, reason, fragileKind)); + FragileFunctionKind fragileKind, + DeclAvailabilityFlags flags) { + if (!T) + return; + T.walk(ProblematicTypeFinder(loc, DC, reason, fragileKind, flags)); +} + +void swift::diagnoseTypeAvailability(const TypeRepr *TR, Type T, SourceLoc loc, + DeclContext *DC, + Optional reason, + FragileFunctionKind fragileKind, + DeclAvailabilityFlags flags) { + if (diagnoseTypeReprAvailability(TR, DC, reason, fragileKind, flags)) + return; + diagnoseTypeAvailability(T, loc, DC, reason, fragileKind, flags); } bool diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index e19a9fe25b61a..40c500bde957b 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -86,7 +86,16 @@ bool diagnoseTypeReprAvailability(const TypeRepr *T, DeclContext *DC, /// Diagnose uses of unavailable conformances in types. void diagnoseTypeAvailability(Type T, SourceLoc loc, DeclContext *DC, Optional reason, - FragileFunctionKind fragileKind); + FragileFunctionKind fragileKind, + DeclAvailabilityFlags flags = None); + +/// Checks both a TypeRepr and a Type, but avoids emitting duplicate +/// diagnostics by only checking the Type if the TypeRepr succeeded. +void diagnoseTypeAvailability(const TypeRepr *TR, Type T, SourceLoc loc, + DeclContext *DC, + Optional reason, + FragileFunctionKind fragileKind, + DeclAvailabilityFlags flags = None); bool diagnoseConformanceAvailability(SourceLoc loc, @@ -110,7 +119,7 @@ bool diagnoseDeclAvailability(const ValueDecl *Decl, DeclContext *DC, Optional reason, FragileFunctionKind fragileKind, - DeclAvailabilityFlags Options); + DeclAvailabilityFlags flags = None); void diagnoseUnavailableOverride(ValueDecl *override, const ValueDecl *base, diff --git a/test/SPI/local_spi_decls.swift b/test/SPI/local_spi_decls.swift index 3b4e68142d525..cc8abe6baeb6c 100644 --- a/test/SPI/local_spi_decls.swift +++ b/test/SPI/local_spi_decls.swift @@ -13,9 +13,9 @@ @_spi() public func emptyParensSPI() {} // expected-error {{expected an SPI identifier as subject of the '@_spi' attribute}} @_spi(set) public func keywordSPI() {} // expected-error {{expected an SPI identifier as subject of the '@_spi' attribute}} -@_spi(S) public class SPIClass { // expected-note 5 {{type declared here}} - // expected-note @-1 3 {{class 'SPIClass' is not '@usableFromInline' or public}} - // expected-note @-2 {{class 'SPIClass' is not public}} +@_spi(S) public class SPIClass { // expected-note 6 {{type declared here}} + // expected-note@-1 3 {{class 'SPIClass' is not '@usableFromInline' or public}} + // expected-note@-2 {{class 'SPIClass' is not public}} public init() {} // expected-note@-1 2 {{initializer 'init()' is not '@usableFromInline' or public}} // expected-note@-2 {{initializer 'init()' is not public}} @@ -38,6 +38,7 @@ func inlinable() -> SPIClass { // expected-error {{class 'SPIClass' is '@_spi' a } @_spi(S) public struct SPIStruct { // expected-note 2 {{struct 'SPIStruct' is not '@usableFromInline' or public}} +// expected-note@-1 2 {{type declared here}} // FIXME: Misleading diagnostic here public init() {} // expected-note@-1 2 {{initializer 'init()' is not '@usableFromInline' or public}} diff --git a/test/Sema/spi-in-decls.swift b/test/Sema/spi-in-decls.swift index e83c88a7cc660..e3fd458bf86fd 100644 --- a/test/Sema/spi-in-decls.swift +++ b/test/Sema/spi-in-decls.swift @@ -22,7 +22,7 @@ extension NormalClass: NormalProto { } @_spi(X) -public struct BadStruct {} // expected-note 27 {{type declared here}} +public struct BadStruct {} // expected-note 34 {{type declared here}} @_spi(X) public protocol BadProto {} // expected-note 20 {{type declared here}} @_spi(X) From e10df96983f337dda96be6797b1e0be7fb676391 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 19 Oct 2020 15:37:09 -0400 Subject: [PATCH 576/745] Sema: Fold TypeChecker::isDeclAvailable() into its callers --- lib/Sema/TypeCheckAvailability.cpp | 39 +++++++++--------------------- lib/Sema/TypeCheckProtocol.cpp | 15 ++++++------ lib/Sema/TypeChecker.h | 11 --------- 3 files changed, 19 insertions(+), 46 deletions(-) diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 904916c4f5adb..b0e5570af7908 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -712,29 +712,6 @@ TypeChecker::overApproximateAvailabilityAtLocation(SourceLoc loc, return OverApproximateContext; } -bool TypeChecker::isDeclAvailable(const Decl *D, SourceLoc referenceLoc, - const DeclContext *referenceDC, - AvailabilityContext &OutAvailableInfo) { - ASTContext &Context = referenceDC->getASTContext(); - - AvailabilityContext safeRangeUnderApprox{ - AvailabilityInference::availableRange(D, Context)}; - AvailabilityContext runningOSOverApprox = - overApproximateAvailabilityAtLocation(referenceLoc, referenceDC); - - // The reference is safe if an over-approximation of the running OS - // versions is fully contained within an under-approximation - // of the versions on which the declaration is available. If this - // containment cannot be guaranteed, we say the reference is - // not available. - if (!(runningOSOverApprox.isContainedIn(safeRangeUnderApprox))) { - OutAvailableInfo = safeRangeUnderApprox; - return false; - } - - return true; -} - Optional TypeChecker::checkDeclarationAvailability(const Decl *D, SourceLoc referenceLoc, const DeclContext *referenceDC) { @@ -749,12 +726,20 @@ TypeChecker::checkDeclarationAvailability(const Decl *D, SourceLoc referenceLoc, return None; } - auto safeRangeUnderApprox = AvailabilityContext::neverAvailable(); - if (isDeclAvailable(D, referenceLoc, referenceDC, safeRangeUnderApprox)) { + AvailabilityContext runningOSOverApprox = + overApproximateAvailabilityAtLocation(referenceLoc, referenceDC); + + AvailabilityContext safeRangeUnderApprox{ + AvailabilityInference::availableRange(D, Context)}; + + // The reference is safe if an over-approximation of the running OS + // versions is fully contained within an under-approximation + // of the versions on which the declaration is available. If this + // containment cannot be guaranteed, we say the reference is + // not available. + if (runningOSOverApprox.isContainedIn(safeRangeUnderApprox)) return None; - } - // safeRangeUnderApprox now holds the safe range. VersionRange version = safeRangeUnderApprox.getOSVersion(); return UnavailabilityReason::requiresVersionRange(version); } diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 01cf489a25a6b..48ff739eb8306 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -5455,14 +5455,13 @@ static void inferStaticInitializeObjCMetadata(ClassDecl *classDecl) { // only statically initialize the Objective-C metadata when running on // a new-enough OS. if (auto sourceFile = classDecl->getParentSourceFile()) { - AvailabilityContext availableInfo = AvailabilityContext::alwaysAvailable(); - for (Decl *enclosingDecl = classDecl; enclosingDecl; - enclosingDecl = enclosingDecl->getDeclContext() - ->getInnermostDeclarationDeclContext()) { - if (!TypeChecker::isDeclAvailable(enclosingDecl, SourceLoc(), sourceFile, - availableInfo)) - return; - } + AvailabilityContext safeRangeUnderApprox{ + AvailabilityInference::availableRange(classDecl, ctx)}; + AvailabilityContext runningOSOverApprox = + AvailabilityContext::forDeploymentTarget(ctx); + + if (!runningOSOverApprox.isContainedIn(safeRangeUnderApprox)) + return; } // Infer @_staticInitializeObjCMetadata. diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index beb69dda5b82b..77bfc6f10ff47 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1011,17 +1011,6 @@ TypeRefinementContext *getOrBuildTypeRefinementContext(SourceFile *SF); Optional> diagnosticIfDeclCannotBePotentiallyUnavailable(const Decl *D); -/// Checks whether a declaration is available when referred to at the given -/// location (this reference location must be in the passed-in -/// reference DeclContext). -/// If the declaration is available, return true. -/// If the declaration is not available, return false and write the -/// declaration's availability info to the out parameter -/// \p OutAvailableRange. -bool isDeclAvailable(const Decl *D, SourceLoc referenceLoc, - const DeclContext *referenceDC, - AvailabilityContext &OutAvailableRange); - /// Checks whether a declaration should be considered unavailable when /// referred to at the given location and, if so, returns the reason why the /// declaration is unavailable. Returns None is the declaration is From c2ed3476658ec9adad8895694a36f2abf85f7a9e Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 19 Oct 2020 18:11:20 -0400 Subject: [PATCH 577/745] Sema: Pass ValueDecl instead of ConcreteDeclRef in a few places in availability checking The substitution map is checked elsewhere. --- lib/Sema/ResilienceDiagnostics.cpp | 37 +++++++++--------------------- lib/Sema/TypeCheckAvailability.cpp | 4 ++-- lib/Sema/TypeChecker.h | 5 ++-- 3 files changed, 16 insertions(+), 30 deletions(-) diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index 14b9ba6c23098..3c459162d35f0 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -28,12 +28,11 @@ using namespace swift; bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc, - ConcreteDeclRef declRef, + const ValueDecl *D, const DeclContext *DC, FragileFunctionKind Kind) { assert(Kind.kind != FragileFunctionKind::None); - const ValueDecl *D = declRef.getDecl(); // Do some important fast-path checks that apply to all cases. // Type parameters are OK. @@ -48,7 +47,7 @@ bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc, // Skip this check for accessors because the associated property or subscript // will also be checked, and will provide a better error message. if (!isa(D)) - if (diagnoseDeclRefExportability(loc, declRef, DC, + if (diagnoseDeclRefExportability(loc, D, DC, None, Kind)) return true; @@ -141,11 +140,12 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, return (downgradeToWarning == DowngradeToWarning::No); } -static bool diagnoseDeclExportability(SourceLoc loc, const ValueDecl *D, - const SourceFile &userSF, - const DeclContext *userDC, - Optional reason, - FragileFunctionKind fragileKind) { +bool +TypeChecker::diagnoseDeclRefExportability(SourceLoc loc, + const ValueDecl *D, + const DeclContext *DC, + Optional reason, + FragileFunctionKind fragileKind) { if (fragileKind.kind == FragileFunctionKind::None && !reason.hasValue()) return false; @@ -153,7 +153,9 @@ static bool diagnoseDeclExportability(SourceLoc loc, const ValueDecl *D, auto downgradeToWarning = DowngradeToWarning::No; auto originKind = getDisallowedOriginKind( - D, userSF, userDC->getInnermostDeclarationDeclContext(), + D, + *DC->getParentSourceFile(), + DC->getInnermostDeclarationDeclContext(), downgradeToWarning); if (originKind == DisallowedOriginKind::None) return false; @@ -211,20 +213,3 @@ TypeChecker::diagnoseConformanceExportability(SourceLoc loc, static_cast(originKind)); return true; } - -bool -TypeChecker::diagnoseDeclRefExportability(SourceLoc loc, - ConcreteDeclRef declRef, - const DeclContext *DC, - Optional reason, - FragileFunctionKind fragileKind) { - // We're only interested in diagnosing uses from source files. - auto userSF = DC->getParentSourceFile(); - if (!userSF) - return false; - - const ValueDecl *D = declRef.getDecl(); - if (diagnoseDeclExportability(loc, D, *userSF, DC, reason, fragileKind)) - return true; - return false; -} diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index b0e5570af7908..37391769bb587 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -2653,11 +2653,11 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, if (FragileKind.kind != FragileFunctionKind::None) { if (R.isValid()) - if (TypeChecker::diagnoseInlinableDeclRef(R.Start, declRef, DC, FragileKind)) + if (TypeChecker::diagnoseInlinableDeclRef(R.Start, D, DC, FragileKind)) return true; } else if (ExportReason.hasValue()) { if (R.isValid()) - if (TypeChecker::diagnoseDeclRefExportability(R.Start, declRef, DC, + if (TypeChecker::diagnoseDeclRefExportability(R.Start, D, DC, ExportReason, FragileKind)) return true; } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 77bfc6f10ff47..84fba621d72e3 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -947,7 +947,7 @@ DeclName getObjectLiteralConstructorName(ASTContext &ctx, ModuleDecl *getStdlibModule(const DeclContext *dc); /// \name Resilience diagnostics -bool diagnoseInlinableDeclRef(SourceLoc loc, ConcreteDeclRef declRef, +bool diagnoseInlinableDeclRef(SourceLoc loc, const ValueDecl *D, const DeclContext *DC, FragileFunctionKind Kind); Expr *buildDefaultInitializer(Type type); @@ -959,7 +959,8 @@ bool diagnoseInlinableDeclRefAccess(SourceLoc loc, const ValueDecl *D, /// Given that a declaration is used from a particular context which /// exposes it in the interface of the current module, diagnose if it cannot /// reasonably be shared. -bool diagnoseDeclRefExportability(SourceLoc loc, ConcreteDeclRef declRef, +bool diagnoseDeclRefExportability(SourceLoc loc, + const ValueDecl *D, const DeclContext *DC, Optional exportability, FragileFunctionKind fragileKind); From 9f08ab3918a6f83d34671e8c72c4c002e595f801 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 19 Oct 2020 18:45:46 -0400 Subject: [PATCH 578/745] Sema: Remove an unnecessary overload of TypeChecker::diagnosePotentialUnavailability() --- lib/Sema/TypeCheckAvailability.cpp | 15 ++++----------- lib/Sema/TypeChecker.h | 8 -------- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 37391769bb587..154db4ef00bdc 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -744,14 +744,6 @@ TypeChecker::checkDeclarationAvailability(const Decl *D, SourceLoc referenceLoc, return UnavailabilityReason::requiresVersionRange(version); } -void TypeChecker::diagnosePotentialUnavailability( - const ValueDecl *D, SourceRange ReferenceRange, - const DeclContext *ReferenceDC, - const UnavailabilityReason &Reason) { - diagnosePotentialUnavailability(D, D->getName(), ReferenceRange, - ReferenceDC, Reason); -} - /// A class that walks the AST to find the innermost (i.e., deepest) node that /// contains a target SourceRange and matches a particular criterion. /// This class finds the innermost nodes of interest by walking @@ -1382,8 +1374,9 @@ void TypeChecker::diagnosePotentialOpaqueTypeUnavailability( } void TypeChecker::diagnosePotentialUnavailability( - const Decl *D, DeclName Name, SourceRange ReferenceRange, - const DeclContext *ReferenceDC, const UnavailabilityReason &Reason) { + const ValueDecl *D, SourceRange ReferenceRange, + const DeclContext *ReferenceDC, + const UnavailabilityReason &Reason) { ASTContext &Context = ReferenceDC->getASTContext(); // We only emit diagnostics for API unavailability, not for explicitly @@ -1398,7 +1391,7 @@ void TypeChecker::diagnosePotentialUnavailability( auto Err = Context.Diags.diagnose( ReferenceRange.Start, diag::availability_decl_only_version_newer, - Name, prettyPlatformString(targetPlatform(Context.LangOpts)), + D->getName(), prettyPlatformString(targetPlatform(Context.LangOpts)), Reason.getRequiredOSVersionRange().getLowerEndpoint()); // Direct a fixit to the error if an existing guard is nearly-correct diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 84fba621d72e3..146a47780cf85 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1033,14 +1033,6 @@ void diagnosePotentialUnavailability(const ValueDecl *D, const DeclContext *ReferenceDC, const UnavailabilityReason &Reason); -// Emits a diagnostic, if necessary, for a reference to a declaration -// that is potentially unavailable at the given source location, using -// Name as the diagnostic name. -void diagnosePotentialUnavailability(const Decl *D, DeclName Name, - SourceRange ReferenceRange, - const DeclContext *ReferenceDC, - const UnavailabilityReason &Reason); - void diagnosePotentialOpaqueTypeUnavailability(SourceRange ReferenceRange, const DeclContext *ReferenceDC, From 3044c057e8b2a11f14b38d6445c430c54ca11802 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 19 Oct 2020 22:51:59 -0400 Subject: [PATCH 579/745] Sema: Wrap up some availability-related state in a new ExportContext type The ExportContext describes the restrictions, if any, on what declarations can be uttered in a specific place in the program. Here, the place is either the signature of a declaration, or the body of a function. --- lib/Sema/ResilienceDiagnostics.cpp | 53 +++--- lib/Sema/TypeCheckAccess.cpp | 75 +-------- lib/Sema/TypeCheckAvailability.cpp | 258 ++++++++++++++++++----------- lib/Sema/TypeCheckAvailability.h | 62 ++++--- lib/Sema/TypeCheckStmt.cpp | 11 +- lib/Sema/TypeChecker.h | 16 +- 6 files changed, 247 insertions(+), 228 deletions(-) diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index 3c459162d35f0..17e7e821c6f94 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -29,9 +29,10 @@ using namespace swift; bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc, const ValueDecl *D, - const DeclContext *DC, - FragileFunctionKind Kind) { - assert(Kind.kind != FragileFunctionKind::None); + ExportContext where) { + auto fragileKind = where.getFragileFunctionKind(); + if (fragileKind.kind == FragileFunctionKind::None) + return false; // Do some important fast-path checks that apply to all cases. @@ -40,15 +41,14 @@ bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc, return false; // Check whether the declaration is accessible. - if (diagnoseInlinableDeclRefAccess(loc, D, DC, Kind)) + if (diagnoseInlinableDeclRefAccess(loc, D, where)) return true; // Check whether the declaration comes from a publically-imported module. // Skip this check for accessors because the associated property or subscript // will also be checked, and will provide a better error message. if (!isa(D)) - if (diagnoseDeclRefExportability(loc, D, DC, - None, Kind)) + if (diagnoseDeclRefExportability(loc, D, where)) return true; return false; @@ -56,9 +56,10 @@ bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc, bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, const ValueDecl *D, - const DeclContext *DC, - FragileFunctionKind Kind) { - assert(Kind.kind != FragileFunctionKind::None); + ExportContext where) { + auto *DC = where.getDeclContext(); + auto fragileKind = where.getFragileFunctionKind(); + assert(fragileKind.kind != FragileFunctionKind::None); // Local declarations are OK. if (D->getDeclContext()->isLocalContext()) @@ -66,7 +67,7 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, // Public declarations or SPI used from SPI are OK. if (D->getFormalAccessScope(/*useDC=*/nullptr, - Kind.allowUsableFromInline).isPublic() && + fragileKind.allowUsableFromInline).isPublic() && !(D->isSPI() && !DC->getInnermostDeclarationDeclContext()->isSPI())) return false; @@ -126,10 +127,10 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, loc, diagID, D->getDescriptiveKind(), diagName, D->getFormalAccessScope().accessLevelForDiagnostics(), - static_cast(Kind.kind), + static_cast(fragileKind.kind), isAccessor); - if (Kind.allowUsableFromInline) { + if (fragileKind.allowUsableFromInline) { Context.Diags.diagnose(D, diag::resilience_decl_declared_here, D->getDescriptiveKind(), diagName, isAccessor); } else { @@ -143,15 +144,15 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, bool TypeChecker::diagnoseDeclRefExportability(SourceLoc loc, const ValueDecl *D, - const DeclContext *DC, - Optional reason, - FragileFunctionKind fragileKind) { - if (fragileKind.kind == FragileFunctionKind::None && !reason.hasValue()) + ExportContext where) { + if (!where.mustOnlyReferenceExportedDecls()) return false; auto definingModule = D->getModuleContext(); auto downgradeToWarning = DowngradeToWarning::No; + + auto *DC = where.getDeclContext(); auto originKind = getDisallowedOriginKind( D, *DC->getParentSourceFile(), @@ -162,6 +163,9 @@ TypeChecker::diagnoseDeclRefExportability(SourceLoc loc, ASTContext &ctx = definingModule->getASTContext(); + auto fragileKind = where.getFragileFunctionKind(); + auto reason = where.getExportabilityReason(); + if (fragileKind.kind == FragileFunctionKind::None) { auto errorOrWarning = downgradeToWarning == DowngradeToWarning::Yes? diag::decl_from_hidden_module_warn: @@ -187,24 +191,25 @@ TypeChecker::diagnoseDeclRefExportability(SourceLoc loc, bool TypeChecker::diagnoseConformanceExportability(SourceLoc loc, const RootProtocolConformance *rootConf, - const SourceFile &userSF, - const DeclContext *userDC, - Optional reason, - FragileFunctionKind fragileKind) { - if (fragileKind.kind == FragileFunctionKind::None && !reason.hasValue()) + ExportContext where) { + if (!where.mustOnlyReferenceExportedDecls()) return false; + auto *DC = where.getDeclContext(); auto originKind = getDisallowedOriginKind( rootConf->getDeclContext()->getAsDecl(), - userSF, userDC->getInnermostDeclarationDeclContext()); + *DC->getParentSourceFile(), + DC->getInnermostDeclarationDeclContext()); if (originKind == DisallowedOriginKind::None) return false; + ModuleDecl *M = rootConf->getDeclContext()->getParentModule(); + ASTContext &ctx = M->getASTContext(); + + auto reason = where.getExportabilityReason(); if (!reason.hasValue()) reason = ExportabilityReason::General; - ModuleDecl *M = rootConf->getDeclContext()->getParentModule(); - ASTContext &ctx = M->getASTContext(); ctx.Diags.diagnose(loc, diag::conformance_from_implementation_only_module, rootConf->getType(), rootConf->getProtocol()->getName(), diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index d5d662d794de4..c31bafb8a922a 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -1483,63 +1483,11 @@ swift::getDisallowedOriginKind(const Decl *decl, return DisallowedOriginKind::None; }; -static bool isExported(const ValueDecl *VD) { - if (VD->getAttrs().hasAttribute()) - return false; - - // Is this part of the module's API or ABI? - AccessScope accessScope = - VD->getFormalAccessScope(nullptr, - /*treatUsableFromInlineAsPublic*/true); - if (accessScope.isPublic()) - return true; - - // Is this a stored property in a non-resilient struct or class? - auto *property = dyn_cast(VD); - if (!property || !property->hasStorage() || property->isStatic()) - return false; - auto *parentNominal = dyn_cast(property->getDeclContext()); - if (!parentNominal || parentNominal->isResilient()) - return false; - - // Is that struct or class part of the module's API or ABI? - AccessScope parentAccessScope = parentNominal->getFormalAccessScope( - nullptr, /*treatUsableFromInlineAsPublic*/true); - if (parentAccessScope.isPublic()) - return true; - - return false; -} - -static bool isExported(Decl *D) { - if (auto *VD = dyn_cast(D)) { - return isExported(VD); - } - if (auto *PBD = dyn_cast(D)) { - for (unsigned i = 0, e = PBD->getNumPatternEntries(); i < e; ++i) { - if (auto *VD = PBD->getAnchoringVarDecl(i)) - return isExported(VD); - } - - return false; - } - if (auto *ED = dyn_cast(D)) { - if (auto *NTD = ED->getExtendedNominal()) - return isExported(NTD); - - return false; - } - - return true; -} - namespace { /// Diagnose declarations whose signatures refer to unavailable types. class DeclAvailabilityChecker : public DeclVisitor { - DeclContext *DC; - FragileFunctionKind FragileKind; - bool Exported; + ExportContext Where; void checkType(Type type, const TypeRepr *typeRepr, const Decl *context, ExportabilityReason reason=ExportabilityReason::General, @@ -1548,10 +1496,6 @@ class DeclAvailabilityChecker : public DeclVisitor { if (type && type->hasError()) return; - Optional optReason; - if (Exported) - optReason = reason; - DeclAvailabilityFlags flags = None; // We allow a type to conform to a protocol that is less available than @@ -1568,8 +1512,8 @@ class DeclAvailabilityChecker : public DeclVisitor { if (auto *varDecl = dyn_cast(context)) loc = varDecl->getNameLoc(); - diagnoseTypeAvailability(typeRepr, type, loc, DC, - optReason, FragileKind, flags); + diagnoseTypeAvailability(typeRepr, type, loc, + Where.forReason(reason), flags); } void checkGenericParams(const GenericContext *ownerCtx, @@ -1598,10 +1542,7 @@ class DeclAvailabilityChecker : public DeclVisitor { public: explicit DeclAvailabilityChecker(Decl *D) - : DC(D->getInnermostDeclContext()) { - FragileKind = DC->getFragileFunctionKind(); - Exported = isExported(D); - } + : Where(ExportContext::forDeclSignature(D)) {} // Force all kinds to be handled at a lower level. void visitDecl(Decl *D) = delete; @@ -1798,7 +1739,7 @@ class DeclAvailabilityChecker : public DeclVisitor { /*allowUnavailableProtocol=*/true); }); - bool wasExported = Exported; + auto wasWhere = Where; // 2) If the extension contains exported members, the as-written // extended type should be exportable. @@ -1810,14 +1751,14 @@ class DeclAvailabilityChecker : public DeclVisitor { return isExported(valueMember); }); - Exported = wasExported && hasExportedMembers; + Where = wasWhere.forExported(hasExportedMembers); checkType(ED->getExtendedType(), ED->getExtendedTypeRepr(), ED, ExportabilityReason::ExtensionWithPublicMembers); // 3) If the extension contains exported members or defines conformances, // the 'where' clause must only name exported types. - Exported = wasExported && (hasExportedMembers || - !ED->getInherited().empty()); + Where = wasWhere.forExported(hasExportedMembers || + !ED->getInherited().empty()); checkConstrainedExtensionRequirements(ED, hasExportedMembers); } diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 154db4ef00bdc..692c4a06d8e7c 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -37,6 +37,111 @@ #include "llvm/Support/SaveAndRestore.h" using namespace swift; +ExportContext::ExportContext(DeclContext *DC, FragileFunctionKind kind, + bool spi, bool exported) + : DC(DC), FragileKind(kind) { + SPI = spi; + Exported = exported; + Reason = ExportabilityReason::General; +} + +bool swift::isExported(const ValueDecl *VD) { + if (VD->getAttrs().hasAttribute()) + return false; + + // Is this part of the module's API or ABI? + AccessScope accessScope = + VD->getFormalAccessScope(nullptr, + /*treatUsableFromInlineAsPublic*/true); + if (accessScope.isPublic()) + return true; + + // Is this a stored property in a non-resilient struct or class? + auto *property = dyn_cast(VD); + if (!property || !property->hasStorage() || property->isStatic()) + return false; + auto *parentNominal = dyn_cast(property->getDeclContext()); + if (!parentNominal || parentNominal->isResilient()) + return false; + + // Is that struct or class part of the module's API or ABI? + AccessScope parentAccessScope = parentNominal->getFormalAccessScope( + nullptr, /*treatUsableFromInlineAsPublic*/true); + if (parentAccessScope.isPublic()) + return true; + + return false; +} + +bool swift::isExported(const Decl *D) { + if (auto *VD = dyn_cast(D)) { + return isExported(VD); + } + if (auto *PBD = dyn_cast(D)) { + for (unsigned i = 0, e = PBD->getNumPatternEntries(); i < e; ++i) { + if (auto *VD = PBD->getAnchoringVarDecl(i)) + return isExported(VD); + } + + return false; + } + if (auto *ED = dyn_cast(D)) { + if (auto *NTD = ED->getExtendedNominal()) + return isExported(NTD); + + return false; + } + + return true; +} + +ExportContext ExportContext::forDeclSignature(Decl *D) { + auto *DC = D->getInnermostDeclContext(); + auto fragileKind = DC->getFragileFunctionKind(); + bool spi = D->isSPI(); + bool exported = ::isExported(D); + + return ExportContext(DC, fragileKind, spi, exported); +} + +ExportContext ExportContext::forFunctionBody(DeclContext *DC) { + ; + auto fragileKind = DC->getFragileFunctionKind(); + + bool spi = false; + bool exported = false; + + if (auto *D = DC->getInnermostDeclarationDeclContext()) { + spi = D->isSPI(); + } else { + assert(fragileKind.kind == FragileFunctionKind::None); + } + + return ExportContext(DC, fragileKind, spi, exported); +} + +ExportContext ExportContext::forReason(ExportabilityReason reason) const { + auto copy = *this; + copy.Reason = reason; + return copy; +} + +ExportContext ExportContext::forExported(bool exported) const { + auto copy = *this; + copy.Exported = isExported() && exported; + return copy; +} + +bool ExportContext::mustOnlyReferenceExportedDecls() const { + return Exported || FragileKind.kind != FragileFunctionKind::None; +} + +Optional ExportContext::getExportabilityReason() const { + if (Exported) + return Reason; + return None; +} + /// Returns the first availability attribute on the declaration that is active /// on the target platform. static const AvailableAttr *getActiveAvailableAttribute(const Decl *D, @@ -2320,15 +2425,14 @@ class AvailabilityWalker : public ASTWalker { }; ASTContext &Context; - DeclContext *DC; MemberAccessContext AccessContext = MemberAccessContext::Getter; SmallVector ExprStack; - Optional ExportReason; - FragileFunctionKind FragileKind; + ExportContext Where; /// Returns true if DC is an \c init(rawValue:) declaration and it is marked /// implicit. bool inSynthesizedInitRawValue() { + auto *DC = Where.getDeclContext(); auto init = dyn_cast_or_null( DC->getInnermostDeclarationDeclContext()); @@ -2340,11 +2444,8 @@ class AvailabilityWalker : public ASTWalker { } public: - AvailabilityWalker(DeclContext *DC, - Optional ExportReason, - FragileFunctionKind FragileKind) - : Context(DC->getASTContext()), DC(DC), - ExportReason(ExportReason), FragileKind(FragileKind) {} + AvailabilityWalker(ExportContext Where) + : Context(Where.getDeclContext()->getASTContext()), Where(Where) {} bool shouldWalkIntoSeparatelyCheckedClosure(ClosureExpr *expr) override { return false; @@ -2353,6 +2454,8 @@ class AvailabilityWalker : public ASTWalker { bool shouldWalkIntoTapExpression() override { return false; } std::pair walkToExprPre(Expr *E) override { + auto *DC = Where.getDeclContext(); + ExprStack.push_back(E); auto visitChildren = [&]() { return std::make_pair(true, E); }; @@ -2412,23 +2515,22 @@ class AvailabilityWalker : public ASTWalker { if (auto T = dyn_cast(E)) { if (!T->isImplicit()) { diagnoseTypeAvailability(T->getTypeRepr(), T->getType(), E->getLoc(), - DC, ExportReason, FragileKind); + Where); } } if (auto CE = dyn_cast(E)) { for (auto *param : *CE->getParameters()) { diagnoseTypeAvailability(param->getTypeRepr(), param->getInterfaceType(), - E->getLoc(), DC, ExportReason, FragileKind); + E->getLoc(), Where); } diagnoseTypeAvailability(CE->hasExplicitResultType() ? CE->getExplicitResultTypeRepr() : nullptr, - CE->getResultType(), E->getLoc(), DC, - ExportReason, FragileKind); + CE->getResultType(), E->getLoc(), Where); } if (auto CE = dyn_cast(E)) { diagnoseTypeAvailability(CE->getCastTypeRepr(), CE->getCastType(), - E->getLoc(), DC, ExportReason, FragileKind); + E->getLoc(), Where); } return visitChildren(); @@ -2512,6 +2614,7 @@ class AvailabilityWalker : public ASTWalker { return; // Diagnose for appropriate accessors, given the access context. + auto *DC = Where.getDeclContext(); maybeDiagStorageAccess(D, E->getSourceRange(), DC); } @@ -2644,24 +2747,25 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, return false; } - if (FragileKind.kind != FragileFunctionKind::None) { + if (Where.getFragileFunctionKind().kind != FragileFunctionKind::None) { if (R.isValid()) - if (TypeChecker::diagnoseInlinableDeclRef(R.Start, D, DC, FragileKind)) + if (TypeChecker::diagnoseInlinableDeclRef(R.Start, D, Where)) return true; - } else if (ExportReason.hasValue()) { + } else if (Where.getExportabilityReason().hasValue()) { if (R.isValid()) - if (TypeChecker::diagnoseDeclRefExportability(R.Start, D, DC, - ExportReason, FragileKind)) + if (TypeChecker::diagnoseDeclRefExportability(R.Start, D, Where)) return true; } if (R.isValid()) { if (diagnoseSubstitutionMapAvailability(R.Start, declRef.getSubstitutions(), - DC, ExportReason, FragileKind)) { + Where)) { return true; } } + auto *DC = Where.getDeclContext(); + if (diagnoseExplicitUnavailability(D, R, DC, call, Flags)) return true; @@ -2738,6 +2842,7 @@ AvailabilityWalker::diagnoseIncDecRemoval(const ValueDecl *D, SourceRange R, // If the expression type is integer or floating point, then we can rewrite it // to "lvalue += 1". + auto *DC = Where.getDeclContext(); std::string replacement; if (isIntegerOrFloatingPointType(call->getType(), DC, Context)) replacement = isInc ? " += 1" : " -= 1"; @@ -2853,23 +2958,18 @@ AvailabilityWalker::diagnoseMemoryLayoutMigration(const ValueDecl *D, /// Diagnose uses of unavailable declarations. void swift::diagAvailability(const Expr *E, DeclContext *DC) { - Optional reason = None; - FragileFunctionKind fragileKind = DC->getFragileFunctionKind(); - AvailabilityWalker walker(DC, reason, fragileKind); + AvailabilityWalker walker(ExportContext::forFunctionBody(DC)); const_cast(E)->walk(walker); } namespace { class StmtAvailabilityWalker : public ASTWalker { - DeclContext *DC; - Optional ExportReason; - FragileFunctionKind FragileKind; + ExportContext Where; public: - explicit StmtAvailabilityWalker(DeclContext *DC) : DC(DC) { - FragileKind = DC->getFragileFunctionKind(); - } + explicit StmtAvailabilityWalker(DeclContext *DC) + : Where(ExportContext::forFunctionBody(DC)) {} /// We'll visit the expression from performSyntacticExprDiagnostics(). std::pair walkToExprPre(Expr *E) override { @@ -2877,14 +2977,13 @@ class StmtAvailabilityWalker : public ASTWalker { } bool walkToTypeReprPre(TypeRepr *T) override { - diagnoseTypeReprAvailability(T, DC, ExportReason, FragileKind); + diagnoseTypeReprAvailability(T, Where); return false; } std::pair walkToPatternPre(Pattern *P) override { if (auto *IP = dyn_cast(P)) - diagnoseTypeAvailability(IP->getCastType(), P->getLoc(), DC, - ExportReason, FragileKind); + diagnoseTypeAvailability(IP->getCastType(), P->getLoc(), Where); return std::make_pair(true, P); } @@ -2904,17 +3003,13 @@ void swift::diagAvailability(const Stmt *S, DeclContext *DC) { namespace { class TypeReprAvailabilityWalker : public ASTWalker { - DeclContext *DC; - Optional reason; - FragileFunctionKind fragileKind; + ExportContext where; DeclAvailabilityFlags flags; bool checkComponentIdentTypeRepr(ComponentIdentTypeRepr *ITR) { if (auto *typeDecl = ITR->getBoundDecl()) { auto range = ITR->getNameLoc().getSourceRange(); - if (diagnoseDeclAvailability(typeDecl, range, DC, - reason, fragileKind, - flags)) + if (diagnoseDeclAvailability(typeDecl, range, where, flags)) return true; } @@ -2925,9 +3020,7 @@ class TypeReprAvailabilityWalker : public ASTWalker { genericFlags -= DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol; for (auto *genericArg : GTR->getGenericArgs()) { - if (diagnoseTypeReprAvailability(genericArg, DC, - reason, fragileKind, - genericFlags)) + if (diagnoseTypeReprAvailability(genericArg, where, genericFlags)) foundAnyIssues = true; } } @@ -2938,11 +3031,9 @@ class TypeReprAvailabilityWalker : public ASTWalker { public: bool foundAnyIssues = false; - TypeReprAvailabilityWalker(DeclContext *DC, - Optional reason, - FragileFunctionKind fragileKind, + TypeReprAvailabilityWalker(ExportContext where, DeclAvailabilityFlags flags) - : DC(DC), reason(reason), fragileKind(fragileKind), flags(flags) {} + : where(where), flags(flags) {} bool walkToTypeReprPre(TypeRepr *T) override { if (auto *ITR = dyn_cast(T)) { @@ -2975,13 +3066,11 @@ class TypeReprAvailabilityWalker : public ASTWalker { } -bool swift::diagnoseTypeReprAvailability(const TypeRepr *T, DeclContext *DC, - Optional reason, - FragileFunctionKind fragileKind, +bool swift::diagnoseTypeReprAvailability(const TypeRepr *T, ExportContext where, DeclAvailabilityFlags flags) { if (!T) return false; - TypeReprAvailabilityWalker walker(DC, reason, fragileKind, flags); + TypeReprAvailabilityWalker walker(where, flags); const_cast(T)->walk(walker); return walker.foundAnyIssues; } @@ -2990,29 +3079,19 @@ namespace { class ProblematicTypeFinder : public TypeDeclFinder { SourceLoc Loc; - DeclContext *DC; - Optional ExportReason; - FragileFunctionKind FragileKind; + ExportContext Where; DeclAvailabilityFlags Flags; public: - ProblematicTypeFinder(SourceLoc Loc, DeclContext *DC, - Optional ExportReason, - FragileFunctionKind FragileKind, + ProblematicTypeFinder(SourceLoc Loc, ExportContext Where, DeclAvailabilityFlags Flags) - : Loc(Loc), DC(DC), - ExportReason(ExportReason), - FragileKind(FragileKind), - Flags(Flags) {} + : Loc(Loc), Where(Where), Flags(Flags) {} void visitTypeDecl(TypeDecl *decl) { // We only need to diagnose exportability here. Availability was // already checked on the TypeRepr. - if (FragileKind.kind != FragileFunctionKind::None || - ExportReason.hasValue()) { - TypeChecker::diagnoseDeclRefExportability(Loc, decl, DC, - ExportReason, FragileKind); - } + if (Where.mustOnlyReferenceExportedDecls()) + TypeChecker::diagnoseDeclRefExportability(Loc, decl, Where); } Action visitNominalType(NominalType *ty) override { @@ -3025,11 +3104,9 @@ class ProblematicTypeFinder : public TypeDeclFinder { Action visitBoundGenericType(BoundGenericType *ty) override { visitTypeDecl(ty->getDecl()); - ModuleDecl *useModule = DC->getParentModule(); + ModuleDecl *useModule = Where.getDeclContext()->getParentModule(); auto subs = ty->getContextSubstitutionMap(useModule, ty->getDecl()); - (void) diagnoseSubstitutionMapAvailability(Loc, subs, DC, - ExportReason, - FragileKind); + (void) diagnoseSubstitutionMapAvailability(Loc, subs, Where); return Action::Continue; } @@ -3037,9 +3114,7 @@ class ProblematicTypeFinder : public TypeDeclFinder { visitTypeDecl(ty->getDecl()); auto subs = ty->getSubstitutionMap(); - (void) diagnoseSubstitutionMapAvailability(Loc, subs, DC, - ExportReason, - FragileKind); + (void) diagnoseSubstitutionMapAvailability(Loc, subs, Where); return Action::Continue; } @@ -3047,10 +3122,10 @@ class ProblematicTypeFinder : public TypeDeclFinder { // post-visitor so that we diagnose any unexportable component // types first. Action walkToTypePost(Type T) override { - if (FragileKind.kind != FragileFunctionKind::None || - ExportReason.hasValue()) { + if (Where.mustOnlyReferenceExportedDecls()) { if (auto fnType = T->getAs()) { if (auto clangType = fnType->getClangTypeInfo().getType()) { + auto *DC = Where.getDeclContext(); auto &ctx = DC->getASTContext(); auto loader = ctx.getClangModuleLoader(); // Serialization will serialize the sugared type if it can, @@ -3069,58 +3144,47 @@ class ProblematicTypeFinder : public TypeDeclFinder { } -void swift::diagnoseTypeAvailability(Type T, SourceLoc loc, DeclContext *DC, - Optional reason, - FragileFunctionKind fragileKind, +void swift::diagnoseTypeAvailability(Type T, SourceLoc loc, ExportContext where, DeclAvailabilityFlags flags) { if (!T) return; - T.walk(ProblematicTypeFinder(loc, DC, reason, fragileKind, flags)); + T.walk(ProblematicTypeFinder(loc, where, flags)); } void swift::diagnoseTypeAvailability(const TypeRepr *TR, Type T, SourceLoc loc, - DeclContext *DC, - Optional reason, - FragileFunctionKind fragileKind, + ExportContext where, DeclAvailabilityFlags flags) { - if (diagnoseTypeReprAvailability(TR, DC, reason, fragileKind, flags)) + if (diagnoseTypeReprAvailability(TR, where, flags)) return; - diagnoseTypeAvailability(T, loc, DC, reason, fragileKind, flags); + diagnoseTypeAvailability(T, loc, where, flags); } bool swift::diagnoseConformanceAvailability(SourceLoc loc, ProtocolConformanceRef conformance, - const DeclContext *DC, - Optional reason, - FragileFunctionKind fragileKind) { + ExportContext where) { if (!conformance.isConcrete()) return false; const ProtocolConformance *concreteConf = conformance.getConcrete(); + auto *DC = where.getDeclContext(); SubstitutionMap subConformanceSubs = concreteConf->getSubstitutions(DC->getParentModule()); - diagnoseSubstitutionMapAvailability(loc, subConformanceSubs, DC, - reason, fragileKind); - + diagnoseSubstitutionMapAvailability(loc, subConformanceSubs, where); const RootProtocolConformance *rootConf = concreteConf->getRootConformance(); return TypeChecker::diagnoseConformanceExportability( - loc, rootConf, *DC->getParentSourceFile(), DC, - reason, fragileKind); + loc, rootConf, where); } bool swift::diagnoseSubstitutionMapAvailability(SourceLoc loc, SubstitutionMap subs, - const DeclContext *DC, - Optional reason, - FragileFunctionKind fragileKind) { + ExportContext where) { bool hadAnyIssues = false; for (ProtocolConformanceRef conformance : subs.getConformances()) { - if (diagnoseConformanceAvailability(loc, conformance, DC, - reason, fragileKind)) + if (diagnoseConformanceAvailability(loc, conformance, where)) hadAnyIssues = true; } return hadAnyIssues; @@ -3131,12 +3195,10 @@ swift::diagnoseSubstitutionMapAvailability(SourceLoc loc, /// TypeReprs. bool swift::diagnoseDeclAvailability(const ValueDecl *Decl, SourceRange R, - DeclContext *DC, - Optional reason, - FragileFunctionKind fragileKind, + ExportContext Where, DeclAvailabilityFlags Flags) { - AvailabilityWalker AW(DC, reason, fragileKind); + AvailabilityWalker AW(Where); return AW.diagAvailability(const_cast(Decl), R, nullptr, Flags); } diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index 40c500bde957b..3a7747a866073 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -13,6 +13,7 @@ #ifndef SWIFT_SEMA_TYPE_CHECK_AVAILABILITY_H #define SWIFT_SEMA_TYPE_CHECK_AVAILABILITY_H +#include "swift/AST/DeclContext.h" #include "swift/AST/AttrKind.h" #include "swift/AST/Identifier.h" #include "swift/Basic/LLVM.h" @@ -24,18 +25,15 @@ namespace swift { class ApplyExpr; class AvailableAttr; - class DeclContext; class Expr; class InFlightDiagnostic; class Decl; - struct FragileFunctionKind; class ProtocolConformanceRef; class Stmt; class SubstitutionMap; class Type; class TypeRepr; class ValueDecl; - enum class DeclAvailabilityFlag : uint8_t { /// Do not diagnose uses of protocols in versions before they were introduced. /// Used when type-checking protocol conformances, since conforming to a @@ -70,6 +68,38 @@ enum class ExportabilityReason : unsigned { ExtensionWithConditionalConformances }; +class ExportContext { + DeclContext *DC; + FragileFunctionKind FragileKind; + unsigned SPI : 1; + unsigned Exported : 1; + ExportabilityReason Reason; + + ExportContext(DeclContext *DC, FragileFunctionKind kind, bool spi, bool exported); + +public: + static ExportContext forDeclSignature(Decl *D); + static ExportContext forFunctionBody(DeclContext *DC); + + ExportContext forReason(ExportabilityReason reason) const; + ExportContext forExported(bool exported) const; + + DeclContext *getDeclContext() const { return DC; } + FragileFunctionKind getFragileFunctionKind() const { return FragileKind; } + + bool isSPI() const { return SPI; } + bool isExported() const { return Exported; } + + bool mustOnlyReferenceExportedDecls() const; + + Optional getExportabilityReason() const; +}; + +/// Check if a public declaration is part of a module's API; that is, this +/// will return false if the declaration is @_spi or @_implementationOnly. +bool isExported(const ValueDecl *VD); +bool isExported(const Decl *D); + /// Diagnose uses of unavailable declarations in expressions. void diagAvailability(const Expr *E, DeclContext *DC); @@ -78,47 +108,37 @@ void diagAvailability(const Expr *E, DeclContext *DC); void diagAvailability(const Stmt *S, DeclContext *DC); /// Diagnose uses of unavailable declarations in types. -bool diagnoseTypeReprAvailability(const TypeRepr *T, DeclContext *DC, - Optional reason, - FragileFunctionKind fragileKind, +bool diagnoseTypeReprAvailability(const TypeRepr *T, + ExportContext context, DeclAvailabilityFlags flags = None); /// Diagnose uses of unavailable conformances in types. -void diagnoseTypeAvailability(Type T, SourceLoc loc, DeclContext *DC, - Optional reason, - FragileFunctionKind fragileKind, +void diagnoseTypeAvailability(Type T, SourceLoc loc, + ExportContext context, DeclAvailabilityFlags flags = None); /// Checks both a TypeRepr and a Type, but avoids emitting duplicate /// diagnostics by only checking the Type if the TypeRepr succeeded. void diagnoseTypeAvailability(const TypeRepr *TR, Type T, SourceLoc loc, - DeclContext *DC, - Optional reason, - FragileFunctionKind fragileKind, + ExportContext context, DeclAvailabilityFlags flags = None); bool diagnoseConformanceAvailability(SourceLoc loc, ProtocolConformanceRef conformance, - const DeclContext *DC, - Optional reason, - FragileFunctionKind fragileKind); + ExportContext context); bool diagnoseSubstitutionMapAvailability(SourceLoc loc, SubstitutionMap subs, - const DeclContext *DC, - Optional reason, - FragileFunctionKind fragileKind); + ExportContext context); /// Run the Availability-diagnostics algorithm otherwise used in an expr /// context, but for non-expr contexts such as TypeDecls referenced from /// TypeReprs. bool diagnoseDeclAvailability(const ValueDecl *Decl, SourceRange R, - DeclContext *DC, - Optional reason, - FragileFunctionKind fragileKind, + ExportContext context, DeclAvailabilityFlags flags = None); void diagnoseUnavailableOverride(ValueDecl *override, diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index c6ffc17b2b1f5..f9e6edd02bbd6 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1686,7 +1686,7 @@ static bool checkSuperInit(ConstructorDecl *fromCtor, for (auto decl : lookupResults) { auto superclassCtor = dyn_cast(decl); - if (!superclassCtor || !superclassCtor->isDesignatedInit() || + if (!superclassCtor || !superclassCtor->isDesignatedInit() || superclassCtor == ctor) continue; @@ -1696,12 +1696,9 @@ static bool checkSuperInit(ConstructorDecl *fromCtor, } // Make sure we can reference the designated initializer correctly. - auto fragileKind = fromCtor->getFragileFunctionKind(); - if (fragileKind.kind != FragileFunctionKind::None) { - TypeChecker::diagnoseInlinableDeclRef( - fromCtor->getLoc(), ctor, fromCtor, - fragileKind); - } + TypeChecker::diagnoseInlinableDeclRef( + fromCtor->getLoc(), ctor, + ExportContext::forFunctionBody(fromCtor)); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 146a47780cf85..551f382d256e1 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -37,6 +37,7 @@ namespace swift { +class ExportContext; class GenericSignatureBuilder; class NominalTypeDecl; class NormalProtocolConformance; @@ -947,33 +948,26 @@ DeclName getObjectLiteralConstructorName(ASTContext &ctx, ModuleDecl *getStdlibModule(const DeclContext *dc); /// \name Resilience diagnostics -bool diagnoseInlinableDeclRef(SourceLoc loc, const ValueDecl *D, - const DeclContext *DC, FragileFunctionKind Kind); +bool diagnoseInlinableDeclRef(SourceLoc loc, const ValueDecl *D, ExportContext where); Expr *buildDefaultInitializer(Type type); bool diagnoseInlinableDeclRefAccess(SourceLoc loc, const ValueDecl *D, - const DeclContext *DC, - FragileFunctionKind Kind); + ExportContext where); /// Given that a declaration is used from a particular context which /// exposes it in the interface of the current module, diagnose if it cannot /// reasonably be shared. bool diagnoseDeclRefExportability(SourceLoc loc, const ValueDecl *D, - const DeclContext *DC, - Optional exportability, - FragileFunctionKind fragileKind); + ExportContext where); /// Given that a conformance is used from a particular context which /// exposes it in the interface of the current module, diagnose if the /// conformance is SPI or visible via an implementation-only import. bool diagnoseConformanceExportability(SourceLoc loc, const RootProtocolConformance *rootConf, - const SourceFile &userSF, - const DeclContext *userDC, - Optional reason, - FragileFunctionKind fragileKind); + ExportContext where); /// \name Availability checking /// From a824e5a33b63a9b2ef7ed49e78993cd79baec968 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 20 Oct 2020 00:16:40 -0400 Subject: [PATCH 580/745] Sema: Refactor getDisallowedOriginKind() to take an ExportContext Converting the innermost DeclContext into a Decl won't work if the innermost DeclContext is the parent context of a VarDecl. Instead, use the relevant bit of state from the ExportContext, which is computed correctly. This fixes a regression caused by an earlier commit in this PR which our test suite did not catch. --- lib/Sema/ResilienceDiagnostics.cpp | 10 ++-------- lib/Sema/TypeCheckAccess.cpp | 17 ++++++++--------- lib/Sema/TypeCheckAccess.h | 7 +++---- lib/Sema/TypeCheckAvailability.cpp | 12 ++++++++++++ lib/Sema/TypeCheckProtocol.cpp | 4 +++- test/SPI/spi_members.swift | 11 +++++++++++ 6 files changed, 39 insertions(+), 22 deletions(-) create mode 100644 test/SPI/spi_members.swift diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index 17e7e821c6f94..38e08afd1a9ca 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -152,12 +152,8 @@ TypeChecker::diagnoseDeclRefExportability(SourceLoc loc, auto downgradeToWarning = DowngradeToWarning::No; - auto *DC = where.getDeclContext(); auto originKind = getDisallowedOriginKind( - D, - *DC->getParentSourceFile(), - DC->getInnermostDeclarationDeclContext(), - downgradeToWarning); + D, where, downgradeToWarning); if (originKind == DisallowedOriginKind::None) return false; @@ -195,11 +191,9 @@ TypeChecker::diagnoseConformanceExportability(SourceLoc loc, if (!where.mustOnlyReferenceExportedDecls()) return false; - auto *DC = where.getDeclContext(); auto originKind = getDisallowedOriginKind( rootConf->getDeclContext()->getAsDecl(), - *DC->getParentSourceFile(), - DC->getInnermostDeclarationDeclContext()); + where); if (originKind == DisallowedOriginKind::None) return false; diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index c31bafb8a922a..447099f037185 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -1460,22 +1460,22 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, /// Local variant to swift::getDisallowedOriginKind for downgrade to warnings. DisallowedOriginKind swift::getDisallowedOriginKind(const Decl *decl, - const SourceFile &userSF, - const Decl *userContext, + ExportContext where, DowngradeToWarning &downgradeToWarning) { downgradeToWarning = DowngradeToWarning::No; ModuleDecl *M = decl->getModuleContext(); - if (userSF.isImportedImplementationOnly(M)) { + auto *SF = where.getDeclContext()->getParentSourceFile(); + if (SF->isImportedImplementationOnly(M)) { // Temporarily downgrade implementation-only exportability in SPI to // a warning. - if (userContext->isSPI()) + if (where.isSPI()) downgradeToWarning = DowngradeToWarning::Yes; // Implementation-only imported, cannot be reexported. return DisallowedOriginKind::ImplementationOnly; - } else if (decl->isSPI() && !userContext->isSPI()) { + } else if (decl->isSPI() && !where.isSPI()) { // SPI can only be exported in SPI. - return userContext->getModuleContext() == M ? + return where.getDeclContext()->getParentModule() == M ? DisallowedOriginKind::SPILocal : DisallowedOriginKind::SPIImported; } @@ -1842,10 +1842,9 @@ static void checkExtensionGenericParamAccess(const ExtensionDecl *ED) { } DisallowedOriginKind swift::getDisallowedOriginKind(const Decl *decl, - const SourceFile &userSF, - const Decl *declContext) { + ExportContext where) { auto downgradeToWarning = DowngradeToWarning::No; - return getDisallowedOriginKind(decl, userSF, declContext, downgradeToWarning); + return getDisallowedOriginKind(decl, where, downgradeToWarning); } void swift::checkAccessControl(Decl *D) { diff --git a/lib/Sema/TypeCheckAccess.h b/lib/Sema/TypeCheckAccess.h index 764603a85ac09..29d82411bba5c 100644 --- a/lib/Sema/TypeCheckAccess.h +++ b/lib/Sema/TypeCheckAccess.h @@ -22,6 +22,7 @@ namespace swift { class Decl; +class ExportContext; class SourceFile; /// Performs access-related checks for \p D. @@ -55,12 +56,10 @@ enum class DowngradeToWarning: bool { /// Returns the kind of origin, implementation-only import or SPI declaration, /// that restricts exporting \p decl from the given file and context. DisallowedOriginKind getDisallowedOriginKind(const Decl *decl, - const SourceFile &userSF, - const Decl *userContext); + ExportContext where); DisallowedOriginKind getDisallowedOriginKind(const Decl *decl, - const SourceFile &userSF, - const Decl *userContext, + ExportContext where, DowngradeToWarning &downgradeToWarning); } // end namespace swift diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 692c4a06d8e7c..4fb4bbe298b64 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -98,7 +98,19 @@ bool swift::isExported(const Decl *D) { ExportContext ExportContext::forDeclSignature(Decl *D) { auto *DC = D->getInnermostDeclContext(); auto fragileKind = DC->getFragileFunctionKind(); + bool spi = D->isSPI(); + if (auto *PBD = dyn_cast(D)) { + for (unsigned i = 0, e = PBD->getNumPatternEntries(); i < e; ++i) { + if (auto *VD = PBD->getAnchoringVarDecl(i)) { + if (VD->isSPI()) { + spi = true; + break; + } + } + } + } + bool exported = ::isExported(D); return ExportContext(DC, fragileKind, spi, exported); diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 48ff739eb8306..79bbcd82b8cb6 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4337,9 +4337,11 @@ static void checkExportability(Type depTy, Type replacementTy, conformance->getRootConformance(); ModuleDecl *M = rootConformance->getDeclContext()->getParentModule(); + auto where = ExportContext::forDeclSignature( + DC->getInnermostDeclarationDeclContext()); auto originKind = getDisallowedOriginKind( rootConformance->getDeclContext()->getAsDecl(), - *SF, DC->getAsDecl()); + where); if (originKind == DisallowedOriginKind::None) return; diff --git a/test/SPI/spi_members.swift b/test/SPI/spi_members.swift new file mode 100644 index 0000000000000..45c70d9c88c5a --- /dev/null +++ b/test/SPI/spi_members.swift @@ -0,0 +1,11 @@ +// RUN: %target-typecheck-verify-swift + +@_spi(Foo) +public class Bar {} + +public struct Foo { + public init() {} + + @_spi(Foo) public func method(_: Bar) {} + @_spi(Foo) public var property: Bar { Bar() } +} From 73dc49fc3efb43690543c5cd501f8f6a82811653 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Mon, 19 Oct 2020 23:05:02 -0700 Subject: [PATCH 581/745] [Build System] Remove tvOS i368 slice before copying the libs --- utils/build-script-impl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index 7b746ce89af80..185f6bf555092 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -1385,7 +1385,11 @@ function copy_embedded_compiler_rt_builtins_from_darwin_host_toolchain() { DEST_LIB_PATH="${DEST_BUILTINS_DIR}/${LIB_NAME}" if [[ ! -f "${DEST_LIB_PATH}" ]]; then if [[ -f "${HOST_LIB_PATH}" ]]; then - call cp "${HOST_LIB_PATH}" "${DEST_LIB_PATH}" + if [[ "$OS" == "tvos" ]]; then + call lipo -remove i386 "${HOST_LIB_PATH}" -output "${DEST_LIB_PATH}" + else + call cp "${HOST_LIB_PATH}" "${DEST_LIB_PATH}" + fi elif [[ "${VERBOSE_BUILD}" ]]; then echo "no file exists at ${HOST_LIB_PATH}" fi @@ -1397,7 +1401,11 @@ function copy_embedded_compiler_rt_builtins_from_darwin_host_toolchain() { DEST_SIM_LIB_PATH="${DEST_BUILTINS_DIR}/${SIM_LIB_NAME}" if [[ ! -f "${DEST_SIM_LIB_PATH}" ]]; then if [[ -f "${HOST_SIM_LIB_PATH}" ]]; then - call cp "${HOST_SIM_LIB_PATH}" "${DEST_SIM_LIB_PATH}" + if [[ "$OS" == "tvos" ]]; then + call lipo -remove i386 "${HOST_SIM_LIB_PATH}" -output "${DEST_SIM_LIB_PATH}" + else + call cp "${HOST_SIM_LIB_PATH}" "${DEST_SIM_LIB_PATH}" + fi elif [[ -f "${HOST_LIB_PATH}" ]]; then # The simulator .a might not exist if the host # Xcode is old. In that case, copy over the From ef73c398bbabd902ec314feaf7b57b36c35e3b2f Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 20 Oct 2020 13:49:58 +0200 Subject: [PATCH 582/745] tests: fix APINotes/blocks.swift The requirement executable_test is not needed here. It was there for historical reasons. rdar://problem/70400635 --- test/APINotes/blocks.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/test/APINotes/blocks.swift b/test/APINotes/blocks.swift index dd81a47e3b0c0..aeda4f40647ad 100644 --- a/test/APINotes/blocks.swift +++ b/test/APINotes/blocks.swift @@ -1,5 +1,4 @@ // RUN: %target-build-swift -Xfrontend %clang-importer-sdk %s -emit-ir -// REQUIRES: executable_test // REQUIRES: objc_interop From c5176c031594e84726adf4994ef4da864762525b Mon Sep 17 00:00:00 2001 From: tbkka Date: Tue, 20 Oct 2020 09:14:17 -0700 Subject: [PATCH 583/745] Update swift-rpathize.py to work with Python 3.8 and Python 2.7 (#34332) * Update swift-rpathize.py to work with Python 3.8 and Python 2.7 In Python 2, str() is also a kind of bytes blob In Python 3, str() is a Unicode string that's unrelated * Avoid `l` as a variable name Python-lint correctly warns about single-character variables that can be confused with numbers --- utils/swift-rpathize.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/swift-rpathize.py b/utils/swift-rpathize.py index e88bdd81988ce..5db01717fad0c 100755 --- a/utils/swift-rpathize.py +++ b/utils/swift-rpathize.py @@ -64,7 +64,8 @@ def rpathize(filename): # Build a command to invoke install_name_tool. command = ['install_name_tool'] - for line in dylibsOutput.splitlines(): + for binaryline in dylibsOutput.splitlines(): + line = binaryline.decode("utf-8", "strict") match = dylib_regex.match(line) if match: command.append('-change') From 59aff3e8500d36213eb947ef8b6b69c8daa577c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=85=B7=E9=85=B7=E7=9A=84=E5=93=80=E6=AE=BF?= Date: Wed, 21 Oct 2020 00:22:44 +0800 Subject: [PATCH 584/745] Update ObjectIdentifier.swift --- stdlib/public/core/ObjectIdentifier.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/public/core/ObjectIdentifier.swift b/stdlib/public/core/ObjectIdentifier.swift index a32d99fdb787f..3d782927af091 100644 --- a/stdlib/public/core/ObjectIdentifier.swift +++ b/stdlib/public/core/ObjectIdentifier.swift @@ -14,14 +14,14 @@ /// /// In Swift, only class instances and metatypes have unique identities. There /// is no notion of identity for structs, enums, functions, or tuples. -@frozen // trivial-implementation -/// /// `ObjectIdentifier` is only guaranteed to remain unique for the /// lifetime of an object. When the instance gets deallocated, its object /// identifier may be reused for a different object. (Internally, objects are /// identified by their memory location.) -/// If you need an object identifier over the lifetime of an objec, it may +/// If you need an object identifier over the lifetime of an object, it may /// be appropriate to provide a custom implementation of `Identifiable`. +@frozen // trivial-implementation +/// public struct ObjectIdentifier { @usableFromInline // trivial-implementation internal let _value: Builtin.RawPointer From c2159e240124ab36e6e93ddbca9bcaaf57af8266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=85=B7=E9=85=B7=E7=9A=84=E5=93=80=E6=AE=BF?= Date: Wed, 21 Oct 2020 00:23:14 +0800 Subject: [PATCH 585/745] Update ObjectIdentifier.swift --- stdlib/public/core/ObjectIdentifier.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/public/core/ObjectIdentifier.swift b/stdlib/public/core/ObjectIdentifier.swift index 3d782927af091..36c800c672c48 100644 --- a/stdlib/public/core/ObjectIdentifier.swift +++ b/stdlib/public/core/ObjectIdentifier.swift @@ -14,14 +14,15 @@ /// /// In Swift, only class instances and metatypes have unique identities. There /// is no notion of identity for structs, enums, functions, or tuples. +/// /// `ObjectIdentifier` is only guaranteed to remain unique for the /// lifetime of an object. When the instance gets deallocated, its object /// identifier may be reused for a different object. (Internally, objects are /// identified by their memory location.) +/// /// If you need an object identifier over the lifetime of an object, it may /// be appropriate to provide a custom implementation of `Identifiable`. @frozen // trivial-implementation -/// public struct ObjectIdentifier { @usableFromInline // trivial-implementation internal let _value: Builtin.RawPointer From 6a40a3a8aac7b4133bac70f98d92adabf30b6f7b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 20 Oct 2020 13:24:51 -0700 Subject: [PATCH 586/745] [SE-0289] Add support for @resultBuilder. "Function builders" are being renamed to "result builders". Add the corresponding `@resultBuilder` attribute, with `@_functionBuilder` as an alias for it, Update test cases to use @resultBuilder. --- include/swift/AST/Attr.def | 4 +- lib/Parse/ParseDecl.cpp | 5 ++ test/Constraints/function_builder.swift | 12 ++--- .../function_builder_availability.swift | 4 +- test/Constraints/function_builder_diags.swift | 12 ++--- test/Constraints/function_builder_infer.swift | 4 +- .../function_builder_one_way.swift | 2 +- .../function_builder_opaque_result.swift | 2 +- test/Constraints/rdar64890308.swift | 4 +- test/Constraints/rdar65320500.swift | 2 +- test/Constraints/sr13183.swift | 2 +- test/IDE/complete_ambiguous.swift | 2 +- test/IDE/complete_decl_attribute.swift | 5 ++ test/IDE/complete_function_builder.swift | 6 +-- test/IDE/complete_rdar63965160.swift | 2 +- test/IDE/print_swift_module.swift | 2 +- test/Index/function_builders.swift | 2 +- test/ModuleInterface/function_builders.swift | 2 +- test/NameLookup/Inputs/custom_attrs_A.swift | 2 +- test/NameLookup/Inputs/custom_attrs_B.swift | 2 +- test/Profiler/coverage_function_builder.swift | 2 +- .../function_builder_curry_thunks.swift | 4 +- test/SILGen/function_builder_memberwise.swift | 2 +- test/Sema/type_eraser.swift | 2 +- test/Serialization/function_builders.swift | 2 +- .../complete_sequence_builderfunc.swift | 2 +- test/SourceKit/CodeExpand/code-expand.swift | 2 +- .../CursorInfo/function_builder.swift | 6 +-- test/attr/attr_function_builder.swift | 6 +++ test/decl/function_builder_fixits.swift | 4 +- test/decl/var/function_builders.swift | 46 +++++++++---------- .../var/function_builders_availability.swift | 2 +- .../Inputs/function_builder_definition.swift | 2 +- .../custom-attrs/Foo.swift.expected | 2 +- .../custom-attrs/Other.swift.expected | 2 +- .../custom-attrs/wrapped.swift.expected | 2 +- .../Outputs/custom-attrs/Foo.swift.expected | 2 +- .../Outputs/custom-attrs/Other.swift.expected | 2 +- .../custom-attrs/wrapped.swift.expected | 2 +- .../SyntacticRename/custom-attrs.swift | 2 +- .../diagnostics/function-builder-methods.md | 2 +- .../rdar50869732.swift | 2 +- .../rdar67239650.swift | 2 +- 43 files changed, 98 insertions(+), 82 deletions(-) create mode 100644 test/attr/attr_function_builder.swift diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 20427b356c81b..685f2a9a14ebe 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -493,8 +493,8 @@ SIMPLE_DECL_ATTR(_disfavoredOverload, DisfavoredOverload, OnAbstractFunction | OnVar | OnSubscript | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, 87) -SIMPLE_DECL_ATTR(_functionBuilder, FunctionBuilder, - OnNominalType | UserInaccessible | +SIMPLE_DECL_ATTR(resultBuilder, FunctionBuilder, + OnNominalType | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, 88) DECL_ATTR(_projectedValueProperty, ProjectedValueProperty, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index f4a7b9632070f..dbf2a7df088fa 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2708,6 +2708,11 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc At checkInvalidAttrName("_propertyWrapper", "propertyWrapper", DAK_PropertyWrapper, diag::attr_renamed_warning); + // Historical name for result builders. + checkInvalidAttrName( + "_functionBuilder", "resultBuilder", DAK_FunctionBuilder, + diag::attr_renamed_warning); + if (DK == DAK_Count && Tok.getText() == "warn_unused_result") { // The behavior created by @warn_unused_result is now the default. Emit a // Fix-It to remove. diff --git a/test/Constraints/function_builder.swift b/test/Constraints/function_builder.swift index c6e7cf21a5a05..82dffb9863582 100644 --- a/test/Constraints/function_builder.swift +++ b/test/Constraints/function_builder.swift @@ -6,7 +6,7 @@ enum Either { case second(U) } -@_functionBuilder +@resultBuilder struct TupleBuilder { static func buildBlock(_ t1: T1) -> (T1) { return (t1) @@ -247,7 +247,7 @@ extension Int: Taggable { } extension String: Taggable { } extension Double: Taggable { } -@_functionBuilder +@resultBuilder struct TaggedBuilder { static func buildBlock() -> () { } @@ -330,7 +330,7 @@ enum Component { indirect case optional(Component?) } -@_functionBuilder +@resultBuilder struct ComponentBuilder { static func buildExpression(_ string: StaticString) -> Component { return .string(string) @@ -422,7 +422,7 @@ testForEach1.show() // CHECK: ("testForEach1", main.Either<(Swift.String, Swift.Bool), (Swift.Bool, Swift.String)>.second(true, "end")) func test_single_stmt_closure_support() { - @_functionBuilder + @resultBuilder struct MyBuilder { static func buildBlock(_ numbers: Int...) -> Int { return 42 @@ -621,7 +621,7 @@ testSwitchCombined(getE(2)) // Test buildOptional(_:) as an alternative to buildIf(_:). -@_functionBuilder +@resultBuilder struct TupleBuilderWithOpt { static func buildBlock(_ t1: T1) -> (T1) { return (t1) @@ -715,7 +715,7 @@ extension FunctionBuilderProtocol { static func buildLimitedAvailability(_ component: Component) -> Component { component } } -@_functionBuilder +@resultBuilder enum ArrayBuilder: FunctionBuilderProtocol { typealias Expression = E typealias Component = FunctionBuilder diff --git a/test/Constraints/function_builder_availability.swift b/test/Constraints/function_builder_availability.swift index 6e08f25875cd6..a823845b2091f 100644 --- a/test/Constraints/function_builder_availability.swift +++ b/test/Constraints/function_builder_availability.swift @@ -7,7 +7,7 @@ enum Either { case second(U) } -@_functionBuilder +@resultBuilder struct TupleBuilder { // expected-note{{add 'buildLimitedAvailability(_:)' to the function builder 'TupleBuilder' to erase type information for less-available types}}{{22-22=\n static func buildLimitedAvailability(_ component: <#Component#>) -> <#Component#> {\n <#code#>\n \}}} static func buildBlock(_ t1: T1) -> (T1) { return (t1) @@ -77,7 +77,7 @@ tuplify(true) { cond in } // Function builder that can perform type erasure for #available. -@_functionBuilder +@resultBuilder struct TupleBuilderAvailability { static func buildBlock(_ t1: T1) -> (T1) { return (t1) diff --git a/test/Constraints/function_builder_diags.swift b/test/Constraints/function_builder_diags.swift index d2389a8cc8b58..fd206e68ede41 100644 --- a/test/Constraints/function_builder_diags.swift +++ b/test/Constraints/function_builder_diags.swift @@ -5,7 +5,7 @@ enum Either { case second(U) } -@_functionBuilder +@resultBuilder struct TupleBuilder { // expected-note 2 {{struct 'TupleBuilder' declared here}} static func buildBlock() -> () { } @@ -44,7 +44,7 @@ struct TupleBuilder { // expected-note 2 {{struct 'TupleBuilder' declared here}} } } -@_functionBuilder +@resultBuilder struct TupleBuilderWithoutIf { // expected-note 3{{struct 'TupleBuilderWithoutIf' declared here}} // expected-note@-1{{add 'buildOptional(_:)' to the function builder 'TupleBuilderWithoutIf' to add support for 'if' statements without an 'else'}} // expected-note@-2{{add 'buildEither(first:)' and 'buildEither(second:)' to the function builder 'TupleBuilderWithoutIf' to add support for 'if'-'else' and 'switch'}} @@ -171,7 +171,7 @@ struct TupleP : P { init(_: U) {} } -@_functionBuilder +@resultBuilder struct Builder { static func buildBlock(_ stmt1: S0, _ stmt2: S1) // expected-note {{required by static method 'buildBlock' where 'S1' = 'Label<_>.Type'}} -> TupleP<(S0, S1)> where S0: P, S1: P { @@ -331,7 +331,7 @@ func checkSingleReturn(cond: Bool) { // rdar://problem/59116520 func checkImplicitSelfInClosure() { - @_functionBuilder + @resultBuilder struct Builder { static func buildBlock(_ children: String...) -> Element { Element() } } @@ -518,7 +518,7 @@ func testCaseVarTypes(e: E3) { } // Test for buildFinalResult. -@_functionBuilder +@resultBuilder struct WrapperBuilder { static func buildBlock() -> () { } @@ -581,7 +581,7 @@ func testWrapperBuilder() { func rdar61347993() { struct Result {} - @_functionBuilder + @resultBuilder struct Builder { static func buildBlock() -> Result { Result() diff --git a/test/Constraints/function_builder_infer.swift b/test/Constraints/function_builder_infer.swift index 1715352d39424..3deeff29a4473 100644 --- a/test/Constraints/function_builder_infer.swift +++ b/test/Constraints/function_builder_infer.swift @@ -5,7 +5,7 @@ enum Either { case second(U) } -@_functionBuilder +@resultBuilder struct TupleBuilder { static func buildBlock() -> () { return () @@ -79,7 +79,7 @@ struct DoNotTupleMe { extension DoNotTupleMe: Tupled { } -@_functionBuilder +@resultBuilder struct OtherTupleBuilder { static func buildBlock() -> () { return () diff --git a/test/Constraints/function_builder_one_way.swift b/test/Constraints/function_builder_one_way.swift index e3519800497f1..a45c99cac066a 100644 --- a/test/Constraints/function_builder_one_way.swift +++ b/test/Constraints/function_builder_one_way.swift @@ -7,7 +7,7 @@ enum Either { case second(U) } -@_functionBuilder +@resultBuilder struct TupleBuilder { static func buildBlock(_ t1: T1) -> T1 { return t1 diff --git a/test/Constraints/function_builder_opaque_result.swift b/test/Constraints/function_builder_opaque_result.swift index e75db6d9ea431..bbd64c830c88b 100644 --- a/test/Constraints/function_builder_opaque_result.swift +++ b/test/Constraints/function_builder_opaque_result.swift @@ -3,7 +3,7 @@ protocol Taggable {} extension String: Taggable {} -@_functionBuilder +@resultBuilder struct TaggableBuilder { static func buildBlock(_ params: Taggable...) -> String { return "Your tags weren't worth keeping anyway" diff --git a/test/Constraints/rdar64890308.swift b/test/Constraints/rdar64890308.swift index e445751610afd..1c8a7c857d6f6 100644 --- a/test/Constraints/rdar64890308.swift +++ b/test/Constraints/rdar64890308.swift @@ -4,7 +4,7 @@ import Swift -@_functionBuilder +@resultBuilder class ArrayBuilder { static func buildBlock() -> [Element] { [] } static func buildBlock(_ elt: Element) -> [Element] { [elt] } @@ -32,7 +32,7 @@ bar("") { x, ty in protocol P {} extension String : P {} -@_functionBuilder +@resultBuilder struct FooBuilder {} extension FooBuilder where T : P { diff --git a/test/Constraints/rdar65320500.swift b/test/Constraints/rdar65320500.swift index dfb8f722a2692..dd99f9d0906be 100644 --- a/test/Constraints/rdar65320500.swift +++ b/test/Constraints/rdar65320500.swift @@ -2,7 +2,7 @@ struct Result {} -@_functionBuilder +@resultBuilder struct Builder { static func buildBlock() -> Result { Result() diff --git a/test/Constraints/sr13183.swift b/test/Constraints/sr13183.swift index d3a33d2e5b83e..0cd8a4106c6be 100644 --- a/test/Constraints/sr13183.swift +++ b/test/Constraints/sr13183.swift @@ -14,7 +14,7 @@ struct Map { let transform: (Value) -> Transformed } -@_functionBuilder +@resultBuilder struct ScopeBuilder { static func buildExpression(_ map: Map) -> Component { Component() diff --git a/test/IDE/complete_ambiguous.swift b/test/IDE/complete_ambiguous.swift index f1f535ad53c05..5793ce6633b1a 100644 --- a/test/IDE/complete_ambiguous.swift +++ b/test/IDE/complete_ambiguous.swift @@ -264,7 +264,7 @@ _ = testing([Point(4, 89)]) { arg in struct Thing { init(_ block: (Point) -> Void) {} } -@_functionBuilder +@resultBuilder struct ThingBuilder { static func buildBlock(_ x: Thing...) -> [Thing] { x } } diff --git a/test/IDE/complete_decl_attribute.swift b/test/IDE/complete_decl_attribute.swift index 23606dacfa21e..c3e1f228d1262 100644 --- a/test/IDE/complete_decl_attribute.swift +++ b/test/IDE/complete_decl_attribute.swift @@ -89,6 +89,7 @@ struct MyStruct {} // KEYWORD3-NEXT: Keyword/None: NSApplicationMain[#Class Attribute#]; name=NSApplicationMain{{$}} // KEYWORD3-NEXT: Keyword/None: usableFromInline[#Class Attribute#]; name=usableFromInline // KEYWORD3-NEXT: Keyword/None: propertyWrapper[#Class Attribute#]; name=propertyWrapper +// KEYWORD3-NEXT: Keyword/None: resultBuilder[#Class Attribute#]; name=resultBuilder // KEYWORD3-NEXT: End completions @#^KEYWORD3_2^#IB class C2 {} @@ -104,6 +105,7 @@ struct MyStruct {} // KEYWORD4-NEXT: Keyword/None: usableFromInline[#Enum Attribute#]; name=usableFromInline // KEYWORD4-NEXT: Keyword/None: frozen[#Enum Attribute#]; name=frozen // KEYWORD4-NEXT: Keyword/None: propertyWrapper[#Enum Attribute#]; name=propertyWrapper +// KEYWORD4-NEXT: Keyword/None: resultBuilder[#Enum Attribute#]; name=resultBuilder // KEYWORD4-NEXT: End completions @@ -116,6 +118,7 @@ struct MyStruct {} // KEYWORD5-NEXT: Keyword/None: usableFromInline[#Struct Attribute#]; name=usableFromInline // KEYWORD5-NEXT: Keyword/None: frozen[#Struct Attribute#]; name=frozen // KEYWORD5-NEXT: Keyword/None: propertyWrapper[#Struct Attribute#]; name=propertyWrapper +// KEYWORD5-NEXT: Keyword/None: resultBuilder[#Struct Attribute#]; name=resultBuilder // KEYWORD5-NEXT: End completions @#^ON_GLOBALVAR^# var globalVar @@ -241,6 +244,7 @@ struct _S { // ON_MEMBER_LAST-DAG: Keyword/None: GKInspectable[#Declaration Attribute#]; name=GKInspectable // ON_MEMBER_LAST-DAG: Keyword/None: IBSegueAction[#Declaration Attribute#]; name=IBSegueAction // ON_MEMBER_LAST-DAG: Keyword/None: propertyWrapper[#Declaration Attribute#]; name=propertyWrapper +// ON_MEMBER_LAST-DAG: Keyword/None: resultBuilder[#Declaration Attribute#]; name=resultBuilder // ON_MEMBER_LAST-DAG: Keyword/None: differentiable[#Declaration Attribute#]; name=differentiable // ON_MEMBER_LAST-DAG: Keyword/None: derivative[#Declaration Attribute#]; name=derivative // ON_MEMBER_LAST-DAG: Keyword/None: transpose[#Declaration Attribute#]; name=transpose @@ -287,6 +291,7 @@ func dummy2() {} // KEYWORD_LAST-NEXT: Keyword/None: GKInspectable[#Declaration Attribute#]; name=GKInspectable{{$}} // KEYWORD_LAST-NEXT: Keyword/None: frozen[#Declaration Attribute#]; name=frozen // KEYWORD_LAST-NEXT: Keyword/None: propertyWrapper[#Declaration Attribute#]; name=propertyWrapper +// KEYWORD_LAST-NEXT: Keyword/None: resultBuilder[#Declaration Attribute#]; name=resultBuilder // KEYWORD_LAST-NEXT: Keyword/None: differentiable[#Declaration Attribute#]; name=differentiable // KEYWORD_LAST-NEXT: Keyword/None: IBSegueAction[#Declaration Attribute#]; name=IBSegueAction{{$}} // KEYWORD_LAST-NEXT: Keyword/None: derivative[#Declaration Attribute#]; name=derivative diff --git a/test/IDE/complete_function_builder.swift b/test/IDE/complete_function_builder.swift index cc3ae5fe6a3d2..519e916a1708b 100644 --- a/test/IDE/complete_function_builder.swift +++ b/test/IDE/complete_function_builder.swift @@ -23,7 +23,7 @@ extension Taggable { extension Int: Taggable { } extension String: Taggable { } -@_functionBuilder +@resultBuilder struct TaggedBuilder { static func buildBlock() -> () { } @@ -87,7 +87,7 @@ enum MyEnum { case east, west case north, south } -@_functionBuilder +@resultBuilder struct EnumToVoidBuilder { static func buildBlock() {} static func buildBlock(_ :MyEnum) {} @@ -96,7 +96,7 @@ struct EnumToVoidBuilder { } func acceptBuilder(@EnumToVoidBuilder body: () -> Void) {} -@_functionBuilder +@resultBuilder struct AnyBuilder { static func buildBlock(_ components: Any...) -> Any { 5 } diff --git a/test/IDE/complete_rdar63965160.swift b/test/IDE/complete_rdar63965160.swift index 41dd721ffb8d6..7fe399330d19f 100644 --- a/test/IDE/complete_rdar63965160.swift +++ b/test/IDE/complete_rdar63965160.swift @@ -3,7 +3,7 @@ protocol View {} -@_functionBuilder +@resultBuilder struct Builder { static func buildBlock(_ c0: C0) -> C0 {} static func buildBlock(_ c0: C0, _ c1: C1) -> C1 {} diff --git a/test/IDE/print_swift_module.swift b/test/IDE/print_swift_module.swift index a54c31fa390b3..d8b5dfe4771f7 100644 --- a/test/IDE/print_swift_module.swift +++ b/test/IDE/print_swift_module.swift @@ -27,7 +27,7 @@ public func returnsAlias() -> Alias { return (0, 0) } -@_functionBuilder +@resultBuilder struct BridgeBuilder { static func buildBlock(_: Any...) {} } diff --git a/test/Index/function_builders.swift b/test/Index/function_builders.swift index 2dcb9bc93b3b7..7e78b2a7cccf6 100644 --- a/test/Index/function_builders.swift +++ b/test/Index/function_builders.swift @@ -16,7 +16,7 @@ extension Int: Taggable { } extension String: Taggable { } extension Double: Taggable { } -@_functionBuilder +@resultBuilder struct TaggedBuilder { static func buildBlock() -> () { } diff --git a/test/ModuleInterface/function_builders.swift b/test/ModuleInterface/function_builders.swift index 7657e10665769..6f20cf5ecc687 100644 --- a/test/ModuleInterface/function_builders.swift +++ b/test/ModuleInterface/function_builders.swift @@ -4,7 +4,7 @@ // RUN: %target-swift-frontend -I %t -typecheck -verify %S/Inputs/function_builders_client.swift // RUN: %target-swift-frontend -compile-module-from-interface %t/FunctionBuilders.swiftinterface -o %t/FunctionBuilders.swiftmodule -@_functionBuilder +@resultBuilder public struct TupleBuilder { public static func buildBlock(_ t1: T1, _ t2: T2) -> (T1, T2) { return (t1, t2) diff --git a/test/NameLookup/Inputs/custom_attrs_A.swift b/test/NameLookup/Inputs/custom_attrs_A.swift index c8035b4fa167e..56779d10c098d 100644 --- a/test/NameLookup/Inputs/custom_attrs_A.swift +++ b/test/NameLookup/Inputs/custom_attrs_A.swift @@ -7,7 +7,7 @@ public struct Wrapper { } } -@_functionBuilder +@resultBuilder public struct Builder { static func buildBlock(_ component: T) -> T { component } } diff --git a/test/NameLookup/Inputs/custom_attrs_B.swift b/test/NameLookup/Inputs/custom_attrs_B.swift index c8035b4fa167e..56779d10c098d 100644 --- a/test/NameLookup/Inputs/custom_attrs_B.swift +++ b/test/NameLookup/Inputs/custom_attrs_B.swift @@ -7,7 +7,7 @@ public struct Wrapper { } } -@_functionBuilder +@resultBuilder public struct Builder { static func buildBlock(_ component: T) -> T { component } } diff --git a/test/Profiler/coverage_function_builder.swift b/test/Profiler/coverage_function_builder.swift index fff05ac6ef0f6..be6874f87f641 100644 --- a/test/Profiler/coverage_function_builder.swift +++ b/test/Profiler/coverage_function_builder.swift @@ -1,7 +1,7 @@ // RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sorted-sil -emit-sil -module-name coverage_functon_builder %s | %FileCheck %s // RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -emit-ir %s -@_functionBuilder +@resultBuilder struct Summer { static func buildBlock(_ x: Int...) -> Int { return x.reduce(0, +) diff --git a/test/SILGen/function_builder_curry_thunks.swift b/test/SILGen/function_builder_curry_thunks.swift index ab6802060b854..57764b21cb244 100644 --- a/test/SILGen/function_builder_curry_thunks.swift +++ b/test/SILGen/function_builder_curry_thunks.swift @@ -1,6 +1,6 @@ // RUN: %target-swift-emit-silgen %s | %FileCheck %s -@_functionBuilder +@resultBuilder struct Builder { static func buildBlock(_ t1: T1) -> (T1) { return (t1) @@ -30,4 +30,4 @@ class Outer { // CHECK-LABEL: sil private [ossa] @$s29function_builder_curry_thunks5OuterC5InnerV5buildAA7HandlerVvgyycAEcfu_ : $@convention(thin) (Outer.Inner) -> @owned @callee_guaranteed () -> () // CHECK-LABEL: sil private [ossa] @$s29function_builder_curry_thunks5OuterC5InnerV5buildAA7HandlerVvgyycAEcfu_yycfu0_ : $@convention(thin) (Outer.Inner) -> () // CHECK-LABEL: sil private [ossa] @$s29function_builder_curry_thunks5OuterC5InnerV7handler33_DC254A3F89F9C7E65D25434E199F17A4LLyyF : $@convention(method) (Outer.Inner) -> () -// CHECK-LABEL: sil private [ossa] @$s29function_builder_curry_thunks5OuterC5InnerV5buildAA7HandlerVvgyycAEcfu1_ : $@convention(thin) (Outer.Inner) -> @owned @callee_guaranteed () -> () \ No newline at end of file +// CHECK-LABEL: sil private [ossa] @$s29function_builder_curry_thunks5OuterC5InnerV5buildAA7HandlerVvgyycAEcfu1_ : $@convention(thin) (Outer.Inner) -> @owned @callee_guaranteed () -> () diff --git a/test/SILGen/function_builder_memberwise.swift b/test/SILGen/function_builder_memberwise.swift index c87930e35aa06..f445f8f90a520 100644 --- a/test/SILGen/function_builder_memberwise.swift +++ b/test/SILGen/function_builder_memberwise.swift @@ -1,6 +1,6 @@ // RUN: %target-swift-emit-silgen %s | %FileCheck %s -@_functionBuilder +@resultBuilder struct TupleBuilder { static func buildBlock(_ t1: T1) -> (T1) { return (t1) diff --git a/test/Sema/type_eraser.swift b/test/Sema/type_eraser.swift index 87b6fdf99ce28..70d1a8ad009fe 100644 --- a/test/Sema/type_eraser.swift +++ b/test/Sema/type_eraser.swift @@ -50,7 +50,7 @@ dynamic func testComposition() -> some Composition { } // CHECK-LABEL: struct_decl{{.*}}Builder -@_functionBuilder +@resultBuilder struct Builder { static func buildBlock(_ params: P...) -> ConcreteP { return ConcreteP() diff --git a/test/Serialization/function_builders.swift b/test/Serialization/function_builders.swift index 1f866ff7538e0..17f62f41af1d8 100644 --- a/test/Serialization/function_builders.swift +++ b/test/Serialization/function_builders.swift @@ -2,7 +2,7 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t %s // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -skip-deinit=false -module-to-print=function_builders -I %t -source-filename=%s | %FileCheck %s -@_functionBuilder +@resultBuilder public struct TupleBuilder { public static func buildBlock(_ t1: T1, _ t2: T2) -> (T1, T2) { return (t1, t2) diff --git a/test/SourceKit/CodeComplete/complete_sequence_builderfunc.swift b/test/SourceKit/CodeComplete/complete_sequence_builderfunc.swift index 0681743f269ee..ca72041ba087d 100644 --- a/test/SourceKit/CodeComplete/complete_sequence_builderfunc.swift +++ b/test/SourceKit/CodeComplete/complete_sequence_builderfunc.swift @@ -3,7 +3,7 @@ struct Empty: Entity { var value: Void = () } -@_functionBuilder +@resultBuilder struct Builder { static func buildBlock() -> { Empty() } static func buildBlock(_ t: T) -> T { t } diff --git a/test/SourceKit/CodeExpand/code-expand.swift b/test/SourceKit/CodeExpand/code-expand.swift index 33c2a82643e53..132473dbf58dc 100644 --- a/test/SourceKit/CodeExpand/code-expand.swift +++ b/test/SourceKit/CodeExpand/code-expand.swift @@ -37,7 +37,7 @@ dispatch_after(<#T##when: dispatch_time_t##dispatch_time_t#>, <#T##queue: dispat // CHECK-NEXT: <#code#> // CHECK-NEXT: } -@_functionBuilder +@resultBuilder struct MyBuilder {} func acceptBuilder(@MyBuilder body: () -> Result) {} do { diff --git a/test/SourceKit/CursorInfo/function_builder.swift b/test/SourceKit/CursorInfo/function_builder.swift index 0b24fd04ea470..f5e218ff7fd49 100644 --- a/test/SourceKit/CursorInfo/function_builder.swift +++ b/test/SourceKit/CursorInfo/function_builder.swift @@ -15,7 +15,7 @@ extension Taggable { extension Int: Taggable { } extension String: Taggable { } -@_functionBuilder +@resultBuilder struct TaggedBuilder { static func buildBlock() -> () { } @@ -50,8 +50,8 @@ func testAcceptColorTagged(i: Int, s: String) { // ATTR_NAME-NEXT: s:11BuilderTest06TaggedA0V // ATTR_NAME-NEXT: TaggedBuilder.Type // ATTR_NAME-NEXT: $s11BuilderTest06TaggedA0VyxGmD -// ATTR_NAME-NEXT: @_functionBuilder struct TaggedBuilder<Tag> -// ATTR_NAME-NEXT: @_functionBuilder struct TaggedBuilder<Tag> +// ATTR_NAME-NEXT: @resultBuilder struct TaggedBuilder<Tag> +// ATTR_NAME-NEXT: @resultBuilder struct TaggedBuilder<Tag> // Generic argument in attribute name. // RUN: %sourcekitd-test -req=cursor -pos=35:47 %s -- %s -module-name BuilderTest | %FileCheck %s --check-prefix=ATTR_GENERICARG diff --git a/test/attr/attr_function_builder.swift b/test/attr/attr_function_builder.swift new file mode 100644 index 0000000000000..4f22cb8338883 --- /dev/null +++ b/test/attr/attr_function_builder.swift @@ -0,0 +1,6 @@ +// RUN: %target-typecheck-verify-swift + +@_functionBuilder // expected-warning{{'@_functionBuilder' has been renamed to '@resultBuilder'}}{{2-18=resultBuilder}} +struct MyBuilder { + static func buildBlock(_: Any...) -> Any { } +} diff --git a/test/decl/function_builder_fixits.swift b/test/decl/function_builder_fixits.swift index 34f6b896cf419..a4502938cde4a 100644 --- a/test/decl/function_builder_fixits.swift +++ b/test/decl/function_builder_fixits.swift @@ -2,10 +2,10 @@ // UNSUPPORTED: windows // Line-feeds in Fix-Its fail to check on Windows. -@_functionBuilder +@resultBuilder struct Maker {} // expected-error {{function builder must provide at least one static 'buildBlock' method}}{{15-15=\n static func buildBlock(_ components: <#Component#>...) -> <#Component#> {\n <#code#>\n \}}} -@_functionBuilder +@resultBuilder struct TupleBuilderWithoutIf { // expected-note 3{{struct 'TupleBuilderWithoutIf' declared here}} // expected-note@-1{{add 'buildOptional(_:)' to the function builder 'TupleBuilderWithoutIf' to add support for 'if' statements without an 'else'}}{{31-31=\n static func buildOptional(_ component: <#Component#>?) -> <#Component#> {\n <#code#>\n \}}} // expected-note@-2{{add 'buildEither(first:)' and 'buildEither(second:)' to the function builder 'TupleBuilderWithoutIf' to add support for 'if'-'else' and 'switch'}}{{31-31=\n static func buildEither(first component: <#Component#>) -> <#Component#> {\n <#code#>\n \}\n\n static func buildEither(second component: <#Component#>) -> <#Component#> {\n <#code#>\n \}}} diff --git a/test/decl/var/function_builders.swift b/test/decl/var/function_builders.swift index b3b860535f1cc..d0bb1b8cb8abd 100644 --- a/test/decl/var/function_builders.swift +++ b/test/decl/var/function_builders.swift @@ -1,15 +1,15 @@ // RUN: %target-typecheck-verify-swift -@_functionBuilder // expected-error {{'@_functionBuilder' attribute cannot be applied to this declaration}} +@resultBuilder // expected-error {{'@resultBuilder' attribute cannot be applied to this declaration}} var globalBuilder: Int -@_functionBuilder // expected-error {{'@_functionBuilder' attribute cannot be applied to this declaration}} +@resultBuilder // expected-error {{'@resultBuilder' attribute cannot be applied to this declaration}} func globalBuilderFunction() -> Int { return 0 } -@_functionBuilder +@resultBuilder struct Maker {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} -@_functionBuilder +@resultBuilder class Inventor {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} @Maker // expected-error {{function builder attribute 'Maker' can only be applied to a parameter, function, or computed property}} @@ -65,11 +65,11 @@ func makerParamExtra(@Maker(5) // expected-error {{function builder attributes c func makerParamAutoclosure(@Maker // expected-error {{function builder attribute 'Maker' cannot be applied to an autoclosure parameter}} fn: @autoclosure () -> ()) {} -@_functionBuilder +@resultBuilder struct GenericMaker {} // expected-note {{generic type 'GenericMaker' declared here}} expected-error {{function builder must provide at least one static 'buildBlock' method}} struct GenericContainer { // expected-note {{generic type 'GenericContainer' declared here}} - @_functionBuilder + @resultBuilder struct Maker {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} } @@ -88,7 +88,7 @@ func makeParamNestedBound(@GenericContainer.Maker protocol P { } -@_functionBuilder +@resultBuilder struct ConstrainedGenericMaker {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} @@ -100,7 +100,7 @@ struct WithinGeneric { fn: () -> ()) {} } -@_functionBuilder +@resultBuilder struct ValidBuilder1 { static func buildBlock(_ exprs: Any...) -> Int { return exprs.count } } @@ -111,41 +111,41 @@ extension BuilderFuncHelper { static func buildBlock(_ exprs: Any...) -> Int { return exprs.count } } -@_functionBuilder +@resultBuilder struct ValidBuilder2: BuilderFuncHelper {} class BuilderFuncBase { static func buildBlock(_ exprs: Any...) -> Int { return exprs.count } } -@_functionBuilder +@resultBuilder class ValidBuilder3: BuilderFuncBase {} -@_functionBuilder +@resultBuilder struct ValidBuilder4 {} extension ValidBuilder4 { static func buildBlock(_ exprs: Any...) -> Int { return exprs.count } } -@_functionBuilder +@resultBuilder struct ValidBuilder5 { static func buildBlock() -> Int { 0 } } -@_functionBuilder +@resultBuilder struct InvalidBuilder1 {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} -@_functionBuilder +@resultBuilder struct InvalidBuilder2 { // expected-error {{function builder must provide at least one static 'buildBlock' method}} func buildBlock(_ exprs: Any...) -> Int { return exprs.count } // expected-note {{did you mean to make instance method 'buildBlock' static?}} {{3-3=static }} } -@_functionBuilder +@resultBuilder struct InvalidBuilder3 { // expected-error {{function builder must provide at least one static 'buildBlock' method}} var buildBlock: (Any...) -> Int = { return $0.count } // expected-note {{potential match 'buildBlock' is not a static method}} } -@_functionBuilder +@resultBuilder struct InvalidBuilder4 {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} extension InvalidBuilder4 { func buildBlock(_ exprs: Any...) -> Int { return exprs.count } // expected-note {{did you mean to make instance method 'buildBlock' static?}} {{3-3=static }} @@ -156,10 +156,10 @@ extension InvalidBuilderHelper { func buildBlock(_ exprs: Any...) -> Int { return exprs.count } // expected-note {{potential match 'buildBlock' is not a static method}} } -@_functionBuilder +@resultBuilder struct InvalidBuilder5: InvalidBuilderHelper {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} -@_functionBuilder +@resultBuilder struct InvalidBuilder6 { // expected-error {{function builder must provide at least one static 'buildBlock' method}} static var buildBlock: Int = 0 // expected-note {{potential match 'buildBlock' is not a static method}} } @@ -168,7 +168,7 @@ struct Callable { func callAsFunction(_ exprs: Any...) -> Int { return exprs.count } } -@_functionBuilder +@resultBuilder struct InvalidBuilder7 { // expected-error {{function builder must provide at least one static 'buildBlock' method}} static var buildBlock = Callable() // expected-note {{potential match 'buildBlock' is not a static method}} } @@ -177,7 +177,7 @@ class BuilderVarBase { static var buildBlock: (Any...) -> Int = { return $0.count } // expected-note {{potential match 'buildBlock' is not a static method}} } -@_functionBuilder +@resultBuilder class InvalidBuilder8: BuilderVarBase {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} protocol BuilderVarHelper {} @@ -186,15 +186,15 @@ extension BuilderVarHelper { static var buildBlock: (Any...) -> Int { { return $0.count } } // expected-note {{potential match 'buildBlock' is not a static method}} } -@_functionBuilder +@resultBuilder struct InvalidBuilder9: BuilderVarHelper {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} -@_functionBuilder +@resultBuilder struct InvalidBuilder10 { // expected-error {{function builder must provide at least one static 'buildBlock' method}} static var buildBlock: (Any...) -> Int = { return $0.count } // expected-note {{potential match 'buildBlock' is not a static method}} } -@_functionBuilder +@resultBuilder enum InvalidBuilder11 { // expected-error {{function builder must provide at least one static 'buildBlock' method}} case buildBlock(Any) // expected-note {{enum case 'buildBlock' cannot be used to satisfy the function builder requirement}} } diff --git a/test/decl/var/function_builders_availability.swift b/test/decl/var/function_builders_availability.swift index 0c0c57dd2d3af..87675360f314c 100644 --- a/test/decl/var/function_builders_availability.swift +++ b/test/decl/var/function_builders_availability.swift @@ -10,7 +10,7 @@ struct Do { var value: T } -@_functionBuilder +@resultBuilder struct TupleBuilder { static func buildBlock(_ t1: T1) -> (T1) { return (t1) diff --git a/test/multifile/Inputs/function_builder_definition.swift b/test/multifile/Inputs/function_builder_definition.swift index 0ea2a999db246..2ef32b64c734a 100644 --- a/test/multifile/Inputs/function_builder_definition.swift +++ b/test/multifile/Inputs/function_builder_definition.swift @@ -1,4 +1,4 @@ -@_functionBuilder +@resultBuilder struct TupleBuilder { static func buildBlock(_ t1: T1, _ t2: T2) -> (T1, T2) { return (t1, t2) diff --git a/test/refactoring/SyntacticRename/FindRangeOutputs/custom-attrs/Foo.swift.expected b/test/refactoring/SyntacticRename/FindRangeOutputs/custom-attrs/Foo.swift.expected index 32bdf931f0d4e..e187bed15ba64 100644 --- a/test/refactoring/SyntacticRename/FindRangeOutputs/custom-attrs/Foo.swift.expected +++ b/test/refactoring/SyntacticRename/FindRangeOutputs/custom-attrs/Foo.swift.expected @@ -16,7 +16,7 @@ struct Projection { var item: T } -@_functionBuilder +@resultBuilder struct /*builder:def*/Other { public static func buildBlock(_ components: String...) -> String { return components.joined() diff --git a/test/refactoring/SyntacticRename/FindRangeOutputs/custom-attrs/Other.swift.expected b/test/refactoring/SyntacticRename/FindRangeOutputs/custom-attrs/Other.swift.expected index d5de19bd68f74..fb29831beeea3 100644 --- a/test/refactoring/SyntacticRename/FindRangeOutputs/custom-attrs/Other.swift.expected +++ b/test/refactoring/SyntacticRename/FindRangeOutputs/custom-attrs/Other.swift.expected @@ -16,7 +16,7 @@ struct Projection { var item: T } -@_functionBuilder +@resultBuilder struct /*builder:def*/Other { public static func buildBlock(_ components: String...) -> String { return components.joined() diff --git a/test/refactoring/SyntacticRename/FindRangeOutputs/custom-attrs/wrapped.swift.expected b/test/refactoring/SyntacticRename/FindRangeOutputs/custom-attrs/wrapped.swift.expected index 09af7db4cfad3..c1e1d9629f0bf 100644 --- a/test/refactoring/SyntacticRename/FindRangeOutputs/custom-attrs/wrapped.swift.expected +++ b/test/refactoring/SyntacticRename/FindRangeOutputs/custom-attrs/wrapped.swift.expected @@ -16,7 +16,7 @@ struct Projection { var item: T } -@_functionBuilder +@resultBuilder struct /*builder:def*/Other { public static func buildBlock(_ components: String...) -> String { return components.joined() diff --git a/test/refactoring/SyntacticRename/Outputs/custom-attrs/Foo.swift.expected b/test/refactoring/SyntacticRename/Outputs/custom-attrs/Foo.swift.expected index 8eff1ed61127c..966344d78b267 100644 --- a/test/refactoring/SyntacticRename/Outputs/custom-attrs/Foo.swift.expected +++ b/test/refactoring/SyntacticRename/Outputs/custom-attrs/Foo.swift.expected @@ -16,7 +16,7 @@ struct Projection { var item: T } -@_functionBuilder +@resultBuilder struct /*builder:def*/Other { public static func buildBlock(_ components: String...) -> String { return components.joined() diff --git a/test/refactoring/SyntacticRename/Outputs/custom-attrs/Other.swift.expected b/test/refactoring/SyntacticRename/Outputs/custom-attrs/Other.swift.expected index 4a9a3335b8dc2..fa65f668a2742 100644 --- a/test/refactoring/SyntacticRename/Outputs/custom-attrs/Other.swift.expected +++ b/test/refactoring/SyntacticRename/Outputs/custom-attrs/Other.swift.expected @@ -16,7 +16,7 @@ struct Projection { var item: T } -@_functionBuilder +@resultBuilder struct /*builder:def*/OtherBuilder { public static func buildBlock(_ components: String...) -> String { return components.joined() diff --git a/test/refactoring/SyntacticRename/Outputs/custom-attrs/wrapped.swift.expected b/test/refactoring/SyntacticRename/Outputs/custom-attrs/wrapped.swift.expected index 9371dacd6ef87..42e35fc3e389d 100644 --- a/test/refactoring/SyntacticRename/Outputs/custom-attrs/wrapped.swift.expected +++ b/test/refactoring/SyntacticRename/Outputs/custom-attrs/wrapped.swift.expected @@ -16,7 +16,7 @@ struct Projection { var item: T } -@_functionBuilder +@resultBuilder struct /*builder:def*/Other { public static func buildBlock(_ components: String...) -> String { return components.joined() diff --git a/test/refactoring/SyntacticRename/custom-attrs.swift b/test/refactoring/SyntacticRename/custom-attrs.swift index 9c58a292a9bb1..c14d363cf81b9 100644 --- a/test/refactoring/SyntacticRename/custom-attrs.swift +++ b/test/refactoring/SyntacticRename/custom-attrs.swift @@ -16,7 +16,7 @@ struct Projection { var item: T } -@_functionBuilder +@resultBuilder struct /*builder:def*/Other { public static func buildBlock(_ components: String...) -> String { return components.joined() diff --git a/userdocs/diagnostics/function-builder-methods.md b/userdocs/diagnostics/function-builder-methods.md index 533ffdbac7620..a9e7970421a2b 100644 --- a/userdocs/diagnostics/function-builder-methods.md +++ b/userdocs/diagnostics/function-builder-methods.md @@ -7,7 +7,7 @@ example function builder illustrates the various function-building methods one can define: ```swift -@_functionBuilder +@resultBuilder struct ExampleFunctionBuilder { /// The type of individual statement expressions in the transformed function, /// which defaults to Component if buildExpression() is not provided. diff --git a/validation-test/Sema/type_checker_crashers_fixed/rdar50869732.swift b/validation-test/Sema/type_checker_crashers_fixed/rdar50869732.swift index 0b758816d6734..ca99694a28df2 100644 --- a/validation-test/Sema/type_checker_crashers_fixed/rdar50869732.swift +++ b/validation-test/Sema/type_checker_crashers_fixed/rdar50869732.swift @@ -8,7 +8,7 @@ struct Generic { init(_ value: T) {} } -@_functionBuilder +@resultBuilder struct Builder { static func buildBlock(_ c0: C0, _ c1: C1) // expected-note 2 {{where 'C0' = 'Empty'}} expected-note {{where 'C1' = 'Test>'}} // expected-note@-1 {{'buildBlock' declared here}} diff --git a/validation-test/compiler_crashers_2_fixed/rdar67239650.swift b/validation-test/compiler_crashers_2_fixed/rdar67239650.swift index 5ec036b8b61cf..a24a080f4d0e2 100644 --- a/validation-test/compiler_crashers_2_fixed/rdar67239650.swift +++ b/validation-test/compiler_crashers_2_fixed/rdar67239650.swift @@ -1,6 +1,6 @@ // RUN: %target-swift-frontend -typecheck %s -@_functionBuilder +@resultBuilder struct SillyBuilder { static func buildBlock() -> () {} } From 9f83ceda3f24ba689d0a7c25b32ef82c2490ce61 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Tue, 20 Oct 2020 16:44:17 -0700 Subject: [PATCH 587/745] Add more file-level comments to MemAccessUtils. --- include/swift/SIL/MemAccessUtils.h | 35 ++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index 54f9ce67f3fdb..a922d194c7526 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -49,7 +49,7 @@ /// /// For better identification an access base, use /// AccessedStorage::compute(). It returns an AccessedStorage value -/// that identifies the storage location of a memory access. It provides APIs +/// that identifies the storage of a memory access. It provides APIs /// for inspecting type of accessed storage and allows for disambiguation /// between different types of storage and different properties within a class. /// @@ -64,7 +64,7 @@ /// from an enforced begin_access or from any memory operation that is part of a /// formal access, then it returns a valid AccessedStorage value. If the memory /// operation is not part of a formal access, then it still identifies the -/// accessed location as a best effort, but the result may be invalid storage. +/// accessed storage as a best effort, but the result may be invalid storage. /// /// An active goal is to require compute() to always return a /// valid AccessedStorage value even for operations that aren't part of a @@ -77,13 +77,30 @@ /// make incorrect assumptions about the absence of access to globals or class /// properties. /// -/// identifyFormalAccess() is similar to AccessedStorage::compute(), but returns -/// the formally accessed storage of a begin_access instruction. This must -/// return a valid AccessedStorage value unless the access has "Unsafe" -/// enforcement. The formal access location may be nested within an outer -/// begin_access. For the purpose of exclusivity, nested accesses are considered -/// distinct formal accesses so they return distinct AccessedStorage values even -/// though they may access the same memory. +/// AccessedStorage::computeInScope() returns an AccessedStorage value for the +/// immediately enclosing access scope. Within a formal access, it always +/// returns a Nested storage kind, which provides the begin_access marker. +/// +/// identifyFormalAccess() works like AccessedStorage::computeInScope(), but +/// finds the storage corresponding to a begin_access marker, rather than an +/// arbitrary address. This must return a valid AccessedStorage value unless the +/// access has "Unsafe" enforcement. The given begin_access marker may be nested +/// within another, outer access scope. For the purpose of exclusivity, nested +/// accesses are considered distinct formal accesses so they return distinct +/// AccessedStorage values even though they may access the same memory. This +/// way, nested accesses do not appear to conflict. +/// +/// AccessPath identifies both the accessed storage and the path to a specific +/// storage location within that storage object. See ProgrammersGuide.md and the +/// class comments below for details. AccessPath::compute() and +/// AccessPath::computeInScope() mirror the AccessedStorage API. +/// AccessPath::contains() and AccessPath::mayOverlap() provide efficient +/// comparison of access paths. +/// +/// AccessPath::collectUses() provides all reachable uses of the accessed +/// storage, allowing the selection of Exact, Inner, or Overlapping uses. +/// visitAccessedStorageUses() and visitAccessPathUses() generalize +/// handling of all reachable uses for a given storage location. /// //===----------------------------------------------------------------------===// From 090d57a092b4a890ff183f7cd0a12712cba57717 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Sun, 27 Sep 2020 22:44:39 -0700 Subject: [PATCH 588/745] Look past class casts when finding the reference root. For class storage AccessedStorage is now close to what some passes use for RC identity, but it still does not look past wrapping references in an Optional. --- include/swift/SIL/MemAccessUtils.h | 4 ++ lib/SIL/Utils/MemAccessUtils.cpp | 60 ++++++++++++++++++++---------- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index 54f9ce67f3fdb..b2961f995895a 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -1201,6 +1201,10 @@ SILBasicBlock::iterator removeBeginAccess(BeginAccessInst *beginAccess); namespace swift { +/// Return true if \p value is a cast that preserves the identity of the +/// reference at operand zero. +bool isRCIdentityPreservingCast(SingleValueInstruction *svi); + /// If \p svi is an access projection, return an address-type operand for the /// incoming address. /// diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index 95770fdf049bc..abd11b6155c4d 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -365,15 +365,27 @@ bool swift::isLetAddress(SILValue address) { // MARK: FindReferenceRoot //===----------------------------------------------------------------------===// +bool swift::isRCIdentityPreservingCast(SingleValueInstruction *svi) { + switch (svi->getKind()) { + default: + return false; + // Ignore ownership casts + case SILInstructionKind::CopyValueInst: + case SILInstructionKind::BeginBorrowInst: + // Ignore class type casts + case SILInstructionKind::UpcastInst: + case SILInstructionKind::UncheckedRefCastInst: + case SILInstructionKind::UnconditionalCheckedCastInst: + case SILInstructionKind::UnconditionalCheckedCastValueInst: + case SILInstructionKind::RefToBridgeObjectInst: + case SILInstructionKind::BridgeObjectToRefInst: + return true; + } +} + namespace { // Essentially RC identity where the starting point is already a reference. -// -// FIXME: We cannot currently see through type casts for true RC identity, -// because property indices are not unique within a class hierarchy. Either fix -// RefElementAddr::getFieldNo so it returns a unique index, or figure out a -// different way to encode the property's VarDecl. (Note that the lack of a -// unique property index could be the source of bugs elsewhere). class FindReferenceRoot { SmallPtrSet visitedPhis; @@ -386,18 +398,13 @@ class FindReferenceRoot { protected: // Return an invalid value for a phi with no resolved inputs. - // - // FIXME: We should be able to see through RC identity like this: - // nextRoot = stripRCIdentityCasts(nextRoot); SILValue recursiveFindRoot(SILValue ref) { - while (true) { - SILValue nextRoot = ref; - nextRoot = stripOwnershipInsts(nextRoot); - if (nextRoot == ref) + while (auto *svi = dyn_cast(ref)) { + if (!isRCIdentityPreservingCast(svi)) { break; - ref = nextRoot; - } - + } + ref = svi->getOperand(0); + }; auto *phi = dyn_cast(ref); if (!phi || !phi->isPhiArgument()) { return ref; @@ -558,8 +565,16 @@ const ValueDecl *AccessedStorage::getDecl(SILValue base) const { return global->getDecl(); case Class: { - auto *decl = getObject()->getType().getNominalOrBoundGenericNominal(); - return decl ? getIndexedField(decl, getPropertyIndex()) : nullptr; + // The property index is relative to the VarDecl in ref_element_addr, and + // can only be reliably determined when the base is avaiable. Otherwise, we + // can only make a best effort to extract it from the object type, which + // might not even be a class in the case of bridge objects. + if (ClassDecl *classDecl = + base ? cast(base)->getClassDecl() + : getObject()->getType().getClassOrBoundGenericClass()) { + return getIndexedField(classDecl, getPropertyIndex()); + } + return nullptr; } case Tail: return nullptr; @@ -1297,7 +1312,14 @@ void AccessPathDefUseTraversal::followProjection(SingleValueInstruction *svi, AccessPathDefUseTraversal::UseKind AccessPathDefUseTraversal::visitSingleValueUser(SingleValueInstruction *svi, DFSEntry dfs) { - if (isAccessedStorageCast(svi)) { + if (dfs.isRef()) { + if (isRCIdentityPreservingCast(svi)) { + pushUsers(svi, dfs); + return IgnoredUse; + } + // 'svi' will be processed below as either RefElementAddrInst, + // RefTailAddrInst, or some unknown LeafUse. + } else if (isAccessedStorageCast(svi)) { pushUsers(svi, dfs); return IgnoredUse; } From f31296d63b6e5fcc66b5acfd4cc26ade683006c7 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Wed, 14 Oct 2020 02:41:25 -0700 Subject: [PATCH 589/745] Fix isRCIdentityPreservingCast to handle trivial-to-reference casts And add assertions. --- include/swift/SIL/MemAccessUtils.h | 4 +-- lib/SIL/IR/SILType.cpp | 3 ++ lib/SIL/Utils/InstructionUtils.cpp | 56 +++++++++++------------------- lib/SIL/Utils/MemAccessUtils.cpp | 15 ++++++-- lib/SIL/Verifier/SILVerifier.cpp | 10 ++++++ 5 files changed, 48 insertions(+), 40 deletions(-) diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index b2961f995895a..5835c5707cbf7 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -1201,8 +1201,8 @@ SILBasicBlock::iterator removeBeginAccess(BeginAccessInst *beginAccess); namespace swift { -/// Return true if \p value is a cast that preserves the identity of the -/// reference at operand zero. +/// Return true if \p svi is a cast that preserves the identity and +/// reference-counting equivalence of the reference at operand zero. bool isRCIdentityPreservingCast(SingleValueInstruction *svi); /// If \p svi is an access projection, return an address-type operand for the diff --git a/lib/SIL/IR/SILType.cpp b/lib/SIL/IR/SILType.cpp index 48ca1e5cbc47d..c478940010411 100644 --- a/lib/SIL/IR/SILType.cpp +++ b/lib/SIL/IR/SILType.cpp @@ -129,6 +129,9 @@ bool SILType::isPointerSizeAndAligned() { // // TODO: handle casting to a loadable existential by generating // init_existential_ref. Until then, only promote to a heap object dest. +// +// This cannot allow trivial-to-reference casts, as required by +// isRCIdentityPreservingCast. bool SILType::canRefCast(SILType operTy, SILType resultTy, SILModule &M) { auto fromTy = operTy.unwrapOptionalType(); auto toTy = resultTy.unwrapOptionalType(); diff --git a/lib/SIL/Utils/InstructionUtils.cpp b/lib/SIL/Utils/InstructionUtils.cpp index 73e84417f3fbb..6b9d7e6bf0aea 100644 --- a/lib/SIL/Utils/InstructionUtils.cpp +++ b/lib/SIL/Utils/InstructionUtils.cpp @@ -12,6 +12,7 @@ #define DEBUG_TYPE "sil-inst-utils" #include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/MemAccessUtils.h" #include "swift/AST/SubstitutionMap.h" #include "swift/Basic/NullablePtr.h" #include "swift/Basic/STLExtras.h" @@ -64,20 +65,6 @@ SILValue swift::getUnderlyingObjectStopAtMarkDependence(SILValue v) { } } -static bool isRCIdentityPreservingCast(ValueKind Kind) { - switch (Kind) { - case ValueKind::UpcastInst: - case ValueKind::UncheckedRefCastInst: - case ValueKind::UnconditionalCheckedCastInst: - case ValueKind::UnconditionalCheckedCastValueInst: - case ValueKind::RefToBridgeObjectInst: - case ValueKind::BridgeObjectToRefInst: - return true; - default: - return false; - } -} - /// Return the underlying SILValue after stripping off identity SILArguments if /// we belong to a BB with one predecessor. SILValue swift::stripSinglePredecessorArgs(SILValue V) { @@ -122,42 +109,39 @@ SILValue swift::stripSinglePredecessorArgs(SILValue V) { } } -SILValue swift::stripCastsWithoutMarkDependence(SILValue V) { +SILValue swift::stripCastsWithoutMarkDependence(SILValue v) { while (true) { - V = stripSinglePredecessorArgs(V); - - auto K = V->getKind(); - if (isRCIdentityPreservingCast(K) - || K == ValueKind::UncheckedTrivialBitCastInst - || K == ValueKind::BeginAccessInst - || K == ValueKind::EndCOWMutationInst) { - V = cast(V)->getOperand(0); - continue; + v = stripSinglePredecessorArgs(v); + if (auto *svi = dyn_cast(v)) { + if (isRCIdentityPreservingCast(svi) + || isa(v) + || isa(v) + || isa(v)) { + v = svi->getOperand(0); + continue; + } } - - return V; + return v; } } SILValue swift::stripCasts(SILValue v) { while (true) { v = stripSinglePredecessorArgs(v); - - auto k = v->getKind(); - if (isRCIdentityPreservingCast(k) - || k == ValueKind::UncheckedTrivialBitCastInst - || k == ValueKind::MarkDependenceInst - || k == ValueKind::BeginAccessInst) { - v = cast(v)->getOperand(0); - continue; + if (auto *svi = dyn_cast(v)) { + if (isRCIdentityPreservingCast(svi) + || isa(v) + || isa(v) + || isa(v)) { + v = cast(v)->getOperand(0); + continue; + } } - SILValue v2 = stripOwnershipInsts(v); if (v2 != v) { v = v2; continue; } - return v; } } diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index abd11b6155c4d..922fb78ea8894 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -365,6 +365,14 @@ bool swift::isLetAddress(SILValue address) { // MARK: FindReferenceRoot //===----------------------------------------------------------------------===// +// On some platforms, casting from a metatype to a reference type dynamically +// allocates a ref-counted box for the metatype. Naturally that is the place +// where RC-identity begins. Considering the source of such a casts to be +// RC-identical would confuse ARC optimization, which might eliminate a retain +// of such an object completely. +// +// The SILVerifier checks that none of these operations cast a nontrivial value +// to a reference except unconditional_checked_cast[_value]. bool swift::isRCIdentityPreservingCast(SingleValueInstruction *svi) { switch (svi->getKind()) { default: @@ -375,11 +383,14 @@ bool swift::isRCIdentityPreservingCast(SingleValueInstruction *svi) { // Ignore class type casts case SILInstructionKind::UpcastInst: case SILInstructionKind::UncheckedRefCastInst: - case SILInstructionKind::UnconditionalCheckedCastInst: - case SILInstructionKind::UnconditionalCheckedCastValueInst: case SILInstructionKind::RefToBridgeObjectInst: case SILInstructionKind::BridgeObjectToRefInst: return true; + case SILInstructionKind::UnconditionalCheckedCastInst: + case SILInstructionKind::UnconditionalCheckedCastValueInst: + // If the source is nontrivial, then this checked cast may actually create a + // new object, so its source is not ref-count equivalent. + return !svi->getOperand(0)->getType().isTrivial(*svi->getFunction()); } } diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 9a9df3bc03dfa..52f47d996d3e4 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -3591,6 +3591,15 @@ class SILVerifier : public SILVerifierBase { verifyOpenedArchetype(CI, CI->getType().getASTType()); } + // Make sure that opcodes handled by isRCIdentityPreservingCast cannot cast + // from a trivial to a reference type. Such a cast may dynamically + // instantiate a new reference-counted object. + void checkNoTrivialToReferenceCast(SingleValueInstruction *svi) { + require(!svi->getOperand(0)->getType().isTrivial(*svi->getFunction()) + || svi->getType().isTrivial(*svi->getFunction()), + "Unexpected trivial-to-reference conversion: "); + } + /// Verify if a given type is or contains an opened archetype or dynamic self. /// If this is the case, verify that the provided instruction has a type /// dependent operand for it. @@ -3753,6 +3762,7 @@ class SILVerifier : public SILVerifierBase { void checkUpcastInst(UpcastInst *UI) { require(UI->getType() != UI->getOperand()->getType(), "can't upcast to same type"); + checkNoTrivialToReferenceCast(UI); if (UI->getType().is()) { CanType instTy(UI->getType().castTo()->getInstanceType()); require(UI->getOperand()->getType().is(), From d8dd6208e956263cad81efe2c86b026126924920 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Sun, 27 Sep 2020 22:44:55 -0700 Subject: [PATCH 590/745] Test cases for AccessedStorage with reference casts. --- test/SILOptimizer/accessed_storage.sil | 30 ++-- .../accessed_storage_analysis.sil | 2 +- test/SILOptimizer/accesspath_uses.sil | 160 ++++++++++++++++++ 3 files changed, 176 insertions(+), 16 deletions(-) diff --git a/test/SILOptimizer/accessed_storage.sil b/test/SILOptimizer/accessed_storage.sil index 26c9775556a1a..f5796389524c4 100644 --- a/test/SILOptimizer/accessed_storage.sil +++ b/test/SILOptimizer/accessed_storage.sil @@ -131,14 +131,14 @@ struct MyArray { // CHECK-LABEL: @arrayValue // CHECK: load [trivial] %{{.*}} : $*Builtin.Int64 -// CHECK: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Tail %{{.*}} = struct_extract %{{.*}} : $_MyBridgeStorage, #_MyBridgeStorage.rawValue // CHECK: Base: %{{.*}} = ref_tail_addr [immutable] %{{.*}} : $__ContiguousArrayStorageBase, $Int64 -// CHECK: Storage: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Storage: Tail %{{.*}} = struct_extract %{{.*}} : $_MyBridgeStorage, #_MyBridgeStorage.rawValue // CHECK: Path: (@3,#0) // CHECK: load [trivial] %{{.*}} : $*Builtin.Int64 -// CHECK: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Tail %{{.*}} = struct_extract %5 : $_MyBridgeStorage, #_MyBridgeStorage.rawValue // CHECK: Base: %{{.*}} = ref_tail_addr [immutable] %{{.*}} : $__ContiguousArrayStorageBase, $Int64 -// CHECK: Storage: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Storage: Tail %{{.*}} = struct_extract %5 : $_MyBridgeStorage, #_MyBridgeStorage.rawValue // CHECK: Path: (@4,#0) sil [ossa] @arrayValue : $@convention(thin) (@guaranteed MyArray) -> Int64 { bb0(%0 : @guaranteed $MyArray): @@ -191,7 +191,7 @@ bb0(%0 : $Builtin.RawPointer): // CHECK-LABEL: @staticIndexAddrSubobject // CHECK: ###For MemOp: %5 = load [trivial] %4 : $*Int64 -// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer // user: %1 +// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer // CHECK: INVALID sil [ossa] @staticIndexAddrSubobject : $@convention(thin) (Builtin.RawPointer) -> () { bb0(%0 : $Builtin.RawPointer): @@ -271,10 +271,10 @@ class B : A { // CHECK: Field: var prop1: Int64 Index: 1 // CHECK: Path: () // CHECK: store %0 to %{{.*}} : $*Int64 -// CHECK: Class %{{.*}} = upcast %{{.*}} : $B to $A +// CHECK: Class %{{.*}} = alloc_ref $B // CHECK: Field: var prop0: Int64 Index: 0 // CHECK: Base: %{{.*}} = ref_element_addr %{{.*}} : $A, #A.prop0 -// CHECK: Storage: Class %{{.*}} = upcast %{{.*}} : $B to $A +// CHECK: Storage: Class %{{.*}} = alloc_ref $B // CHECK: Field: var prop0: Int64 Index: 0 // CHECK: Path: () sil @testNonUniquePropertyIndex : $@convention(thin) (Int64) -> () { @@ -291,21 +291,21 @@ bb0(%0 : $Int64): // CHECK-LABEL: @testRefTailAndStruct0 // CHECK: %{{.*}} = load %{{.*}} : $*Builtin.Int64 -// CHECK: Class %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase -// CHECK: Field: @usableFromInline final var countAndCapacity: _ArrayBody Index: 0 +// CHECK: Class %{{.*}} = struct_extract %{{.*}} : $_MyBridgeStorage, #_MyBridgeStorage.rawValue +// CHECK: Index: 0 // CHECK: Base: %{{.*}} = ref_element_addr [immutable] %{{.*}} : $__ContiguousArrayStorageBase, #__ContiguousArrayStorageBase.countAndCapacity -// CHECK: Storage: Class %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase -// CHECK: Field: @usableFromInline final var countAndCapacity: _ArrayBody Index: 0 +// CHECK: Storage: Class %{{.*}} = struct_extract %{{.*}} : $_MyBridgeStorage, #_MyBridgeStorage.rawValue +// CHECK: Index: 0 // CHECK: Path: (#0,#0,#0) // CHECK: %{{.*}} = load %{{.*}} : $*_StringGuts -// CHECK: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Tail %{{.*}} = struct_extract %{{.*}} : $_MyBridgeStorage, #_MyBridgeStorage.rawValue // CHECK: Base: %{{.*}} = ref_tail_addr [immutable] %{{.*}} : $__ContiguousArrayStorageBase, $String -// CHECK: Storage: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Storage: Tail %{{.*}} = struct_extract %{{.*}} : $_MyBridgeStorage, #_MyBridgeStorage.rawValue // CHECK: Path: (#0) // CHECK: %{{.*}} = load %{{.*}} : $*String -// CHECK: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Tail %{{.*}} = struct_extract %{{.*}} : $_MyBridgeStorage, #_MyBridgeStorage.rawValue // CHECK: Base: %{{.*}} = ref_tail_addr [immutable] %{{.*}} : $__ContiguousArrayStorageBase, $String -// CHECK: Storage: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase +// CHECK: Storage: Tail %{{.*}} = struct_extract %{{.*}} : $_MyBridgeStorage, #_MyBridgeStorage.rawValue // CHECK: Path: () sil hidden [noinline] @testRefTailAndStruct0 : $@convention(thin) (@owned MyArray) -> () { bb0(%0 : $MyArray): diff --git a/test/SILOptimizer/accessed_storage_analysis.sil b/test/SILOptimizer/accessed_storage_analysis.sil index fc9db4532cd12..908b57e8e6af4 100644 --- a/test/SILOptimizer/accessed_storage_analysis.sil +++ b/test/SILOptimizer/accessed_storage_analysis.sil @@ -638,7 +638,7 @@ class BaseClass { class SubClass : BaseClass {} // CHECK-LABEL: @testElementAddressPhiAccess -// CHECK: [read] [no_nested_conflict] Class %{{.*}} = upcast %0 : $SubClass to $BaseClass +// CHECK: [read] [no_nested_conflict] Class %{{.*}} = argument of bb0 : $SubClass // CHECK: Field: @_hasInitialValue var f: Float sil @testElementAddressPhiAccess : $@convention(thin) (@guaranteed SubClass) -> () { bb0(%0 : $SubClass): diff --git a/test/SILOptimizer/accesspath_uses.sil b/test/SILOptimizer/accesspath_uses.sil index 73bf391e593c9..82e72c6438bd6 100644 --- a/test/SILOptimizer/accesspath_uses.sil +++ b/test/SILOptimizer/accesspath_uses.sil @@ -706,3 +706,163 @@ bb0(%0 : $*T, %1 : $*T): %10 = tuple () return %10 : $() } + +// Test reference root casts. +class A { + var prop0: Int64 +} +class B : A { + var alsoProp0: Int64 +} + +// CHECK-LABEL: @testReferenceRootCast +// CHECK: ###For MemOp: store %0 to [[ADR0:%.*]] : $*Int64 +// CHECK: Class %{{.*}} = alloc_ref $B +// CHECK: Field: var prop0: Int64 Index: 0 +// CHECK: Base: [[ADR0]] = ref_element_addr %{{.*}} : $A, #A.prop0 +// CHECK: Storage: Class %1 = alloc_ref $B +// CHECK: Field: var prop0: Int64 Index: 0 +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK-NEXT: store %0 to [[ADR0]] : $*Int64 +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: store %0 to [[ADR0]] : $*Int64 +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK: ###For MemOp: store %0 to [[ADR1:%.*]] : $*Int64 +// CHECK-NEXT: Class %{{.*}} = alloc_ref $B +// CHECK-NEXT: Field: var alsoProp0: Int64 Index: 1 +// CHECK-NEXT: Base: [[ADR1]] = ref_element_addr %10 : $B, #B.alsoProp0 +// CHECK-NEXT: Storage: Class %{{.*}} = alloc_ref $B +// CHECK-NEXT: Field: var alsoProp0: Int64 Index: 1 +// CHECK-NEXT: Path: () +// CHECK-NEXT: Exact Uses { +// CHECK-NEXT: store %0 to [[ADR1]] : $*Int64 +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK: Overlapping Uses { +// CHECK-NEXT: store %0 to [[ADR1]] : $*Int64 +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +sil @testReferenceRootCast : $@convention(thin) (Int64) -> () { +bb0(%0 : $Int64): + %1 = alloc_ref $B + cond_br undef, bb1, bb2 + +bb1: + %3 = upcast %1 : $B to $A + br bb3(%3 : $A) + +bb2: + %5 = upcast %1 : $B to $A + br bb3(%5 : $A) + +bb3(%7 : $A): + %8 = ref_element_addr %7 : $A, #A.prop0 + store %0 to %8 : $*Int64 + %10 = unchecked_ref_cast %7 : $A to $B + %11 = ref_element_addr %10 : $B, #B.alsoProp0 + store %0 to %11 : $*Int64 + %99 = tuple () + return %99 : $() +} + +// Test a phi cycle where the phi itself is the root (there is no common phi reference). +// CHECK-LABEL: @testRootPhiCycle +// CHECK: ###For MemOp: %{{.*}} = load %{{.*}} : $*Double +// CHECK: Storage: Tail %{{.*}} = argument of bb3 : $Builtin.BridgeObject +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK-NEXT: %{{.*}} = load %{{.*}} : $*Double +// CHECK-NEXT: Path: () +// CHECK-NEXT: %{{.*}} = load %{{.*}} : $*Double +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK: ###For MemOp: %{{.*}} = load %{{.*}} : $*Double +// CHECK: Storage: Tail %{{.*}} = argument of bb3 : $Builtin.BridgeObject +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK-NEXT: %{{.*}} = load %{{.*}} : $*Double +// CHECK-NEXT: Path: () +// CHECK-NEXT: %{{.*}} = load %{{.*}} : $*Double +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +sil @testRootPhiCycle : $@convention(thin) (Builtin.BridgeObject, Builtin.BridgeObject) -> () { +bb0(%0 : $Builtin.BridgeObject, %1 : $Builtin.BridgeObject): + cond_br undef, bb1, bb2 + +bb1: + br bb3(%0 : $Builtin.BridgeObject) + +bb2: + br bb3(%1 : $Builtin.BridgeObject) + +bb3(%820 : $Builtin.BridgeObject): + %834 = unchecked_ref_cast %820 : $Builtin.BridgeObject to $C + %844 = ref_tail_addr [immutable] %834 : $C, $Double + %853 = load %844 : $*Double + %943 = load %844 : $*Double + cond_br undef, bb4, bb5 + +bb4: + br bb3(%820 : $Builtin.BridgeObject) + +bb5: + %999 = tuple () + return %999 : $() +} + +class C {} + +// Test a CoW mutation followed by a phi cycle. +// +// Note: FindReferenceRoot currently does not see past CoW mutation, +// but probably should. +// CHECK: @testCowMutationPhi +// CHECK: ###For MemOp: (%{{.*}}, %{{.*}}) = begin_cow_mutation [native] %0 : $Builtin.BridgeObject +// CHECK-NEXT: ###For MemOp: %{{.*}} = load %{{.*}} : $*Double +// CHECK: Storage: Tail %{{.*}} = argument of bb3 : $Builtin.BridgeObject +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK-NEXT: %{{.*}} = load %{{.*}} : $*Double +// CHECK-NEXT: Path: () +// CHECK-NEXT: %{{.*}} = load %{{.*}} : $*Double +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +// CHECK: ###For MemOp: %{{.*}} = load %{{.*}} : $*Double +// CHECK: Storage: Tail %{{.*}} = argument of bb3 : $Builtin.BridgeObject +// CHECK: Path: () +// CHECK: Exact Uses { +// CHECK-NEXT: %{{.*}} = load %{{.*}} : $*Double +// CHECK-NEXT: Path: () +// CHECK-NEXT: %{{.*}} = load %{{.*}} : $*Double +// CHECK-NEXT: Path: () +// CHECK-NEXT: } +sil @testCowMutationPhi : $@convention(thin) (Builtin.BridgeObject) -> () { +bb0(%0 : $Builtin.BridgeObject): + cond_br undef, bb1, bb2 + +bb1: + br bb3(%0 : $Builtin.BridgeObject) + +bb2: + (%3, %4) = begin_cow_mutation [native] %0 : $Builtin.BridgeObject + %5 = end_cow_mutation [keep_unique] %4 : $Builtin.BridgeObject + br bb3(%5 : $Builtin.BridgeObject) + +bb3(%820 : $Builtin.BridgeObject): + %834 = unchecked_ref_cast %820 : $Builtin.BridgeObject to $C + %844 = ref_tail_addr [immutable] %834 : $C, $Double + %853 = load %844 : $*Double + %943 = load %844 : $*Double + cond_br undef, bb4, bb5 + +bb4: + br bb3(%820 : $Builtin.BridgeObject) + +bb5: + %999 = tuple () + return %999 : $() +} From abecaa3dcd21b30c99b923913fc3a04888dabd88 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 20 Oct 2020 16:30:22 -0700 Subject: [PATCH 591/745] [Gardening] Move Fingerprints Tests Under Incremental --- .../Fingerprints}/Inputs/class-fingerprint/definesAB-after.swift | 0 .../Fingerprints}/Inputs/class-fingerprint/definesAB-before.swift | 0 .../Fingerprints}/Inputs/class-fingerprint/main.swift | 0 .../Fingerprints}/Inputs/class-fingerprint/ofm.json | 0 .../Fingerprints}/Inputs/class-fingerprint/usesA.swift | 0 .../Fingerprints}/Inputs/class-fingerprint/usesB.swift | 0 .../Fingerprints}/Inputs/enum-fingerprint/definesAB-after.swift | 0 .../Fingerprints}/Inputs/enum-fingerprint/definesAB-before.swift | 0 .../Fingerprints}/Inputs/enum-fingerprint/main.swift | 0 .../Fingerprints}/Inputs/enum-fingerprint/ofm.json | 0 .../Fingerprints}/Inputs/enum-fingerprint/usesA.swift | 0 .../Fingerprints}/Inputs/enum-fingerprint/usesB.swift | 0 .../Inputs/extension-adds-member/definesAB-after.swift | 0 .../Inputs/extension-adds-member/definesAB-before.swift | 0 .../Fingerprints}/Inputs/extension-adds-member/main.swift | 0 .../Fingerprints}/Inputs/extension-adds-member/ofm.json | 0 .../Fingerprints}/Inputs/extension-adds-member/usesA.swift | 0 .../Fingerprints}/Inputs/extension-adds-member/usesB.swift | 0 .../Inputs/protocol-fingerprint/definesAB-after.swift | 0 .../Inputs/protocol-fingerprint/definesAB-before.swift | 0 .../Fingerprints}/Inputs/protocol-fingerprint/main.swift | 0 .../Fingerprints}/Inputs/protocol-fingerprint/ofm.json | 0 .../Fingerprints}/Inputs/protocol-fingerprint/usesA.swift | 0 .../Fingerprints}/Inputs/protocol-fingerprint/usesB.swift | 0 .../Fingerprints}/Inputs/struct-fingerprint/definesAB-after.swift | 0 .../Inputs/struct-fingerprint/definesAB-before.swift | 0 .../Fingerprints}/Inputs/struct-fingerprint/main.swift | 0 .../Fingerprints}/Inputs/struct-fingerprint/ofm.json | 0 .../Fingerprints}/Inputs/struct-fingerprint/usesA.swift | 0 .../Fingerprints}/Inputs/struct-fingerprint/usesB.swift | 0 .../Fingerprints}/class-fingerprint.swift | 0 .../Fingerprints}/enum-fingerprint.swift | 0 .../Fingerprints}/extension-adds-member.swift | 0 .../Fingerprints}/protocol-fingerprint.swift | 0 .../Fingerprints}/struct-fingerprint.swift | 0 35 files changed, 0 insertions(+), 0 deletions(-) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/class-fingerprint/definesAB-after.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/class-fingerprint/definesAB-before.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/class-fingerprint/main.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/class-fingerprint/ofm.json (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/class-fingerprint/usesA.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/class-fingerprint/usesB.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/enum-fingerprint/definesAB-after.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/enum-fingerprint/definesAB-before.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/enum-fingerprint/main.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/enum-fingerprint/ofm.json (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/enum-fingerprint/usesA.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/enum-fingerprint/usesB.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/extension-adds-member/definesAB-after.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/extension-adds-member/definesAB-before.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/extension-adds-member/main.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/extension-adds-member/ofm.json (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/extension-adds-member/usesA.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/extension-adds-member/usesB.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/protocol-fingerprint/definesAB-after.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/protocol-fingerprint/definesAB-before.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/protocol-fingerprint/main.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/protocol-fingerprint/ofm.json (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/protocol-fingerprint/usesA.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/protocol-fingerprint/usesB.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/struct-fingerprint/definesAB-after.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/struct-fingerprint/definesAB-before.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/struct-fingerprint/main.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/struct-fingerprint/ofm.json (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/struct-fingerprint/usesA.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/Inputs/struct-fingerprint/usesB.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/class-fingerprint.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/enum-fingerprint.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/extension-adds-member.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/protocol-fingerprint.swift (100%) rename test/{Frontend/PrivateFingerprints => Incremental/Fingerprints}/struct-fingerprint.swift (100%) diff --git a/test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/definesAB-after.swift b/test/Incremental/Fingerprints/Inputs/class-fingerprint/definesAB-after.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/definesAB-after.swift rename to test/Incremental/Fingerprints/Inputs/class-fingerprint/definesAB-after.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/definesAB-before.swift b/test/Incremental/Fingerprints/Inputs/class-fingerprint/definesAB-before.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/definesAB-before.swift rename to test/Incremental/Fingerprints/Inputs/class-fingerprint/definesAB-before.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/main.swift b/test/Incremental/Fingerprints/Inputs/class-fingerprint/main.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/main.swift rename to test/Incremental/Fingerprints/Inputs/class-fingerprint/main.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/ofm.json b/test/Incremental/Fingerprints/Inputs/class-fingerprint/ofm.json similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/ofm.json rename to test/Incremental/Fingerprints/Inputs/class-fingerprint/ofm.json diff --git a/test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/usesA.swift b/test/Incremental/Fingerprints/Inputs/class-fingerprint/usesA.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/usesA.swift rename to test/Incremental/Fingerprints/Inputs/class-fingerprint/usesA.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/usesB.swift b/test/Incremental/Fingerprints/Inputs/class-fingerprint/usesB.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/class-fingerprint/usesB.swift rename to test/Incremental/Fingerprints/Inputs/class-fingerprint/usesB.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/definesAB-after.swift b/test/Incremental/Fingerprints/Inputs/enum-fingerprint/definesAB-after.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/definesAB-after.swift rename to test/Incremental/Fingerprints/Inputs/enum-fingerprint/definesAB-after.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/definesAB-before.swift b/test/Incremental/Fingerprints/Inputs/enum-fingerprint/definesAB-before.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/definesAB-before.swift rename to test/Incremental/Fingerprints/Inputs/enum-fingerprint/definesAB-before.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/main.swift b/test/Incremental/Fingerprints/Inputs/enum-fingerprint/main.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/main.swift rename to test/Incremental/Fingerprints/Inputs/enum-fingerprint/main.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/ofm.json b/test/Incremental/Fingerprints/Inputs/enum-fingerprint/ofm.json similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/ofm.json rename to test/Incremental/Fingerprints/Inputs/enum-fingerprint/ofm.json diff --git a/test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/usesA.swift b/test/Incremental/Fingerprints/Inputs/enum-fingerprint/usesA.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/usesA.swift rename to test/Incremental/Fingerprints/Inputs/enum-fingerprint/usesA.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/usesB.swift b/test/Incremental/Fingerprints/Inputs/enum-fingerprint/usesB.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/enum-fingerprint/usesB.swift rename to test/Incremental/Fingerprints/Inputs/enum-fingerprint/usesB.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/definesAB-after.swift b/test/Incremental/Fingerprints/Inputs/extension-adds-member/definesAB-after.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/definesAB-after.swift rename to test/Incremental/Fingerprints/Inputs/extension-adds-member/definesAB-after.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/definesAB-before.swift b/test/Incremental/Fingerprints/Inputs/extension-adds-member/definesAB-before.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/definesAB-before.swift rename to test/Incremental/Fingerprints/Inputs/extension-adds-member/definesAB-before.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/main.swift b/test/Incremental/Fingerprints/Inputs/extension-adds-member/main.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/main.swift rename to test/Incremental/Fingerprints/Inputs/extension-adds-member/main.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/ofm.json b/test/Incremental/Fingerprints/Inputs/extension-adds-member/ofm.json similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/ofm.json rename to test/Incremental/Fingerprints/Inputs/extension-adds-member/ofm.json diff --git a/test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/usesA.swift b/test/Incremental/Fingerprints/Inputs/extension-adds-member/usesA.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/usesA.swift rename to test/Incremental/Fingerprints/Inputs/extension-adds-member/usesA.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/usesB.swift b/test/Incremental/Fingerprints/Inputs/extension-adds-member/usesB.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/extension-adds-member/usesB.swift rename to test/Incremental/Fingerprints/Inputs/extension-adds-member/usesB.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/definesAB-after.swift b/test/Incremental/Fingerprints/Inputs/protocol-fingerprint/definesAB-after.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/definesAB-after.swift rename to test/Incremental/Fingerprints/Inputs/protocol-fingerprint/definesAB-after.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/definesAB-before.swift b/test/Incremental/Fingerprints/Inputs/protocol-fingerprint/definesAB-before.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/definesAB-before.swift rename to test/Incremental/Fingerprints/Inputs/protocol-fingerprint/definesAB-before.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/main.swift b/test/Incremental/Fingerprints/Inputs/protocol-fingerprint/main.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/main.swift rename to test/Incremental/Fingerprints/Inputs/protocol-fingerprint/main.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/ofm.json b/test/Incremental/Fingerprints/Inputs/protocol-fingerprint/ofm.json similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/ofm.json rename to test/Incremental/Fingerprints/Inputs/protocol-fingerprint/ofm.json diff --git a/test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/usesA.swift b/test/Incremental/Fingerprints/Inputs/protocol-fingerprint/usesA.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/usesA.swift rename to test/Incremental/Fingerprints/Inputs/protocol-fingerprint/usesA.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/usesB.swift b/test/Incremental/Fingerprints/Inputs/protocol-fingerprint/usesB.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/protocol-fingerprint/usesB.swift rename to test/Incremental/Fingerprints/Inputs/protocol-fingerprint/usesB.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/definesAB-after.swift b/test/Incremental/Fingerprints/Inputs/struct-fingerprint/definesAB-after.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/definesAB-after.swift rename to test/Incremental/Fingerprints/Inputs/struct-fingerprint/definesAB-after.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/definesAB-before.swift b/test/Incremental/Fingerprints/Inputs/struct-fingerprint/definesAB-before.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/definesAB-before.swift rename to test/Incremental/Fingerprints/Inputs/struct-fingerprint/definesAB-before.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/main.swift b/test/Incremental/Fingerprints/Inputs/struct-fingerprint/main.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/main.swift rename to test/Incremental/Fingerprints/Inputs/struct-fingerprint/main.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/ofm.json b/test/Incremental/Fingerprints/Inputs/struct-fingerprint/ofm.json similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/ofm.json rename to test/Incremental/Fingerprints/Inputs/struct-fingerprint/ofm.json diff --git a/test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/usesA.swift b/test/Incremental/Fingerprints/Inputs/struct-fingerprint/usesA.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/usesA.swift rename to test/Incremental/Fingerprints/Inputs/struct-fingerprint/usesA.swift diff --git a/test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/usesB.swift b/test/Incremental/Fingerprints/Inputs/struct-fingerprint/usesB.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/Inputs/struct-fingerprint/usesB.swift rename to test/Incremental/Fingerprints/Inputs/struct-fingerprint/usesB.swift diff --git a/test/Frontend/PrivateFingerprints/class-fingerprint.swift b/test/Incremental/Fingerprints/class-fingerprint.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/class-fingerprint.swift rename to test/Incremental/Fingerprints/class-fingerprint.swift diff --git a/test/Frontend/PrivateFingerprints/enum-fingerprint.swift b/test/Incremental/Fingerprints/enum-fingerprint.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/enum-fingerprint.swift rename to test/Incremental/Fingerprints/enum-fingerprint.swift diff --git a/test/Frontend/PrivateFingerprints/extension-adds-member.swift b/test/Incremental/Fingerprints/extension-adds-member.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/extension-adds-member.swift rename to test/Incremental/Fingerprints/extension-adds-member.swift diff --git a/test/Frontend/PrivateFingerprints/protocol-fingerprint.swift b/test/Incremental/Fingerprints/protocol-fingerprint.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/protocol-fingerprint.swift rename to test/Incremental/Fingerprints/protocol-fingerprint.swift diff --git a/test/Frontend/PrivateFingerprints/struct-fingerprint.swift b/test/Incremental/Fingerprints/struct-fingerprint.swift similarity index 100% rename from test/Frontend/PrivateFingerprints/struct-fingerprint.swift rename to test/Incremental/Fingerprints/struct-fingerprint.swift From 5260a6226956bf0f4cff613734c9f94f9e136800 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 20 Oct 2020 16:33:10 -0700 Subject: [PATCH 592/745] [Gardening] Drop 'private-' Prefix From Tests --- .../{private-function-fine.swift => function-fine.swift} | 0 ...ion-return-type-fine.swift => function-return-type-fine.swift} | 0 ...conformer-ext-fine.swift => protocol-conformer-ext-fine.swift} | 0 ...rotocol-conformer-fine.swift => protocol-conformer-fine.swift} | 0 ...{private-struct-member-fine.swift => struct-member-fine.swift} | 0 .../{private-subscript-fine.swift => subscript-fine.swift} | 0 .../{private-typealias-fine.swift => typealias-fine.swift} | 0 .../Dependencies/{private-var-fine.swift => var-fine.swift} | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename test/Incremental/Dependencies/{private-function-fine.swift => function-fine.swift} (100%) rename test/Incremental/Dependencies/{private-function-return-type-fine.swift => function-return-type-fine.swift} (100%) rename test/Incremental/Dependencies/{private-protocol-conformer-ext-fine.swift => protocol-conformer-ext-fine.swift} (100%) rename test/Incremental/Dependencies/{private-protocol-conformer-fine.swift => protocol-conformer-fine.swift} (100%) rename test/Incremental/Dependencies/{private-struct-member-fine.swift => struct-member-fine.swift} (100%) rename test/Incremental/Dependencies/{private-subscript-fine.swift => subscript-fine.swift} (100%) rename test/Incremental/Dependencies/{private-typealias-fine.swift => typealias-fine.swift} (100%) rename test/Incremental/Dependencies/{private-var-fine.swift => var-fine.swift} (100%) diff --git a/test/Incremental/Dependencies/private-function-fine.swift b/test/Incremental/Dependencies/function-fine.swift similarity index 100% rename from test/Incremental/Dependencies/private-function-fine.swift rename to test/Incremental/Dependencies/function-fine.swift diff --git a/test/Incremental/Dependencies/private-function-return-type-fine.swift b/test/Incremental/Dependencies/function-return-type-fine.swift similarity index 100% rename from test/Incremental/Dependencies/private-function-return-type-fine.swift rename to test/Incremental/Dependencies/function-return-type-fine.swift diff --git a/test/Incremental/Dependencies/private-protocol-conformer-ext-fine.swift b/test/Incremental/Dependencies/protocol-conformer-ext-fine.swift similarity index 100% rename from test/Incremental/Dependencies/private-protocol-conformer-ext-fine.swift rename to test/Incremental/Dependencies/protocol-conformer-ext-fine.swift diff --git a/test/Incremental/Dependencies/private-protocol-conformer-fine.swift b/test/Incremental/Dependencies/protocol-conformer-fine.swift similarity index 100% rename from test/Incremental/Dependencies/private-protocol-conformer-fine.swift rename to test/Incremental/Dependencies/protocol-conformer-fine.swift diff --git a/test/Incremental/Dependencies/private-struct-member-fine.swift b/test/Incremental/Dependencies/struct-member-fine.swift similarity index 100% rename from test/Incremental/Dependencies/private-struct-member-fine.swift rename to test/Incremental/Dependencies/struct-member-fine.swift diff --git a/test/Incremental/Dependencies/private-subscript-fine.swift b/test/Incremental/Dependencies/subscript-fine.swift similarity index 100% rename from test/Incremental/Dependencies/private-subscript-fine.swift rename to test/Incremental/Dependencies/subscript-fine.swift diff --git a/test/Incremental/Dependencies/private-typealias-fine.swift b/test/Incremental/Dependencies/typealias-fine.swift similarity index 100% rename from test/Incremental/Dependencies/private-typealias-fine.swift rename to test/Incremental/Dependencies/typealias-fine.swift diff --git a/test/Incremental/Dependencies/private-var-fine.swift b/test/Incremental/Dependencies/var-fine.swift similarity index 100% rename from test/Incremental/Dependencies/private-var-fine.swift rename to test/Incremental/Dependencies/var-fine.swift From 9e0dd2a78bac57ace12e490e6fe12045e51e0a4d Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 20 Oct 2020 16:37:16 -0700 Subject: [PATCH 593/745] [NFC] Remove Reference to "private" in Incremental Test --- test/Incremental/superfluous-cascade.swift | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/test/Incremental/superfluous-cascade.swift b/test/Incremental/superfluous-cascade.swift index 06d3f677d4c40..760ec1f6e655d 100644 --- a/test/Incremental/superfluous-cascade.swift +++ b/test/Incremental/superfluous-cascade.swift @@ -1,8 +1,4 @@ -// ============================================================================= -// With private dependencies -// ============================================================================= - -// Establish status quo +// Establish baseline // RUN: %empty-directory(%t) // RUN: cp %S/Inputs/superfluous-cascade/* %t @@ -20,7 +16,7 @@ // RUN: cd %t && %target-swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental main.swift definesPoint.swift usesPoint.swift usesDisplay.swift -module-name main -output-file-map ofm.json >&output4 -// RUN: %FileCheck -check-prefix=CHECK-PRIVATE-RECOMPILED %s --dump-input=always < %t/output4 +// RUN: %FileCheck -check-prefix=CHECK-RECOMPILED %s --dump-input=always < %t/output4 -// CHECK-PRIVATE-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesPoint.o <= usesPoint.swift} -// CHECK-PRIVATE-RECOMPILED-NOT: Queuing because of dependencies discovered later: {compile: usesDisplay.o <= usesDisplay.swift} +// CHECK-RECOMPILED: Queuing because of dependencies discovered later: {compile: usesPoint.o <= usesPoint.swift} +// CHECK-RECOMPILED-NOT: Queuing because of dependencies discovered later: {compile: usesDisplay.o <= usesDisplay.swift} From a3fc337d01228347a95c6bb7004d99b426ec80b5 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Tue, 20 Oct 2020 19:40:50 -0700 Subject: [PATCH 594/745] [CMake] Compile with library evolution for all Darwin platforms. In practice, SWIFT_HOST_VARIANT_SDK may be IOS or some other non-OSX Darwin platform. Fixes part of rdar://70156840. --- stdlib/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/CMakeLists.txt b/stdlib/CMakeLists.txt index 5f8202b406ab2..8616ffc2f874c 100644 --- a/stdlib/CMakeLists.txt +++ b/stdlib/CMakeLists.txt @@ -9,7 +9,7 @@ project(swift-stdlib LANGUAGES C CXX) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") -if("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "OSX") +if("${SWIFT_HOST_VARIANT_SDK}" MATCHES "(OSX|IOS*|TVOS*|WATCHOS*)") # All Darwin platforms have ABI stability. set(SWIFT_STDLIB_STABLE_ABI_default TRUE) elseif("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "LINUX") From dd4a9f3bde5c960e9b35bb662698cfe8d5c0a730 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Tue, 20 Oct 2020 20:34:49 -0700 Subject: [PATCH 595/745] [CMake] Enable library evolution for Darwin overlay. Fixes rdar://70156840. --- cmake/modules/StandaloneOverlay.cmake | 10 +++++++++- stdlib/CMakeLists.txt | 11 +++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/cmake/modules/StandaloneOverlay.cmake b/cmake/modules/StandaloneOverlay.cmake index 7b02fbdf998ad..f84b10d6727af 100644 --- a/cmake/modules/StandaloneOverlay.cmake +++ b/cmake/modules/StandaloneOverlay.cmake @@ -58,9 +58,17 @@ set(SWIFT_NATIVE_CLANG_TOOLS_PATH "${TOOLCHAIN_DIR}/usr/bin" CACHE STRING set(SWIFT_NATIVE_SWIFT_TOOLS_PATH "${TOOLCHAIN_DIR}/usr/bin" CACHE STRING "Path to Swift tools that are executable on the build machine.") +# NOTE: The initialization in stdlib/CMakeLists.txt will be bypassed if we +# directly invoke CMake for this directory, so we initialize the variables +# related to library evolution here as well. + +option(SWIFT_STDLIB_STABLE_ABI + "Should stdlib be built with stable ABI (library evolution, resilience)." + TRUE) + option(SWIFT_ENABLE_MODULE_INTERFACES "Generate .swiftinterface files alongside .swiftmodule files." - TRUE) + "${SWIFT_STDLIB_STABLE_ABI}") set(SWIFT_STDLIB_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "Build type for the Swift standard library and SDK overlays.") diff --git a/stdlib/CMakeLists.txt b/stdlib/CMakeLists.txt index 8616ffc2f874c..f0541b549149b 100644 --- a/stdlib/CMakeLists.txt +++ b/stdlib/CMakeLists.txt @@ -42,10 +42,17 @@ endif() # User-configurable options for the standard library. # +# NOTE: Some of these variables are also initialized in StandaloneOverlay.cmake +# so that interfaces are emitted when overlays are separately built. + option(SWIFT_STDLIB_STABLE_ABI "Should stdlib be built with stable ABI (library evolution, resilience)." "${SWIFT_STDLIB_STABLE_ABI_default}") +option(SWIFT_ENABLE_MODULE_INTERFACES + "Generate .swiftinterface files alongside .swiftmodule files" + "${SWIFT_STDLIB_STABLE_ABI}") + option(SWIFT_ENABLE_COMPATIBILITY_OVERRIDES "Support back-deploying compatibility fixes for newer apps running on older runtimes." TRUE) @@ -62,10 +69,6 @@ option(SWIFT_STDLIB_OS_VERSIONING "Build stdlib with availability based on OS versions (Darwin only)." TRUE) -option(SWIFT_ENABLE_MODULE_INTERFACES - "Generate .swiftinterface files alongside .swiftmodule files" - "${SWIFT_STDLIB_STABLE_ABI}") - option(SWIFT_COMPILE_DIFFERENTIATION_WITHOUT_TGMATH "Build Differentation without tgmath (and dependency on platform runtime libraries)" FALSE) From 548b96a9ff48861a99d7251648f48e3eb8d90d25 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 20 Oct 2020 16:33:32 -0400 Subject: [PATCH 596/745] Sema: Add documentation comments for ExportContext --- lib/Sema/TypeCheckAvailability.h | 58 ++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index 3a7747a866073..f64dfa168cc44 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -34,6 +34,7 @@ namespace swift { class Type; class TypeRepr; class ValueDecl; + enum class DeclAvailabilityFlag : uint8_t { /// Do not diagnose uses of protocols in versions before they were introduced. /// Used when type-checking protocol conformances, since conforming to a @@ -68,6 +69,29 @@ enum class ExportabilityReason : unsigned { ExtensionWithConditionalConformances }; +/// A description of the restrictions on what declarations can be referenced +/// from a the signature or body of a declaration. +/// +/// We say a declaration is "exported" if it is `public` or +/// `@usableFromInline`, not `_@spi`, and not visible via an +/// `@_implementationOnly` import. +/// +/// The "signature" of a declaration is the set of all types written in the +/// declaration (such as function parameter and return types), but not +/// including the function body. +/// +/// The signature of an exported declaration can only reference other +/// exported types. +/// +/// The body of an inlinable function can only reference other `public` and +/// `@usableFromInline` declarations; furthermore, if the inlinable +/// function is also exported, its body is restricted to referencing other +/// exported declarations. +/// +/// The ExportContext also stores if the location in the program is inside +/// of a function or type body with deprecated or unavailable availability. +/// This allows referencing other deprecated and unavailable declarations, +/// without producing a warning or error, respectively. class ExportContext { DeclContext *DC; FragileFunctionKind FragileKind; @@ -78,20 +102,54 @@ class ExportContext { ExportContext(DeclContext *DC, FragileFunctionKind kind, bool spi, bool exported); public: + + /// Create an instance describing the types that can be referenced from the + /// given declaration's signature. + /// + /// If the declaration is exported, the resulting context is restricted to + /// referencing exported types only. Otherwise it can reference anything. static ExportContext forDeclSignature(Decl *D); + + /// Create an instance describing the declarations that can be referenced + /// from the given function's body. + /// + /// If the function is inlinable, the resulting context is restricted to + /// referencing ABI-public declarations only. Furthermore, if the function + /// is exported, referenced declarations must also be exported. Otherwise + /// it can reference anything. static ExportContext forFunctionBody(DeclContext *DC); + /// Produce a new context with the same properties as this one, except + /// changing the ExportabilityReason. This only affects diagnostics. ExportContext forReason(ExportabilityReason reason) const; + + /// Produce a new context with the same properties as this one, except + /// that if 'exported' is false, the resulting context can reference + /// declarations that are not exported. If 'exported' is true, the + /// resulting context is indentical to this one. + /// + /// That is, this will perform a 'bitwise and' on the 'exported' bit. ExportContext forExported(bool exported) const; DeclContext *getDeclContext() const { return DC; } + + /// If not 'None', the context has the inlinable function body restriction. FragileFunctionKind getFragileFunctionKind() const { return FragileKind; } + /// If true, the context is SPI and can reference SPI declarations. bool isSPI() const { return SPI; } + + /// If true, the context is exported and cannot reference SPI declarations + /// or declarations from `@_implementationOnly` imports. bool isExported() const { return Exported; } + /// If true, the context can only reference exported declarations, either + /// because it is the signature context of an exported declaration, or + /// because it is the function body context of an inlinable function. bool mustOnlyReferenceExportedDecls() const; + /// Get the ExportabilityReason for diagnostics. If this is 'None', there + /// are no restrictions on referencing unexported declarations. Optional getExportabilityReason() const; }; From d579b697f6c104cf4f7469ca6b0081193c6e2347 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 20 Oct 2020 18:30:00 -0400 Subject: [PATCH 597/745] Sema: Teach ExportContext to compute whether we're inside a deprecated declaration --- lib/Sema/TypeCheckAccess.cpp | 10 +- lib/Sema/TypeCheckAvailability.cpp | 111 ++++++++++++++---- lib/Sema/TypeCheckAvailability.h | 26 +++- lib/Sema/TypeChecker.h | 2 +- test/Sema/availability_deprecated.swift | 42 +++++++ .../availability_deprecated_script_mode.swift | 44 +++++++ 6 files changed, 205 insertions(+), 30 deletions(-) create mode 100644 test/Sema/availability_deprecated.swift create mode 100644 test/Sema/availability_deprecated_script_mode.swift diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 447099f037185..52e4551f0eca9 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -1513,7 +1513,7 @@ class DeclAvailabilityChecker : public DeclVisitor { loc = varDecl->getNameLoc(); diagnoseTypeAvailability(typeRepr, type, loc, - Where.forReason(reason), flags); + Where.withReason(reason), flags); } void checkGenericParams(const GenericContext *ownerCtx, @@ -1751,14 +1751,14 @@ class DeclAvailabilityChecker : public DeclVisitor { return isExported(valueMember); }); - Where = wasWhere.forExported(hasExportedMembers); - checkType(ED->getExtendedType(), ED->getExtendedTypeRepr(), ED, + Where = wasWhere.withExported(hasExportedMembers); + checkType(ED->getExtendedType(), ED->getExtendedTypeRepr(), ED, ExportabilityReason::ExtensionWithPublicMembers); // 3) If the extension contains exported members or defines conformances, // the 'where' clause must only name exported types. - Where = wasWhere.forExported(hasExportedMembers || - !ED->getInherited().empty()); + Where = wasWhere.withExported(hasExportedMembers || + !ED->getInherited().empty()); checkConstrainedExtensionRequirements(ED, hasExportedMembers); } diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 4fb4bbe298b64..1f4acb70c5a2d 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -38,10 +38,11 @@ using namespace swift; ExportContext::ExportContext(DeclContext *DC, FragileFunctionKind kind, - bool spi, bool exported) + bool spi, bool exported, bool deprecated) : DC(DC), FragileKind(kind) { SPI = spi; Exported = exported; + Deprecated = deprecated; Reason = ExportabilityReason::General; } @@ -95,7 +96,73 @@ bool swift::isExported(const Decl *D) { return true; } +template +static void forEachOuterDecl(DeclContext *DC, Fn fn) { + for (; !DC->isModuleScopeContext(); DC = DC->getParent()) { + switch (DC->getContextKind()) { + case DeclContextKind::AbstractClosureExpr: + case DeclContextKind::TopLevelCodeDecl: + case DeclContextKind::SerializedLocal: + case DeclContextKind::Module: + case DeclContextKind::FileUnit: + break; + + case DeclContextKind::Initializer: + if (auto *PBI = dyn_cast(DC)) + fn(PBI->getBinding()); + break; + + case DeclContextKind::SubscriptDecl: + fn(cast(DC)); + break; + + case DeclContextKind::EnumElementDecl: + fn(cast(DC)); + break; + + case DeclContextKind::AbstractFunctionDecl: + fn(cast(DC)); + + if (auto *AD = dyn_cast(DC)) + fn(AD->getStorage()); + break; + + case DeclContextKind::GenericTypeDecl: + fn(cast(DC)); + break; + + case DeclContextKind::ExtensionDecl: + fn(cast(DC)); + break; + } + } +} + +static void computeExportContextBits(Decl *D, + bool *implicit, bool *deprecated) { + if (D->isImplicit()) + *implicit = true; + + if (D->getAttrs().getDeprecated(D->getASTContext())) + *deprecated = true; + + if (auto *PBD = dyn_cast(D)) { + for (unsigned i = 0, e = PBD->getNumPatternEntries(); i < e; ++i) { + if (auto *VD = PBD->getAnchoringVarDecl(i)) + computeExportContextBits(VD, implicit, deprecated); + } + } +} + ExportContext ExportContext::forDeclSignature(Decl *D) { + bool implicit = false; + bool deprecated = false; + computeExportContextBits(D, &implicit, &deprecated); + forEachOuterDecl(D->getDeclContext(), + [&](Decl *D) { + computeExportContextBits(D, &implicit, &deprecated); + }); + auto *DC = D->getInnermostDeclContext(); auto fragileKind = DC->getFragileFunctionKind(); @@ -113,11 +180,19 @@ ExportContext ExportContext::forDeclSignature(Decl *D) { bool exported = ::isExported(D); - return ExportContext(DC, fragileKind, spi, exported); + return ExportContext(DC, fragileKind, spi, exported, deprecated); } ExportContext ExportContext::forFunctionBody(DeclContext *DC) { - ; + assert(DC && "Use ExportContext::forImplicit() for implicit decls"); + + bool implicit = false; + bool deprecated = false; + forEachOuterDecl(DC, + [&](Decl *D) { + computeExportContextBits(D, &implicit, &deprecated); + }); + auto fragileKind = DC->getFragileFunctionKind(); bool spi = false; @@ -129,16 +204,21 @@ ExportContext ExportContext::forFunctionBody(DeclContext *DC) { assert(fragileKind.kind == FragileFunctionKind::None); } - return ExportContext(DC, fragileKind, spi, exported); + return ExportContext(DC, fragileKind, spi, exported, deprecated); +} + +ExportContext ExportContext::forImplicit() { + return ExportContext(nullptr, FragileFunctionKind(), + false, false, false); } -ExportContext ExportContext::forReason(ExportabilityReason reason) const { +ExportContext ExportContext::withReason(ExportabilityReason reason) const { auto copy = *this; copy.Reason = reason; return copy; } -ExportContext ExportContext::forExported(bool exported) const { +ExportContext ExportContext::withExported(bool exported) const { auto copy = *this; copy.Exported = isExported() && exported; return copy; @@ -1672,17 +1752,6 @@ static bool isInsideCompatibleUnavailableDeclaration( return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, IsUnavailable); } -/// Returns true if the reference is lexically contained in a declaration -/// that is deprecated on all deployment targets. -static bool isInsideDeprecatedDeclaration(SourceRange ReferenceRange, - const DeclContext *ReferenceDC){ - auto IsDeprecated = [](const Decl *D) { - return D->getAttrs().getDeprecated(D->getASTContext()); - }; - - return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, IsDeprecated); -} - static void fixItAvailableAttrRename(InFlightDiagnostic &diag, SourceRange referenceRange, const ValueDecl *renamedDecl, @@ -2059,13 +2128,15 @@ getAccessorKindAndNameForDiagnostics(const ValueDecl *D) { } void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange, - const DeclContext *ReferenceDC, + ExportContext Where, const ValueDecl *DeprecatedDecl, const ApplyExpr *Call) { const AvailableAttr *Attr = TypeChecker::getDeprecated(DeprecatedDecl); if (!Attr) return; + auto *ReferenceDC = Where.getDeclContext(); + // We don't want deprecated declarations to trigger warnings // in synthesized code. if (ReferenceRange.isInvalid() && @@ -2075,7 +2146,7 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange, // We match the behavior of clang to not report deprecation warnings // inside declarations that are themselves deprecated on all deployment // targets. - if (isInsideDeprecatedDeclaration(ReferenceRange, ReferenceDC)) { + if (Where.isDeprecated()) { return; } @@ -2788,7 +2859,7 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, // Diagnose for deprecation if (!isAccessorWithDeprecatedStorage) - TypeChecker::diagnoseIfDeprecated(R, DC, D, call); + TypeChecker::diagnoseIfDeprecated(R, Where, D, call); if (Flags.contains(DeclAvailabilityFlag::AllowPotentiallyUnavailable)) return false; diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index f64dfa168cc44..79f0c9f1d4f76 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -97,9 +97,11 @@ class ExportContext { FragileFunctionKind FragileKind; unsigned SPI : 1; unsigned Exported : 1; + unsigned Deprecated : 1; ExportabilityReason Reason; - ExportContext(DeclContext *DC, FragileFunctionKind kind, bool spi, bool exported); + ExportContext(DeclContext *DC, FragileFunctionKind kind, + bool spi, bool exported, bool deprecated); public: @@ -119,9 +121,14 @@ class ExportContext { /// it can reference anything. static ExportContext forFunctionBody(DeclContext *DC); + /// Produce a new context describing an implicit declaration. Implicit code + /// does not have source locations. We simply trust the compiler synthesizes + /// implicit declarations correctly, and skip the checks. + static ExportContext forImplicit(); + /// Produce a new context with the same properties as this one, except /// changing the ExportabilityReason. This only affects diagnostics. - ExportContext forReason(ExportabilityReason reason) const; + ExportContext withReason(ExportabilityReason reason) const; /// Produce a new context with the same properties as this one, except /// that if 'exported' is false, the resulting context can reference @@ -129,13 +136,20 @@ class ExportContext { /// resulting context is indentical to this one. /// /// That is, this will perform a 'bitwise and' on the 'exported' bit. - ExportContext forExported(bool exported) const; + ExportContext withExported(bool exported) const; - DeclContext *getDeclContext() const { return DC; } + DeclContext *getDeclContext() const { + assert(DC != nullptr && "This is an implicit context"); + return DC; + } /// If not 'None', the context has the inlinable function body restriction. FragileFunctionKind getFragileFunctionKind() const { return FragileKind; } + /// If true, the context is part of a synthesized declaration, and + /// availability checking should be disabled. + bool isImplicit() const { return DC == nullptr; } + /// If true, the context is SPI and can reference SPI declarations. bool isSPI() const { return SPI; } @@ -143,6 +157,10 @@ class ExportContext { /// or declarations from `@_implementationOnly` imports. bool isExported() const { return Exported; } + /// If true, the context is part of a deprecated declaration and can + /// reference other deprecated declarations without warning. + bool isDeprecated() const { return Deprecated; } + /// If true, the context can only reference exported declarations, either /// because it is the signature context of an exported declaration, or /// because it is the function body context of an inlinable function. diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 551f382d256e1..03610a78af375 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1047,7 +1047,7 @@ const AvailableAttr *getDeprecated(const Decl *D); /// Callers can provide a lambda that adds additional information (such as a /// fixit hint) to the deprecation diagnostic, if it is emitted. void diagnoseIfDeprecated(SourceRange SourceRange, - const DeclContext *ReferenceDC, + ExportContext Where, const ValueDecl *DeprecatedDecl, const ApplyExpr *Call); /// @} diff --git a/test/Sema/availability_deprecated.swift b/test/Sema/availability_deprecated.swift new file mode 100644 index 0000000000000..ab26c23f912f2 --- /dev/null +++ b/test/Sema/availability_deprecated.swift @@ -0,0 +1,42 @@ +// RUN: %target-typecheck-verify-swift -parse-as-library + +struct DummyType {} + +@available(*, deprecated, renamed: "&-") +func -(x: DummyType, y: DummyType) {} + +// We don't warn if a deprecated declaration is referenced from +// within another deprecated declaration. + +@available(*, deprecated) +func testDeprecatedReferencingDeprecated1(x: DummyType, y: DummyType) { + x - y // no-warning +} + +@available(*, deprecated) +var testDeprecatedReferencingDeprecated2: () { + let x = DummyType() + let y = DummyType() + x - y // no-warning +} + +// FIXME: This doesn't work because the file is parsed in script mode. +@available(*, deprecated) +var testDeprecatedReferencingDeprecated3: () = DummyType() - DummyType() // no-warning + +struct HasDeprecatedMembers { + @available(*, deprecated) + func testDeprecatedReferencingDeprecated1(x: DummyType, y: DummyType) { + x - y // no-warning + } + + @available(*, deprecated) + var testDeprecatedReferencingDeprecated2: () { + let x = DummyType() + let y = DummyType() + x - y // no-warning + } + + @available(*, deprecated) + var testDeprecatedReferencingDeprecated3: () = DummyType() - DummyType() // no-warning +} \ No newline at end of file diff --git a/test/Sema/availability_deprecated_script_mode.swift b/test/Sema/availability_deprecated_script_mode.swift new file mode 100644 index 0000000000000..66d3c0e31b07d --- /dev/null +++ b/test/Sema/availability_deprecated_script_mode.swift @@ -0,0 +1,44 @@ +// RUN: %target-typecheck-verify-swift + +// We don't warn if a deprecated declaration is referenced from +// within another deprecated declaration. + +struct DummyType {} + +@available(*, deprecated, renamed: "&-") +func -(x: DummyType, y: DummyType) {} + +@available(*, deprecated) +func testDeprecatedReferencingDeprecated1(x: DummyType, y: DummyType) { + x - y // no-warning +} + +@available(*, deprecated) +var testDeprecatedReferencingDeprecated2: () { + let x = DummyType() + let y = DummyType() + x - y // no-warning +} + +// FIXME: This doesn't work because the file is parsed in script mode. +@available(*, deprecated) +var testDeprecatedReferencingDeprecated3: () = DummyType() - DummyType() +// expected-warning@-1 {{'-' is deprecated}} +// expected-note@-2 {{use '&-' instead}} + +struct HasDeprecatedMembers { + @available(*, deprecated) + func testDeprecatedReferencingDeprecated1(x: DummyType, y: DummyType) { + x - y // no-warning + } + + @available(*, deprecated) + var testDeprecatedReferencingDeprecated2: () { + let x = DummyType() + let y = DummyType() + x - y // no-warning + } + + @available(*, deprecated) + var testDeprecatedReferencingDeprecated3: () = DummyType() - DummyType() // no-warning +} \ No newline at end of file From 78d2cf2ee945031e0113056d4ea221b3ce772ccb Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 20 Oct 2020 19:51:04 -0400 Subject: [PATCH 598/745] Sema: Teach ExportContext to compute whether we're inside an implicit declaration --- lib/Sema/TypeCheckAccess.cpp | 13 ++- lib/Sema/TypeCheckAttr.cpp | 4 + lib/Sema/TypeCheckAvailability.cpp | 104 ++++-------------- lib/Sema/TypeCheckAvailability.h | 22 +--- .../implementation-only-import-in-decls.swift | 1 - test/Sema/spi-in-decls.swift | 1 - 6 files changed, 37 insertions(+), 108 deletions(-) diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 52e4551f0eca9..a3e6ab7c2ddef 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -1541,8 +1541,8 @@ class DeclAvailabilityChecker : public DeclVisitor { } public: - explicit DeclAvailabilityChecker(Decl *D) - : Where(ExportContext::forDeclSignature(D)) {} + explicit DeclAvailabilityChecker(ExportContext where) + : Where(where) {} // Force all kinds to be handled at a lower level. void visitDecl(Decl *D) = delete; @@ -1857,9 +1857,12 @@ void swift::checkAccessControl(Decl *D) { checkExtensionGenericParamAccess(ED); } - //ExportabilityChecker().visit(D); - if (D->isImplicit() || isa(D)) + if (isa(D)) return; - DeclAvailabilityChecker(D).visit(D); + auto where = ExportContext::forDeclSignature(D); + if (where.isImplicit()) + return; + + DeclAvailabilityChecker(where).visit(D); } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 6bc7a1e2babb5..5ed384c479415 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "MiscDiagnostics.h" +#include "TypeCheckAvailability.h" #include "TypeCheckConcurrency.h" #include "TypeCheckObjC.h" #include "TypeCheckType.h" @@ -1980,6 +1981,9 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator, mainFunction = viableCandidates[0]; } + auto where = ExportContext::forDeclSignature(D); + diagnoseDeclAvailability(mainFunction, attr->getRange(), where, None); + auto *const func = FuncDecl::createImplicit( context, StaticSpellingKind::KeywordStatic, DeclName(context, DeclBaseName(context.Id_MainEntryPoint), diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 1f4acb70c5a2d..c4ddd06348b8b 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -38,10 +38,12 @@ using namespace swift; ExportContext::ExportContext(DeclContext *DC, FragileFunctionKind kind, - bool spi, bool exported, bool deprecated) + bool spi, bool exported, bool implicit, + bool deprecated) : DC(DC), FragileKind(kind) { SPI = spi; Exported = exported; + Implicit = implicit; Deprecated = deprecated; Reason = ExportabilityReason::General; } @@ -180,12 +182,10 @@ ExportContext ExportContext::forDeclSignature(Decl *D) { bool exported = ::isExported(D); - return ExportContext(DC, fragileKind, spi, exported, deprecated); + return ExportContext(DC, fragileKind, spi, exported, implicit, deprecated); } ExportContext ExportContext::forFunctionBody(DeclContext *DC) { - assert(DC && "Use ExportContext::forImplicit() for implicit decls"); - bool implicit = false; bool deprecated = false; forEachOuterDecl(DC, @@ -204,12 +204,7 @@ ExportContext ExportContext::forFunctionBody(DeclContext *DC) { assert(fragileKind.kind == FragileFunctionKind::None); } - return ExportContext(DC, fragileKind, spi, exported, deprecated); -} - -ExportContext ExportContext::forImplicit() { - return ExportContext(nullptr, FragileFunctionKind(), - false, false, false); + return ExportContext(DC, fragileKind, spi, exported, implicit, deprecated); } ExportContext ExportContext::withReason(ExportabilityReason reason) const { @@ -1700,29 +1695,6 @@ someEnclosingDeclMatches(SourceRange ReferenceRange, return Pred(abstractSyntaxDeclForAvailableAttribute(DeclToSearch)); } -/// Returns true if the reference or any of its parents is an -/// implicit function. -static bool isInsideImplicitFunction(SourceRange ReferenceRange, - const DeclContext *DC) { - auto IsInsideImplicitFunc = [](const Decl *D) { - auto *AFD = dyn_cast(D); - return AFD && AFD->isImplicit(); - }; - - return someEnclosingDeclMatches(ReferenceRange, DC, IsInsideImplicitFunc); -} - -/// Returns true if the reference or any of its parents is an -/// unavailable (or obsoleted) declaration. -static bool isInsideUnavailableDeclaration(SourceRange ReferenceRange, - const DeclContext *ReferenceDC) { - auto IsUnavailable = [](const Decl *D) { - return D->getAttrs().getUnavailable(D->getASTContext()); - }; - - return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, IsUnavailable); -} - /// Returns true if the reference or any of its parents is an /// unconditional unavailable declaration for the same platform. static bool isInsideCompatibleUnavailableDeclaration( @@ -2135,14 +2107,6 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange, if (!Attr) return; - auto *ReferenceDC = Where.getDeclContext(); - - // We don't want deprecated declarations to trigger warnings - // in synthesized code. - if (ReferenceRange.isInvalid() && - isInsideImplicitFunction(ReferenceRange, ReferenceDC)) { - return; - } // We match the behavior of clang to not report deprecation warnings // inside declarations that are themselves deprecated on all deployment // targets. @@ -2150,6 +2114,7 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange, return; } + auto *ReferenceDC = Where.getDeclContext(); auto &Context = ReferenceDC->getASTContext(); if (!Context.LangOpts.DisableAvailabilityChecking) { AvailabilityContext RunningOSVersions = @@ -2351,16 +2316,6 @@ bool swift::diagnoseExplicitUnavailability( if (!Attr) return false; - // Suppress the diagnostic if we are in synthesized code inside - // a synthesized function and the reference is lexically - // contained in a declaration that is itself marked unavailable. - // The right thing to do here is to not synthesize that code in the - // first place. rdar://problem/20491640 - if (R.isInvalid() && isInsideImplicitFunction(R, DC) && - isInsideUnavailableDeclaration(R, DC)) { - return false; - } - // Calling unavailable code from within code with the same // unavailability is OK -- the eventual caller can't call the // enclosing code in the same situations it wouldn't be able to @@ -2512,20 +2467,6 @@ class AvailabilityWalker : public ASTWalker { SmallVector ExprStack; ExportContext Where; - /// Returns true if DC is an \c init(rawValue:) declaration and it is marked - /// implicit. - bool inSynthesizedInitRawValue() { - auto *DC = Where.getDeclContext(); - auto init = dyn_cast_or_null( - DC->getInnermostDeclarationDeclContext()); - - return init && - init->isImplicit() && - init->getParameters()->size() == 1 && - init->getParameters()->get(0)->getArgumentName() == - Context.Id_rawValue; - } - public: AvailabilityWalker(ExportContext Where) : Context(Where.getDeclContext()->getASTContext()), Where(Where) {} @@ -2548,20 +2489,8 @@ class AvailabilityWalker : public ASTWalker { }; if (auto DR = dyn_cast(E)) { - DeclAvailabilityFlags flags = None; - if (inSynthesizedInitRawValue()) - // HACK: If a raw-value enum has cases with `@available(introduced:)` - // attributes, the auto-derived `init(rawValue:)` will contain - // DeclRefExprs which reference those cases. It will also contain - // appropriate `guard #available` statements to keep them from running - // on older versions, but the availability checker can't verify that - // because the synthesized code uses invalid SourceLocs. Don't diagnose - // these errors; instead, take it on faith that - // DerivedConformanceRawRepresentable will do the right thing. - flags |= DeclAvailabilityFlag::AllowPotentiallyUnavailable; - diagAvailability(DR->getDeclRef(), DR->getSourceRange(), - getEnclosingApplyExpr(), flags); + getEnclosingApplyExpr(), None); maybeDiagStorageAccess(DR->getDecl(), DR->getSourceRange(), DC); } if (auto MR = dyn_cast(E)) { @@ -2861,9 +2790,6 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, if (!isAccessorWithDeprecatedStorage) TypeChecker::diagnoseIfDeprecated(R, Where, D, call); - if (Flags.contains(DeclAvailabilityFlag::AllowPotentiallyUnavailable)) - return false; - if (Flags.contains(DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol) && isa(D)) return false; @@ -3041,7 +2967,10 @@ AvailabilityWalker::diagnoseMemoryLayoutMigration(const ValueDecl *D, /// Diagnose uses of unavailable declarations. void swift::diagAvailability(const Expr *E, DeclContext *DC) { - AvailabilityWalker walker(ExportContext::forFunctionBody(DC)); + auto where = ExportContext::forFunctionBody(DC); + if (where.isImplicit()) + return; + AvailabilityWalker walker(where); const_cast(E)->walk(walker); } @@ -3051,8 +2980,8 @@ class StmtAvailabilityWalker : public ASTWalker { ExportContext Where; public: - explicit StmtAvailabilityWalker(DeclContext *DC) - : Where(ExportContext::forFunctionBody(DC)) {} + explicit StmtAvailabilityWalker(ExportContext where) + : Where(where) {} /// We'll visit the expression from performSyntacticExprDiagnostics(). std::pair walkToExprPre(Expr *E) override { @@ -3079,7 +3008,11 @@ void swift::diagAvailability(const Stmt *S, DeclContext *DC) { if (isa(S)) return; - StmtAvailabilityWalker walker(DC); + auto where = ExportContext::forFunctionBody(DC); + if (where.isImplicit()) + return; + + StmtAvailabilityWalker walker(where); const_cast(S)->walk(walker); } @@ -3281,6 +3214,7 @@ bool swift::diagnoseDeclAvailability(const ValueDecl *Decl, ExportContext Where, DeclAvailabilityFlags Flags) { + assert(!Where.isImplicit()); AvailabilityWalker AW(Where); return AW.diagAvailability(const_cast(Decl), R, nullptr, Flags); } diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index 79f0c9f1d4f76..021900c7aa38d 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -49,13 +49,9 @@ enum class DeclAvailabilityFlag : uint8_t { /// is inout and both the getter and setter must be available. ForInout = 1 << 2, - /// Do not diagnose uses of declarations in versions before they were - /// introduced. Used to work around availability-checker bugs. - AllowPotentiallyUnavailable = 1 << 3, - /// If an error diagnostic would normally be emitted, demote the error to a /// warning. Used for ObjC key path components. - ForObjCKeyPath = 1 << 4 + ForObjCKeyPath = 1 << 3 }; using DeclAvailabilityFlags = OptionSet; @@ -98,10 +94,12 @@ class ExportContext { unsigned SPI : 1; unsigned Exported : 1; unsigned Deprecated : 1; + unsigned Implicit : 1; ExportabilityReason Reason; ExportContext(DeclContext *DC, FragileFunctionKind kind, - bool spi, bool exported, bool deprecated); + bool spi, bool exported, bool implicit, + bool deprecated); public: @@ -121,11 +119,6 @@ class ExportContext { /// it can reference anything. static ExportContext forFunctionBody(DeclContext *DC); - /// Produce a new context describing an implicit declaration. Implicit code - /// does not have source locations. We simply trust the compiler synthesizes - /// implicit declarations correctly, and skip the checks. - static ExportContext forImplicit(); - /// Produce a new context with the same properties as this one, except /// changing the ExportabilityReason. This only affects diagnostics. ExportContext withReason(ExportabilityReason reason) const; @@ -138,17 +131,14 @@ class ExportContext { /// That is, this will perform a 'bitwise and' on the 'exported' bit. ExportContext withExported(bool exported) const; - DeclContext *getDeclContext() const { - assert(DC != nullptr && "This is an implicit context"); - return DC; - } + DeclContext *getDeclContext() const { return DC; } /// If not 'None', the context has the inlinable function body restriction. FragileFunctionKind getFragileFunctionKind() const { return FragileKind; } /// If true, the context is part of a synthesized declaration, and /// availability checking should be disabled. - bool isImplicit() const { return DC == nullptr; } + bool isImplicit() const { return Implicit; } /// If true, the context is SPI and can reference SPI declarations. bool isSPI() const { return SPI; } diff --git a/test/Sema/implementation-only-import-in-decls.swift b/test/Sema/implementation-only-import-in-decls.swift index 818a62facc47c..0423ad246d4ca 100644 --- a/test/Sema/implementation-only-import-in-decls.swift +++ b/test/Sema/implementation-only-import-in-decls.swift @@ -73,7 +73,6 @@ public protocol TestAssocTypeWhereClause { public enum TestRawType: IntLike { // expected-error {{cannot use struct 'IntLike' here; 'BADLibrary' has been imported as implementation-only}} case x = 1 - // FIXME: expected-error@-1 {{cannot use conformance of 'IntLike' to 'Equatable' here; 'BADLibrary' has been imported as implementation-only}} } public class TestSubclass: BadClass { // expected-error {{cannot use class 'BadClass' here; 'BADLibrary' has been imported as implementation-only}} diff --git a/test/Sema/spi-in-decls.swift b/test/Sema/spi-in-decls.swift index e3fd458bf86fd..2d1e655d168d8 100644 --- a/test/Sema/spi-in-decls.swift +++ b/test/Sema/spi-in-decls.swift @@ -107,7 +107,6 @@ public protocol TestAssocTypeWhereClause { public enum TestRawType: IntLike { // expected-error {{cannot use struct 'IntLike' here; it is SPI}} case x = 1 - // FIXME: expected-error@-1 {{cannot use conformance of 'IntLike' to 'Equatable' here; the conformance is declared as SPI}} } public class TestSubclass: BadClass { // expected-error {{cannot use class 'BadClass' here; it is SPI}} From 85d24953de5c14eb83e4c8492bacd2eef4869986 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 20 Oct 2020 23:30:49 -0400 Subject: [PATCH 599/745] Sema: Teach ExportContext to compute whether we're inside an unavailable declaration --- include/swift/AST/DeclContext.h | 1 + lib/Sema/TypeCheckAvailability.cpp | 148 ++++++++++++----------------- lib/Sema/TypeCheckAvailability.h | 14 ++- lib/Sema/TypeCheckPattern.cpp | 3 +- lib/Sema/TypeCheckStorage.cpp | 3 +- 5 files changed, 75 insertions(+), 94 deletions(-) diff --git a/include/swift/AST/DeclContext.h b/include/swift/AST/DeclContext.h index 904a292653f92..697e6e7858588 100644 --- a/include/swift/AST/DeclContext.h +++ b/include/swift/AST/DeclContext.h @@ -57,6 +57,7 @@ namespace swift { class GenericTypeParamType; class InfixOperatorDecl; class InfixOperatorLookupResult; + enum class PlatformKind: uint8_t; class PrecedenceGroupDecl; class ProtocolDecl; class Requirement; diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index c4ddd06348b8b..e9ae5c693301c 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -38,14 +38,22 @@ using namespace swift; ExportContext::ExportContext(DeclContext *DC, FragileFunctionKind kind, - bool spi, bool exported, bool implicit, - bool deprecated) + bool spi, bool exported, bool implicit, bool deprecated, + Optional unavailablePlatformKind) : DC(DC), FragileKind(kind) { SPI = spi; Exported = exported; Implicit = implicit; Deprecated = deprecated; - Reason = ExportabilityReason::General; + if (unavailablePlatformKind) { + Unavailable = 1; + Platform = unsigned(*unavailablePlatformKind); + } else { + Unavailable = 0; + Platform = 0; + } + + Reason = unsigned(ExportabilityReason::General); } bool swift::isExported(const ValueDecl *VD) { @@ -141,17 +149,25 @@ static void forEachOuterDecl(DeclContext *DC, Fn fn) { } static void computeExportContextBits(Decl *D, - bool *implicit, bool *deprecated) { + bool *implicit, bool *deprecated, + Optional *unavailablePlatformKind) { if (D->isImplicit()) *implicit = true; - if (D->getAttrs().getDeprecated(D->getASTContext())) + auto &Ctx = D->getASTContext(); + + if (D->getAttrs().getDeprecated(Ctx)) *deprecated = true; + if (auto *A = D->getAttrs().getUnavailable(Ctx)) { + *unavailablePlatformKind = A->Platform; + } + if (auto *PBD = dyn_cast(D)) { for (unsigned i = 0, e = PBD->getNumPatternEntries(); i < e; ++i) { if (auto *VD = PBD->getAnchoringVarDecl(i)) - computeExportContextBits(VD, implicit, deprecated); + computeExportContextBits(VD, implicit, deprecated, + unavailablePlatformKind); } } } @@ -159,10 +175,13 @@ static void computeExportContextBits(Decl *D, ExportContext ExportContext::forDeclSignature(Decl *D) { bool implicit = false; bool deprecated = false; - computeExportContextBits(D, &implicit, &deprecated); + Optional unavailablePlatformKind; + computeExportContextBits(D, &implicit, &deprecated, + &unavailablePlatformKind); forEachOuterDecl(D->getDeclContext(), [&](Decl *D) { - computeExportContextBits(D, &implicit, &deprecated); + computeExportContextBits(D, &implicit, &deprecated, + &unavailablePlatformKind); }); auto *DC = D->getInnermostDeclContext(); @@ -182,15 +201,18 @@ ExportContext ExportContext::forDeclSignature(Decl *D) { bool exported = ::isExported(D); - return ExportContext(DC, fragileKind, spi, exported, implicit, deprecated); + return ExportContext(DC, fragileKind, spi, exported, implicit, deprecated, + unavailablePlatformKind); } ExportContext ExportContext::forFunctionBody(DeclContext *DC) { bool implicit = false; bool deprecated = false; + Optional unavailablePlatformKind; forEachOuterDecl(DC, [&](Decl *D) { - computeExportContextBits(D, &implicit, &deprecated); + computeExportContextBits(D, &implicit, &deprecated, + &unavailablePlatformKind); }); auto fragileKind = DC->getFragileFunctionKind(); @@ -204,12 +226,13 @@ ExportContext ExportContext::forFunctionBody(DeclContext *DC) { assert(fragileKind.kind == FragileFunctionKind::None); } - return ExportContext(DC, fragileKind, spi, exported, implicit, deprecated); + return ExportContext(DC, fragileKind, spi, exported, implicit, deprecated, + unavailablePlatformKind); } ExportContext ExportContext::withReason(ExportabilityReason reason) const { auto copy = *this; - copy.Reason = reason; + copy.Reason = unsigned(reason); return copy; } @@ -219,13 +242,19 @@ ExportContext ExportContext::withExported(bool exported) const { return copy; } +Optional ExportContext::getUnavailablePlatformKind() const { + if (Unavailable) + return PlatformKind(Platform); + return None; +} + bool ExportContext::mustOnlyReferenceExportedDecls() const { return Exported || FragileKind.kind != FragileFunctionKind::None; } Optional ExportContext::getExportabilityReason() const { if (Exported) - return Reason; + return ExportabilityReason(Reason); return None; } @@ -1644,62 +1673,15 @@ const AvailableAttr *TypeChecker::getDeprecated(const Decl *D) { return nullptr; } -/// Returns true if some declaration lexically enclosing the reference -/// matches the passed in predicate and false otherwise. -static bool -someEnclosingDeclMatches(SourceRange ReferenceRange, - const DeclContext *ReferenceDC, - llvm::function_ref Pred) { - // Climb the DeclContext hierarchy to see if any of the containing - // declarations matches the predicate. - const DeclContext *DC = ReferenceDC; - while (true) { - auto *D = DC->getInnermostDeclarationDeclContext(); - if (!D) - break; - - if (Pred(D)) { - return true; - } - - // If we are in an accessor, check to see if the associated - // property matches the predicate. - if (auto accessor = dyn_cast(D)) { - if (Pred(accessor->getStorage())) - return true; - } - - DC = D->getDeclContext(); - } - - // Search the AST starting from our innermost declaration context to see if - // if the reference is inside a property declaration but not inside an - // accessor (this can happen for the TypeRepr for the declared type of a - // property, for example). - // We can't rely on the DeclContext hierarchy climb above because properties - // do not introduce a new DeclContext. - - // Don't search for a containing declaration if we don't have a source range. - if (ReferenceRange.isInvalid()) - return false; - - ASTContext &Ctx = ReferenceDC->getASTContext(); - const Decl *DeclToSearch = - findContainingDeclaration(ReferenceRange, ReferenceDC, Ctx.SourceMgr); - - // We may not be able to find a declaration to search if the ReferenceRange - // isn't useful (i.e., we are in synthesized code). - if (!DeclToSearch) - return false; - - return Pred(abstractSyntaxDeclForAvailableAttribute(DeclToSearch)); -} - /// Returns true if the reference or any of its parents is an /// unconditional unavailable declaration for the same platform. static bool isInsideCompatibleUnavailableDeclaration( - const ValueDecl *referencedD, SourceRange ReferenceRange, - const DeclContext *ReferenceDC, const AvailableAttr *attr) { + const ValueDecl *D, ExportContext where, + const AvailableAttr *attr) { + auto referencedPlatform = where.getUnavailablePlatformKind(); + if (!referencedPlatform) + return false; + if (!attr->isUnconditionallyUnavailable()) { return false; } @@ -1708,20 +1690,13 @@ static bool isInsideCompatibleUnavailableDeclaration( // but allow the use of types. PlatformKind platform = attr->Platform; if (platform == PlatformKind::none && - !isa(referencedD)) { + !isa(D)) { return false; } - auto IsUnavailable = [platform](const Decl *D) { - auto EnclosingUnavailable = - D->getAttrs().getUnavailable(D->getASTContext()); - return EnclosingUnavailable && - (EnclosingUnavailable->Platform == platform || - inheritsAvailabilityFromPlatform(platform, - EnclosingUnavailable->Platform)); - }; - - return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, IsUnavailable); + return (*referencedPlatform == platform || + inheritsAvailabilityFromPlatform(platform, + *referencedPlatform)); } static void fixItAvailableAttrRename(InFlightDiagnostic &diag, @@ -2201,8 +2176,8 @@ void swift::diagnoseUnavailableOverride(ValueDecl *override, return; } - diagnoseExplicitUnavailability(base, override->getLoc(), - override->getDeclContext(), + ExportContext where = ExportContext::forDeclSignature(override); + diagnoseExplicitUnavailability(base, override->getLoc(), where, /*Flags*/None, [&](InFlightDiagnostic &diag) { ParsedDeclName parsedName = parseDeclName(attr->Rename); @@ -2235,10 +2210,10 @@ void swift::diagnoseUnavailableOverride(ValueDecl *override, /// marked as unavailable, either through "unavailable" or "obsoleted:". bool swift::diagnoseExplicitUnavailability(const ValueDecl *D, SourceRange R, - const DeclContext *DC, + ExportContext Where, const ApplyExpr *call, DeclAvailabilityFlags Flags) { - return diagnoseExplicitUnavailability(D, R, DC, Flags, + return diagnoseExplicitUnavailability(D, R, Where, Flags, [=](InFlightDiagnostic &diag) { fixItAvailableAttrRename(diag, R, D, AvailableAttr::isUnavailable(D), call); @@ -2309,7 +2284,7 @@ bool isSubscriptReturningString(const ValueDecl *D, ASTContext &Context) { bool swift::diagnoseExplicitUnavailability( const ValueDecl *D, SourceRange R, - const DeclContext *DC, + ExportContext Where, DeclAvailabilityFlags Flags, llvm::function_ref attachRenameFixIts) { auto *Attr = AvailableAttr::isUnavailable(D); @@ -2320,9 +2295,8 @@ bool swift::diagnoseExplicitUnavailability( // unavailability is OK -- the eventual caller can't call the // enclosing code in the same situations it wouldn't be able to // call this code. - if (isInsideCompatibleUnavailableDeclaration(D, R, DC, Attr)) { + if (isInsideCompatibleUnavailableDeclaration(D, Where, Attr)) return false; - } SourceLoc Loc = R.Start; DeclName Name; @@ -2776,9 +2750,7 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, } } - auto *DC = Where.getDeclContext(); - - if (diagnoseExplicitUnavailability(D, R, DC, call, Flags)) + if (diagnoseExplicitUnavailability(D, R, Where, call, Flags)) return true; // Make sure not to diagnose an accessor's deprecation if we already @@ -2794,6 +2766,8 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, && isa(D)) return false; + auto *DC = Where.getDeclContext(); + // Diagnose (and possibly signal) for potential unavailability auto maybeUnavail = TypeChecker::checkDeclarationAvailability(D, R.Start, DC); if (maybeUnavail.hasValue()) { diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index 021900c7aa38d..153bedf733df4 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -95,11 +95,13 @@ class ExportContext { unsigned Exported : 1; unsigned Deprecated : 1; unsigned Implicit : 1; - ExportabilityReason Reason; + unsigned Unavailable : 1; + unsigned Platform : 8; + unsigned Reason : 2; ExportContext(DeclContext *DC, FragileFunctionKind kind, - bool spi, bool exported, bool implicit, - bool deprecated); + bool spi, bool exported, bool implicit, bool deprecated, + Optional unavailablePlatformKind); public: @@ -151,6 +153,8 @@ class ExportContext { /// reference other deprecated declarations without warning. bool isDeprecated() const { return Deprecated; } + Optional getUnavailablePlatformKind() const; + /// If true, the context can only reference exported declarations, either /// because it is the signature context of an exported declaration, or /// because it is the function body context of an inlinable function. @@ -215,7 +219,7 @@ void diagnoseUnavailableOverride(ValueDecl *override, /// marked as unavailable, either through "unavailable" or "obsoleted:". bool diagnoseExplicitUnavailability(const ValueDecl *D, SourceRange R, - const DeclContext *DC, + ExportContext Where, const ApplyExpr *call, DeclAvailabilityFlags Flags = None); @@ -224,7 +228,7 @@ bool diagnoseExplicitUnavailability(const ValueDecl *D, bool diagnoseExplicitUnavailability( const ValueDecl *D, SourceRange R, - const DeclContext *DC, + ExportContext Where, DeclAvailabilityFlags Flags, llvm::function_ref attachRenameFixIts); diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 459b7df7636c8..27a6c0ae1706e 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -37,7 +37,8 @@ using namespace swift; static EnumElementDecl * extractEnumElement(DeclContext *DC, SourceLoc UseLoc, const VarDecl *constant) { - diagnoseExplicitUnavailability(constant, UseLoc, DC, nullptr); + ExportContext where = ExportContext::forFunctionBody(DC); + diagnoseExplicitUnavailability(constant, UseLoc, where, nullptr); const FuncDecl *getter = constant->getAccessor(AccessorKind::Get); if (!getter) diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index e507d3e3322a1..5194d610c16f2 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -785,10 +785,11 @@ static Expr *buildStorageReference(AccessorDecl *accessor, if (accessor->getAccessorKind() == AccessorKind::Get || accessor->getAccessorKind() == AccessorKind::Read) { if (wrappedValue->getAttrs().getUnavailable(ctx)) { + ExportContext where = ExportContext::forDeclSignature(var); diagnoseExplicitUnavailability( wrappedValue, var->getAttachedPropertyWrappers()[i]->getRangeWithAt(), - var->getDeclContext(), nullptr); + where, nullptr); } } From 83474707eea654d81bc49565f952c84cc19508c9 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Wed, 9 Sep 2020 11:08:42 -0700 Subject: [PATCH 600/745] Enable SILMem2Reg for OSSA SILMem2Reg has roughly 2 central algorithms, removal of alloc_stack with uses in a single block vs multiple blocks. While replacing loads and stores to the stack location in each of the 2 algorithms, new handling of qualifiers like [assign], [copy] and [take] which are new to OSSA are needed. Also Disable SILMem2Reg when we see this pattern: load [take] (struct_element_addr/tuple_element_addr %ASI) Convert SILMem2Reg tests into ossa And add new SILMem2Reg tests for non-trivial values. Thanks to zoecarver for additional tests. --- lib/SILOptimizer/Transforms/SILMem2Reg.cpp | 188 +++++- test/SILOptimizer/mem2reg.sil | 4 +- test/SILOptimizer/mem2reg_liveness_ossa.sil | 65 ++ test/SILOptimizer/mem2reg_ossa.sil | 489 ++++++++++++++ test/SILOptimizer/mem2reg_ossa_nontrivial.sil | 615 ++++++++++++++++++ .../mem2reg_ossa_nontrivial_casts.sil | 81 +++ test/SILOptimizer/mem2reg_resilient_ossa.sil | 28 + test/SILOptimizer/mem2reg_simple_ossa.sil | 410 ++++++++++++ .../SILOptimizer/mem2reg_unreachable_ossa.sil | 68 ++ 9 files changed, 1924 insertions(+), 24 deletions(-) create mode 100644 test/SILOptimizer/mem2reg_liveness_ossa.sil create mode 100644 test/SILOptimizer/mem2reg_ossa.sil create mode 100644 test/SILOptimizer/mem2reg_ossa_nontrivial.sil create mode 100644 test/SILOptimizer/mem2reg_ossa_nontrivial_casts.sil create mode 100644 test/SILOptimizer/mem2reg_resilient_ossa.sil create mode 100644 test/SILOptimizer/mem2reg_simple_ossa.sil create mode 100644 test/SILOptimizer/mem2reg_unreachable_ossa.sil diff --git a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp index e72abfb68a8fb..e0b43fb8e24ac 100644 --- a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp +++ b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp @@ -183,23 +183,43 @@ class MemoryToRegisters { /// Returns true if \p I is an address of a LoadInst, skipping struct and /// tuple address projections. Sets \p singleBlock to null if the load (or /// it's address is not in \p singleBlock. -static bool isAddressForLoad(SILInstruction *I, SILBasicBlock *&singleBlock) { - - if (isa(I)) +/// This function looks for these patterns: +/// 1. (load %ASI) +/// 2. (load (struct_element_addr/tuple_element_addr/unchecked_addr_cast %ASI)) +static bool isAddressForLoad(SILInstruction *I, SILBasicBlock *&singleBlock, + bool &hasGuaranteedOwnership) { + + if (isa(I)) { + // SILMem2Reg is disabled when we find: + // (load [take] (struct_element_addr/tuple_element_addr %ASI)) + // struct_element_addr and tuple_element_addr are lowered into + // struct_extract and tuple_extract and these SIL instructions have a + // guaranteed ownership. For replacing load's users, we need an owned value. + // We will need a new copy and destroy of the running val placed after the + // last use. This is not implemented currently. + if (hasGuaranteedOwnership && cast(I)->getOwnershipQualifier() == + LoadOwnershipQualifier::Take) { + return false; + } return true; + } if (!isa(I) && !isa(I) && !isa(I)) return false; - + + if (isa(I) || isa(I)) { + hasGuaranteedOwnership = true; + } + // Recursively search for other (non-)loads in the instruction's uses. for (auto UI : cast(I)->getUses()) { SILInstruction *II = UI->getUser(); if (II->getParent() != singleBlock) singleBlock = nullptr; - - if (!isAddressForLoad(II, singleBlock)) - return false; + + if (!isAddressForLoad(II, singleBlock, hasGuaranteedOwnership)) + return false; } return true; } @@ -233,7 +253,8 @@ static bool isCaptured(AllocStackInst *ASI, bool &inSingleBlock) { singleBlock = nullptr; // Loads are okay. - if (isAddressForLoad(II, singleBlock)) + bool hasGuaranteedOwnership = false; + if (isAddressForLoad(II, singleBlock, hasGuaranteedOwnership)) continue; // We can store into an AllocStack (but not the pointer). @@ -348,6 +369,8 @@ static void collectLoads(SILInstruction *I, SmallVectorImpl &Loads) static void replaceLoad(LoadInst *LI, SILValue val, AllocStackInst *ASI) { ProjectionPath projections(val->getType()); SILValue op = LI->getOperand(); + SILBuilderWithScope builder(LI); + while (op != ASI) { assert(isa(op) || isa(op) || isa(op)); @@ -355,14 +378,45 @@ static void replaceLoad(LoadInst *LI, SILValue val, AllocStackInst *ASI) { projections.push_back(Projection(Inst)); op = Inst->getOperand(0); } - SILBuilder builder(LI); + + SmallVector borrowedVals; for (auto iter = projections.rbegin(); iter != projections.rend(); ++iter) { const Projection &projection = *iter; + assert(projection.getKind() == ProjectionKind::BitwiseCast || + projection.getKind() == ProjectionKind::Struct || + projection.getKind() == ProjectionKind::Tuple); + + // struct_extract and tuple_extract expect guaranteed operand ownership + // non-trivial RunningVal is owned. Insert borrow operation to convert + if (projection.getKind() == ProjectionKind::Struct || + projection.getKind() == ProjectionKind::Tuple) { + SILValue opVal = builder.emitBeginBorrowOperation(LI->getLoc(), val); + if (opVal != val) { + borrowedVals.push_back(opVal); + val = opVal; + } + } val = projection.createObjectProjection(builder, LI->getLoc(), val).get(); } + op = LI->getOperand(); - LI->replaceAllUsesWith(val); + // Replace users of the loaded value with `val` + // If we have a load [copy], replace the users with copy_value of `val` + if (LI->getOwnershipQualifier() == LoadOwnershipQualifier::Copy) { + LI->replaceAllUsesWith(builder.createCopyValue(LI->getLoc(), val)); + } else { + assert(!ASI->getFunction()->hasOwnership() || + val.getOwnershipKind() != ValueOwnershipKind::Guaranteed); + LI->replaceAllUsesWith(val); + } + + for (auto borrowedVal : borrowedVals) { + builder.emitEndBorrowOperation(LI->getLoc(), borrowedVal); + } + + // Delete the load LI->eraseFromParent(); + while (op != ASI && op->use_empty()) { assert(isa(op) || isa(op) || isa(op)); @@ -399,6 +453,7 @@ StoreInst * StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) { LLVM_DEBUG(llvm::dbgs() << "*** Promoting ASI in block: " << *ASI); + // RunningVal is the current value in the stack location. // We don't know the value of the alloca until we find the first store. SILValue RunningVal = SILValue(); // Keep track of the last StoreInst that we found. @@ -415,12 +470,16 @@ StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) { // If we are loading from the AllocStackInst and we already know the // content of the Alloca then use it. LLVM_DEBUG(llvm::dbgs() << "*** Promoting load: " << *Load); - replaceLoad(Load, RunningVal, ASI); ++NumInstRemoved; - } else if (Load->getOperand() == ASI) { + } else if (Load->getOperand() == ASI && + Load->getOwnershipQualifier() != + LoadOwnershipQualifier::Copy) { // If we don't know the content of the AllocStack then the loaded // value *is* the new value; + // Don't use result of load [copy] as a RunningVal, it necessitates + // additional logic for cleanup of consuming instructions of the result. + // StackAllocationPromoter::fixBranchesAndUses will later handle it. LLVM_DEBUG(llvm::dbgs() << "*** First load: " << *Load); RunningVal = Load; } @@ -433,16 +492,51 @@ StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) { if (SI->getDest() != ASI) continue; - // The stored value is the new running value. - RunningVal = SI->getSrc(); + // Special handling of entry block + // If we have a store [assign] in the first block, OSSA guarantees we can + // find the previous value stored in the stack location in RunningVal. + // Create destroy_value of the RunningVal. + // For all other blocks we may not know the previous value stored in the + // stack location. So we will create destroy_value in + // StackAllocationPromoter::fixBranchesAndUses, by getting the live-in + // value to the block. + if (BB->isEntry()) { + if (SI->getOwnershipQualifier() == StoreOwnershipQualifier::Assign) { + assert(RunningVal); + SILBuilderWithScope(SI).createDestroyValue(SI->getLoc(), RunningVal); + } + } // If we met a store before this one, delete it. + // If the LastStore was a store with [assign], delete it only if we know + // the RunningValue to destroy. If not, it will be deleted in + // StackAllocationPromoter::fixBranchesAndUses. if (LastStore) { - ++NumInstRemoved; - LLVM_DEBUG(llvm::dbgs() << "*** Removing redundant store: " - << *LastStore); - LastStore->eraseFromParent(); + if (LastStore->getOwnershipQualifier() == + StoreOwnershipQualifier::Assign) { + if (RunningVal) { + // For entry block, we would have already created the destroy_value, + // skip it. + if (!BB->isEntry()) { + SILBuilderWithScope(LastStore).createDestroyValue( + LastStore->getLoc(), RunningVal); + } + LLVM_DEBUG(llvm::dbgs() + << "*** Removing redundant store: " << *LastStore); + ++NumInstRemoved; + LastStore->eraseFromParent(); + } + } else { + LLVM_DEBUG(llvm::dbgs() + << "*** Removing redundant store: " << *LastStore); + ++NumInstRemoved; + LastStore->eraseFromParent(); + } } + + // The stored value is the new running value. + RunningVal = SI->getSrc(); + // The current store is now the LastStore LastStore = SI; continue; } @@ -466,6 +560,15 @@ StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) { continue; } + if (auto *DVI = dyn_cast(Inst)) { + if (DVI->getOperand() == RunningVal) { + // Reset LastStore. + // So that we don't end up passing dead values as phi args in + // StackAllocationPromoter::fixBranchesAndUses + LastStore = nullptr; + } + } + // Stop on deallocation. if (auto *DSI = dyn_cast(Inst)) { if (DSI->getOperand() == ASI) { @@ -516,6 +619,10 @@ void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *ASI) { // value. if (auto *SI = dyn_cast(Inst)) { if (SI->getDest() == ASI) { + if (SI->getOwnershipQualifier() == StoreOwnershipQualifier::Assign) { + assert(RunningVal); + SILBuilderWithScope(SI).createDestroyValue(SI->getLoc(), RunningVal); + } RunningVal = SI->getSrc(); Inst->eraseFromParent(); ++NumInstRemoved; @@ -647,6 +754,21 @@ void StackAllocationPromoter::fixPhiPredBlock(BlockSet &PhiBlocks, TI->eraseFromParent(); } +static bool hasOnlyUndefIncomingValues(SILPhiArgument *phiArg) { + SmallVector incomingValues; + phiArg->getIncomingPhiValues(incomingValues); + for (auto predArg : incomingValues) { + if (isa(predArg)) + continue; + if (isa(predArg) && + hasOnlyUndefIncomingValues(cast(predArg))) { + continue; + } + return false; + } + return true; +} + void StackAllocationPromoter::fixBranchesAndUses(BlockSet &PhiBlocks) { // First update uses of the value. SmallVector collectedLoads; @@ -683,6 +805,16 @@ void StackAllocationPromoter::fixBranchesAndUses(BlockSet &PhiBlocks) { // on. SILBasicBlock *BB = Inst->getParent(); + if (!BB->isEntry()) { + if (auto *SI = dyn_cast(Inst)) { + if (SI->getOwnershipQualifier() == StoreOwnershipQualifier::Assign) { + SILValue Def = getLiveInValue(PhiBlocks, BB); + SILBuilderWithScope(SI).createDestroyValue(SI->getLoc(), Def); + continue; + } + } + } + if (auto *DVAI = dyn_cast(Inst)) { // Replace DebugValueAddr with DebugValue. SILValue Def = getLiveInValue(PhiBlocks, BB); @@ -714,6 +846,22 @@ void StackAllocationPromoter::fixBranchesAndUses(BlockSet &PhiBlocks) { fixPhiPredBlock(PhiBlocks, Block, PBB); } } + + // If the owned phi arg we added did not have any uses, create end_lifetime to + // end its lifetime. In asserts mode, make sure we have only undef incoming + // values for such phi args. + if (ASI->getFunction()->hasOwnership()) { + for (auto Block : PhiBlocks) { + auto *phiArg = cast( + Block->getArgument(Block->getNumArguments() - 1)); + if (phiArg->getOwnershipKind() == ValueOwnershipKind::Owned && + phiArg->use_empty()) { + assert(hasOnlyUndefIncomingValues(phiArg)); + SILBuilderWithScope(&Block->front()) + .createEndLifetime(Block->front().getLoc(), phiArg); + } + } + } } void StackAllocationPromoter::pruneAllocStackUsage() { @@ -960,10 +1108,6 @@ class SILMem2Reg : public SILFunctionTransform { void run() override { SILFunction *F = getFunction(); - // FIXME: We should be able to support ownership. - if (F->hasOwnership()) - return; - LLVM_DEBUG(llvm::dbgs() << "** Mem2Reg on function: " << F->getName() << " **\n"); diff --git a/test/SILOptimizer/mem2reg.sil b/test/SILOptimizer/mem2reg.sil index 8373fbee77f66..94d43f7053f09 100644 --- a/test/SILOptimizer/mem2reg.sil +++ b/test/SILOptimizer/mem2reg.sil @@ -449,12 +449,12 @@ bb0: return %16 : $((), ()) } -// CHECK-LABEL: sil @unchecked_ref_cast +// CHECK-LABEL: sil @unchecked_bitwise_cast // CHECK-NOT: alloc_stack // CHECK: [[CAST:%.*]] = unchecked_bitwise_cast %0 : $Optional to $Klass // CHECK: release_value [[CAST]] // CHECK: return -sil @unchecked_ref_cast : $@convention(thin) (@owned Optional) -> () { +sil @unchecked_bitwise_cast : $@convention(thin) (@owned Optional) -> () { bb0(%0 : $Optional): %1 = alloc_stack $Optional store %0 to %1 : $*Optional diff --git a/test/SILOptimizer/mem2reg_liveness_ossa.sil b/test/SILOptimizer/mem2reg_liveness_ossa.sil new file mode 100644 index 0000000000000..d9121babdecda --- /dev/null +++ b/test/SILOptimizer/mem2reg_liveness_ossa.sil @@ -0,0 +1,65 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -mem2reg | %FileCheck %s + +import Builtin +import Swift + +sil [ossa] @_Ts5printFT3valSi_T_ : $@convention(thin) (Int64) -> () + +// CHECK-LABEL: sil [ossa] @liveness0 : +// CHECK-NOT: alloc_stack +sil [ossa] @liveness0 : $@convention(thin) (Int64) -> () { +bb0(%0 : $Int64): + %1 = alloc_stack $Int64, var, name "x" + store %0 to [trivial] %1 : $*Int64 + %3 = integer_literal $Builtin.Int64, 10 + %5 = struct_extract %0 : $Int64, #Int64._value + %6 = builtin "cmp_eq_Int64"(%5 : $Builtin.Int64, %3 : $Builtin.Int64) : $Builtin.Int1 + cond_br %6, bb1, bb5 + +bb1: + %8 = alloc_stack $Int64, var, name "y" + %9 = integer_literal $Builtin.Int64, 20 + %10 = struct $Int64 (%9 : $Builtin.Int64) + store %10 to [trivial] %8 : $*Int64 + %12 = integer_literal $Builtin.Int64, 3 + %14 = struct_extract %0 : $Int64, #Int64._value + %15 = builtin "cmp_sgt_Int64"(%14 : $Builtin.Int64, %12 : $Builtin.Int64) : $Builtin.Int1 + cond_br %15, bb2, bb3 + +bb2: + %17 = integer_literal $Builtin.Int64, 0 + %18 = struct $Int64 (%17 : $Builtin.Int64) + store %18 to [trivial] %8 : $*Int64 + br bb4 + +bb3: + %21 = integer_literal $Builtin.Int64, 2 + %22 = struct $Int64 (%21 : $Builtin.Int64) + store %22 to [trivial] %8 : $*Int64 + br bb4 + +bb4: + // function_ref + %25 = function_ref @_Ts5printFT3valSi_T_ : $@convention(thin) (Int64) -> () + %26 = load [trivial] %8 : $*Int64 + %27 = integer_literal $Builtin.Int64, 2 + %28 = integer_literal $Builtin.Int1, -1 + %30 = struct_extract %26 : $Int64, #Int64._value + %31 = builtin "sadd_with_overflow_Int64"(%30 : $Builtin.Int64, %27 : $Builtin.Int64, %28 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) + %32 = tuple_extract %31 : $(Builtin.Int64, Builtin.Int1), 0 + %33 = tuple_extract %31 : $(Builtin.Int64, Builtin.Int1), 1 + %34 = struct $Int64 (%32 : $Builtin.Int64) + cond_fail %33 : $Builtin.Int1 + %36 = apply %25(%34) : $@convention(thin) (Int64) -> () + dealloc_stack %8 : $*Int64 + br bb5 + +// We don't need a PHI node here because the value is dead! +// CHECK: bb5: +bb5: + dealloc_stack %1 : $*Int64 + %40 = tuple () + return %40 : $() +} +// CHECK-LABEL: } // end sil function 'liveness0' + diff --git a/test/SILOptimizer/mem2reg_ossa.sil b/test/SILOptimizer/mem2reg_ossa.sil new file mode 100644 index 0000000000000..26943f8c5442b --- /dev/null +++ b/test/SILOptimizer/mem2reg_ossa.sil @@ -0,0 +1,489 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -mem2reg | %FileCheck %s + +import Builtin +import Swift + +////////////////// +// Declarations // +////////////////// + +class Klass {} + +struct SmallCodesizeStruct { + var cls1 : Klass + var cls2 : Klass +} + +struct LargeCodesizeStruct { + var s1: SmallCodesizeStruct + var s2: SmallCodesizeStruct + var s3: SmallCodesizeStruct + var s4: SmallCodesizeStruct + var s5: SmallCodesizeStruct +} + +/////////// +// Tests // +/////////// + +// CHECK-LABEL: sil [ossa] @store_only_allocas : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'store_only_allocas' +// simple.foo0 (c : Swift.Int64) -> () +sil [ossa] @store_only_allocas : $@convention(thin) (Int64) -> () { +bb0(%0 : $Int64): + %1 = alloc_stack $Int64, var, name "c" + store %0 to [trivial] %1 : $*Int64 + // function_ref Swift.print (val : Swift.Int64) -> () + %3 = function_ref @_Ts5printFT3valSi_T_ : $@convention(thin) (Int64) -> () + %4 = apply %3(%0) : $@convention(thin) (Int64) -> () + dealloc_stack %1 : $*Int64 + %6 = tuple () + return %6 : $() +} + +// Swift.print (val : Swift.Int64) -> () +sil [ossa] @_Ts5printFT3valSi_T_ : $@convention(thin) (Int64) -> () + +// CHECK-LABEL: sil [ossa] @multiple_store_vals : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'multiple_store_vals' +// simple.foo1 (c : Swift.Int64) -> Swift.Int64 +sil [ossa] @multiple_store_vals : $@convention(thin) (Int64) -> Int64 { +bb0(%0 : $Int64): + %1 = alloc_stack $Int64, var, name "c" + store %0 to [trivial] %1 : $*Int64 + %3 = alloc_stack $Int64, var, name "x" + %4 = integer_literal $Builtin.Int64, 2 + %5 = struct $Int64 (%4 : $Builtin.Int64) + store %5 to [trivial] %3 : $*Int64 + %7 = integer_literal $Builtin.Int64, 5 + %8 = integer_literal $Builtin.Int1, 0 + %9 = struct $Int64 (%7 : $Builtin.Int64) + cond_fail %8 : $Builtin.Int1 + store %9 to [trivial] %3 : $*Int64 + store %9 to [trivial] %3 : $*Int64 + dealloc_stack %3 : $*Int64 + dealloc_stack %1 : $*Int64 + return %9 : $Int64 +} + +// CHECK-LABEL: sil [ossa] @multiple_store_vals2 : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'multiple_store_vals2' +// simple.foo2 (c : Swift.Int64) -> Swift.Int64 +sil [ossa] @multiple_store_vals2 : $@convention(thin) (Int64) -> Int64 { +bb0(%0 : $Int64): + %1 = alloc_stack $Int64, var, name "c" + store %0 to [trivial] %1 : $*Int64 + %3 = alloc_box $<τ_0_0> { var τ_0_0 } , var, name "x" + %3a = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 + %4 = integer_literal $Builtin.Int64, 2 + %5 = struct $Int64 (%4 : $Builtin.Int64) + store %5 to [trivial] %3a : $*Int64 + %8 = struct_extract %0 : $Int64, #Int64._value + %9 = builtin "cmp_sgt_Int64"(%8 : $Builtin.Int64, %4 : $Builtin.Int64) : $Builtin.Int1 + cond_br %9, bb1, bb2 + +bb1: + destroy_value %3 : $<τ_0_0> { var τ_0_0 } + br bb3(%5 : $Int64) + +bb2: + %13 = integer_literal $Builtin.Int64, 5 + %14 = struct $Int64 (%13 : $Builtin.Int64) + cond_fail %9 : $Builtin.Int1 + destroy_value %3 : $<τ_0_0> { var τ_0_0 } + br bb3(%14 : $Int64) + +bb3(%18 : $Int64): + dealloc_stack %1 : $*Int64 + return %18 : $Int64 +} + +// CHECK-LABEL: sil [ossa] @with_loads : +// CHECK: bb3([[RET:%[0-9]+]] : $Int64): +// CHECK-LABEL: } // end sil function 'with_loads' +// simple.foo2 (c : Swift.Int64) -> Swift.Int64 +sil [ossa] @with_loads : $@convention(thin) (Int64) -> Int64 { +bb0(%0 : $Int64): + %1 = alloc_stack $Int64, var, name "c" + store %0 to [trivial] %1 : $*Int64 + %3 = alloc_box $<τ_0_0> { var τ_0_0 } , var, name "x" + %3a = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 + %4 = integer_literal $Builtin.Int64, 2 + %5 = struct $Int64 (%4 : $Builtin.Int64) + store %5 to [trivial] %3a : $*Int64 + %8 = struct_extract %0 : $Int64, #Int64._value + %9 = builtin "cmp_sgt_Int64"(%8 : $Builtin.Int64, %4 : $Builtin.Int64) : $Builtin.Int1 + cond_br %9, bb1, bb2 + +bb1: + destroy_value %3 : $<τ_0_0> { var τ_0_0 } + br bb3(%5 : $Int64) + +bb2: + %13 = integer_literal $Builtin.Int64, 5 + %14 = struct $Int64 (%13 : $Builtin.Int64) + cond_fail %9 : $Builtin.Int1 + destroy_value %3 : $<τ_0_0> { var τ_0_0 } + br bb3(%14 : $Int64) + +bb3(%18 : $Int64): + dealloc_stack %1 : $*Int64 + %20 = load [trivial] %1 : $*Int64 + return %18 : $Int64 +} + +// CHECK-LABEL: sil [ossa] @basic_block_with_loads_and_stores : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'basic_block_with_loads_and_stores' +// test.foo3 (c : Swift.Int64) -> () +sil [ossa] @basic_block_with_loads_and_stores : $@convention(thin) (Int64) -> () { +bb0(%0 : $Int64): + %1 = alloc_stack $Int64, var, name "c" + store %0 to [trivial] %1 : $*Int64 + %3 = alloc_stack $Int64, var, name "x" + %4 = integer_literal $Builtin.Int64, 3 + %5 = struct $Int64 (%4 : $Builtin.Int64) + store %5 to [trivial] %3 : $*Int64 + %7 = integer_literal $Builtin.Int64, 3 + %9 = struct_extract %0 : $Int64, #Int64._value + %10 = builtin "cmp_sgt_Int64"(%9 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1 + + %12 = integer_literal $Builtin.Int64, 2 + %13 = struct $Int64 (%12 : $Builtin.Int64) + store %13 to [trivial] %3 : $*Int64 + + // function_ref Swift.print (val : Swift.Int64) -> () + %16 = function_ref @_Ts5printFT3valSi_T_ : $@convention(thin) (Int64) -> () + %17 = load [trivial] %3 : $*Int64 + %18 = apply %16(%17) : $@convention(thin) (Int64) -> () + dealloc_stack %3 : $*Int64 + dealloc_stack %1 : $*Int64 + %21 = tuple () + return %21 : $() +} + +// CHECK-LABEL: sil [ossa] @load_uninitialized_empty : +// CHECK-NOT: load +// CHECK-LABEL: } // end sil function 'load_uninitialized_empty' +sil [ossa] @load_uninitialized_empty : $@convention(thin) (@inout ()) -> () { +bb0(%0 : $*()): + %1 = alloc_stack $() + %2 = load [trivial] %1 : $*() + store %2 to [trivial] %0 : $*() + dealloc_stack %1 : $*() + %3 = tuple () + return %3 : $() +} + +// CHECK-LABEL: sil [ossa] @mem2reg_debug_value_addr : +// CHECK-NOT: alloc_stack +// CHECK-NOT: debug_value_addr +// CHECK: debug_value %0 +// CHECK-LABEL: } // end sil function 'mem2reg_debug_value_addr' +sil [ossa] @mem2reg_debug_value_addr : $@convention(thin) (Int) -> Int { +bb0(%0 : $Int): + %1 = alloc_stack $Int + store %0 to [trivial] %1 : $*Int + debug_value_addr %1 : $*Int + %2 = load [trivial] %1 : $*Int + dealloc_stack %1 : $*Int + return %2 : $Int +} + +// CHECK-LABEL: sil [ossa] @mem2reg_struct_addr : +// CHECK-NOT: alloc_stack +// CHECK: struct_extract +// CHECK-LABEL: } // end sil function 'mem2reg_struct_addr' +sil [ossa] @mem2reg_struct_addr : $@convention(thin) (Int64) -> Builtin.Int64 { +bb0(%0 : $Int64): + %1 = alloc_stack $Int64 + store %0 to [trivial] %1 : $*Int64 + %2 = struct_element_addr %1 : $*Int64, #Int64._value + %3 = load [trivial] %2 : $*Builtin.Int64 + dealloc_stack %1 : $*Int64 + return %3 : $Builtin.Int64 +} + +// CHECK-LABEL: sil [ossa] @mem2reg_tuple_addr : +// CHECK-NOT: alloc_stack +// CHECK: tuple_extract {{.*}}, 0 +// CHECK-LABEL: } // end sil function 'mem2reg_tuple_addr' +sil [ossa] @mem2reg_tuple_addr : $@convention(thin) (Int64) -> Int64 { +bb0(%0 : $Int64): + %1 = alloc_stack $(Int64, Int64) + %2 = tuple (%0 : $Int64, %0 : $Int64) + store %2 to [trivial] %1 : $*(Int64, Int64) + %4 = tuple_element_addr %1 : $*(Int64, Int64), 0 + %5 = load [trivial] %4 : $*Int64 + dealloc_stack %1 : $*(Int64, Int64) + return %5 : $Int64 +} + +// CHECK-LABEL: sil [ossa] @struct_extract_if_then_else : +// CHECK-NOT: alloc_stack +sil [ossa] @struct_extract_if_then_else : $@convention(thin) (Int64) -> Int64 { +bb0(%0 : $Int64): + %1 = alloc_stack $Int64 + store %0 to [trivial] %1 : $*Int64 + %3 = integer_literal $Builtin.Int64, 2 + %4 = struct_extract %0 : $Int64, #Int64._value + %5 = builtin "cmp_sgt_Int64"(%4 : $Builtin.Int64, %3 : $Builtin.Int64) : $Builtin.Int1 + %6 = struct_element_addr %1 : $*Int64, #Int64._value + cond_br %5, bb1, bb2 + +// CHECK: bb1: +// CHECK: struct_extract %0 +bb1: + %8 = load [trivial] %6 : $*Builtin.Int64 + %9 = integer_literal $Builtin.Int64, 1 + %10 = integer_literal $Builtin.Int1, 0 + %11 = builtin "sadd_with_overflow_Int64"(%8 : $Builtin.Int64, %9 : $Builtin.Int64, %10 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) + %12 = tuple_extract %11 : $(Builtin.Int64, Builtin.Int1), 0 + br bb3(%12 : $Builtin.Int64) + +// CHECK: bb2: +// CHECK: struct_extract %0 +bb2: + %14 = load [trivial] %6 : $*Builtin.Int64 + %15 = integer_literal $Builtin.Int64, 2 + %16 = integer_literal $Builtin.Int1, 0 + %17 = builtin "sadd_with_overflow_Int64"(%14 : $Builtin.Int64, %15 : $Builtin.Int64, %16 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) + %18 = tuple_extract %17 : $(Builtin.Int64, Builtin.Int1), 0 + br bb3(%18 : $Builtin.Int64) + +// CHECK-NOT: dealloc_stack +bb3(%20 : $Builtin.Int64): + dealloc_stack %1 : $*Int64 + %22 = struct $Int64 (%20 : $Builtin.Int64) + return %22 : $Int64 +} +// CHECK-LABEL: } // end sil function 'struct_extract_if_then_else' + +sil [ossa] @first : $@convention(thin) () -> Int +sil [ossa] @second : $@convention(thin) () -> Int + +// CHECK: sil [ossa] @promote_function_refs : +sil [ossa] @promote_function_refs : $@convention(thin) (Bool) -> Int { +// CHECK: bb0 +bb0(%0 : $Bool): +// CHECK-NOT: [[STACK:%.*]] = alloc_stack + %1 = alloc_stack $@callee_owned () -> Int + debug_value %0 : $Bool + %3 = struct_extract %0 : $Bool, #Bool._value + cond_br %3, bb1, bb2 + +// CHECK: bb1 +bb1: +// CHECK: [[FIRSTREF:%.*]] = function_ref @first + %5 = function_ref @first : $@convention(thin) () -> Int +// CHECK: [[FIRSTTHICK:%.*]] = thin_to_thick_function [[FIRSTREF]] + %6 = thin_to_thick_function %5 : $@convention(thin) () -> Int to $@callee_owned () -> Int +// CHECK-NOT: store + store %6 to [init] %1 : $*@callee_owned () -> Int +// CHECK: br bb3([[FIRSTTHICK]] : $@callee_owned () -> Int + br bb3 + +// CHECK: bb2 +bb2: +// CHECK: [[SECONDREF:%.*]] = function_ref @second + %9 = function_ref @second : $@convention(thin) () -> Int +// CHECK: [[SECONDTHICK:%.*]] = thin_to_thick_function [[SECONDREF]] + %10 = thin_to_thick_function %9 : $@convention(thin) () -> Int to $@callee_owned () -> Int +// CHECK-NOT: store + store %10 to [init] %1 : $*@callee_owned () -> Int +// CHECK: br bb3([[SECONDTHICK]] : $@callee_owned () -> Int) + br bb3 + +// CHECK: bb3([[ARG:%.*]] : @owned $@callee_owned () -> Int): +bb3: +// CHECK-NOT: load [[STACK]] + %13 = load [copy] %1 : $*@callee_owned () -> Int +// CHECK: [[COPY:%.*]] = copy_value [[ARG]] +// CHECK: [[RESULT:%.*]] = apply [[COPY]] + %15 = apply %13() : $@callee_owned () -> Int + br bb4 + + // NOTE: This block and the branch above exist to ensure that we + // test what happens when %1 hasn't already been loaded in this + // block. +// CHECK: bb4 +bb4: +// CHECK-NOT: destroy_addr [[STACK]] +// CHECK: destroy_value [[ARG]] + destroy_addr %1 : $*@callee_owned () -> Int +// CHECK-NOT: dealloc_stack [[STACK]] + dealloc_stack %1 : $*@callee_owned () -> Int + return %15 : $Int +} +// CHECK-LABEL: } // end sil function 'promote_function_refs' + +// Test cases where the only use is a debug_value_addr +// CHECK-LABEL: sil [ossa] @no_real_uses : +sil [ossa] @no_real_uses : $@convention(thin) () -> () { +// CHECK: bb0 +bb0: + // CHECK-NOT: alloc_stack + %0 = alloc_stack $Builtin.Int32 + // CHECK-NOT: debug_value_addr + debug_value_addr %0 : $*Builtin.Int32, let, name "x", argno 1 + // CHECK-NOT: dealloc_stack + dealloc_stack %0 : $*Builtin.Int32 + %1 = tuple () + return %1 : $() +} +// CHECK-LABEL: } // end sil function 'no_real_uses' + +// CHECK-LABEL: sil [ossa] @keep_release : +// CHECK: destroy_value %0 +// CHECK-LABEL: } // end sil function 'keep_release' +sil [ossa] @keep_release : $@convention(thin) (@owned AnyObject) -> () { +bb0(%0 : @owned $AnyObject): + %1 = alloc_stack $AnyObject + store %0 to [init] %1 : $*AnyObject + destroy_addr %1 : $*AnyObject + dealloc_stack %1 : $*AnyObject + %7 = tuple () + return %7 : $() +} + +// Test cases where there are dead address instructions. +// CHECK-LABEL: sil [ossa] @dead_use : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'dead_use' +sil [ossa] @dead_use : $@convention(thin) () -> () { + %0 = alloc_stack $Int64 + %1 = struct_element_addr %0 : $*Int64, #Int64._value + dealloc_stack %0 : $*Int64 + %2 = alloc_stack $(Int64, Int64) + %3 = tuple_element_addr %2 : $*(Int64, Int64), 0 + dealloc_stack %2 : $*(Int64, Int64) + %4 = tuple () + return %4 : $() +} + +// CHECK-LABEL: sil [ossa] @dont_crash_on_dead_arg_use : +// CHECK: bb0{{.*}}: +// CHECK: tuple () +// CHECK-LABEL: } // end sil function 'dont_crash_on_dead_arg_use' +sil [ossa] @dont_crash_on_dead_arg_use : $@convention(thin) (@inout Int64) -> () { +bb0(%0 : $*Int64): + %2 = alloc_stack $Int64 + %1 = struct_element_addr %0 : $*Int64, #Int64._value + %3 = struct_element_addr %2 : $*Int64, #Int64._value + dealloc_stack %2 : $*Int64 + %4 = tuple () + return %4 : $() +} + +// Make sure that we do expand destroy_addr appropriately for code-size +// trade-offs. +// CHECK-LABEL: sil [ossa] @large_struct_test : +// CHECK: bb0([[ARG0:%.*]] : @owned $LargeCodesizeStruct): +// CHECK: destroy_value [[ARG0]] +// CHECK: } // end sil function 'large_struct_test' +sil [ossa] @large_struct_test : $@convention(thin) (@owned LargeCodesizeStruct) -> () { +bb0(%0 : @owned $LargeCodesizeStruct): + %1 = alloc_stack $LargeCodesizeStruct + store %0 to [init] %1 : $*LargeCodesizeStruct + destroy_addr %1 : $*LargeCodesizeStruct + dealloc_stack %1 : $*LargeCodesizeStruct + %7 = tuple () + return %7 : $() +} + +// CHECK-LABEL: sil [ossa] @small_struct_test : +// CHECK: bb0([[ARG0:%.*]] : @owned $SmallCodesizeStruct): +// CHECK: ([[ELEM1:%[0-9]+]], [[ELEM2:%[0-9]+]]) = destructure_struct [[ARG0]] +// CHECK: destroy_value [[ELEM1]] +// CHECK: destroy_value [[ELEM2]] +// CHECK: } // end sil function 'small_struct_test' +sil [ossa] @small_struct_test : $@convention(thin) (@owned SmallCodesizeStruct) -> () { +bb0(%0 : @owned $SmallCodesizeStruct): + %1 = alloc_stack $SmallCodesizeStruct + store %0 to [init] %1 : $*SmallCodesizeStruct + destroy_addr %1 : $*SmallCodesizeStruct + dealloc_stack %1 : $*SmallCodesizeStruct + %7 = tuple () + return %7 : $() +} + +// CHECK-LABEL: sil [ossa] @small_struct_multi_test : +// CHECK-NOT: alloc_stack +// CHECK: [[COPY:%.*]] = copy_value %0 +// CHECK-NEXT: destructure_struct %0 +// CHECK-NEXT: destroy_value +// CHECK-NEXT: destroy_value +// CHECK-NEXT: begin_borrow [[COPY]] +// CHECK-NEXT: debug_value +// CHECK-NEXT: end_borrow +// CHECK-NEXT: destroy_value [[COPY]] +// CHECK: bb2: +// CHECK-NEXT: destructure_struct %0 +// CHECK-NEXT: destroy_value +// CHECK-NEXT: destroy_value +// CHECK-LABEL: } // end sil function 'small_struct_multi_test' +sil [ossa] @small_struct_multi_test : $@convention(thin) (@owned SmallCodesizeStruct) -> () { +bb0(%0 : @owned $SmallCodesizeStruct): + %1 = alloc_stack $SmallCodesizeStruct + store %0 to [init] %1 : $*SmallCodesizeStruct + cond_br undef, bb1, bb2 + +bb1: + %3 = load [copy] %1 : $*SmallCodesizeStruct + destroy_addr %1 : $*SmallCodesizeStruct + dealloc_stack %1 : $*SmallCodesizeStruct + %4 = begin_borrow %3 : $SmallCodesizeStruct + debug_value %4 : $SmallCodesizeStruct + end_borrow %4 : $SmallCodesizeStruct + destroy_value %3 : $SmallCodesizeStruct + br bb3 + +bb2: + destroy_addr %1 : $*SmallCodesizeStruct + dealloc_stack %1 : $*SmallCodesizeStruct + br bb3 + +bb3: + %7 = tuple () + return %7 : $() +} + + +// CHECK-LABEL: sil [ossa] @dead_address_projections : +// CHECK-NOT: alloc_stack +// CHECK: } // end sil function 'dead_address_projections' +sil [ossa] @dead_address_projections : $@convention(thin) (((), ())) -> ((), ()) { +bb0(%0 : $((), ())): + %1 = alloc_stack $((), ()) + %200 = tuple_element_addr %1 : $*((), ()), 0 + %300 = tuple_element_addr %1 : $*((), ()), 1 + cond_br undef, bb1, bb2 + +bb1: + store %0 to [trivial] %1 : $*((), ()) + %16 = load [trivial] %1 : $*((), ()) + dealloc_stack %1 : $*((), ()) + br bb3(%16 : $((), ())) + +bb2: + dealloc_stack %1 : $*((), ()) + br bb3(%0 : $((), ())) + +bb3(%20 : $((), ())): + return %20 : $((), ()) +} + +// CHECK-LABEL: sil [ossa] @load_tuple_of_void : +// CHECK-NOT: alloc_stack +// CHECK: return undef : $((), ()) +// CHECK: } // end sil function 'load_tuple_of_void' +sil [ossa] @load_tuple_of_void : $@convention(thin) () -> ((), ()) { +bb0: + %1 = alloc_stack $((), ()) + %16 = load [trivial] %1 : $*((), ()) + dealloc_stack %1 : $*((), ()) + return %16 : $((), ()) +} diff --git a/test/SILOptimizer/mem2reg_ossa_nontrivial.sil b/test/SILOptimizer/mem2reg_ossa_nontrivial.sil new file mode 100644 index 0000000000000..33f643644f8ed --- /dev/null +++ b/test/SILOptimizer/mem2reg_ossa_nontrivial.sil @@ -0,0 +1,615 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -mem2reg | %FileCheck %s + +import Builtin +import Swift + +////////////////// +// Declarations // +////////////////// + +class Klass {} + +struct SmallCodesizeStruct { + var cls1 : Klass + var cls2 : Klass +} + +struct WrapperStruct { + var cls : Klass +} + +struct LargeCodesizeStruct { + var s1: SmallCodesizeStruct + var s2: SmallCodesizeStruct + var s3: SmallCodesizeStruct + var s4: SmallCodesizeStruct + var s5: SmallCodesizeStruct +} + +/////////// +// Tests // +/////////// + +sil [noinline] [ossa] @blackhole : $@convention(thin) (@in_guaranteed T) -> () { +bb0(%0 : $*T): + debug_value_addr %0 : $*T, let, name "t", argno 1 + %2 = tuple () + return %2 : $() +} + +sil shared [noinline] @blackhole_spl : $@convention(thin) (@guaranteed Klass) -> () { +bb0(%0 : $Klass): + %1 = tuple () + return %1 : $() +} + +// CHECK-LABEL: sil [ossa] @store_only_allocas : +// CHECK-NOT: alloc_stack +// CHECK: return +sil [ossa] @store_only_allocas : $@convention(thin) (@owned Klass) -> () { +bb0(%0 : @owned $Klass): + %1 = alloc_stack $Klass + store %0 to [init] %1 : $*Klass + %2 = function_ref @blackhole_spl : $@convention(thin) (@guaranteed Klass) -> () + %3 = load [take] %1 : $*Klass + %4 = apply %2(%3) : $@convention(thin) (@guaranteed Klass) -> () + destroy_value %3 : $Klass + dealloc_stack %1 : $*Klass + %6 = tuple () + return %6 : $() +} + +// CHECK-LABEL: sil [ossa] @multiple_store_vals : +// CHECK-NOT: alloc_stack +// CHECK: destroy_value %0 : $Klass +// CHECK: [[FUNC:%.*]] = function_ref @blackhole_spl : +// CHECK: apply [[FUNC]](%1) : $@convention(thin) (@guaranteed Klass) -> () +// CHECK: destroy_value %1 : $Klass +// CHECK-LABEL: } // end sil function 'multiple_store_vals' +sil [ossa] @multiple_store_vals : $@convention(thin) (@owned Klass, @owned Klass) -> () { +bb0(%0 : @owned $Klass, %1 : @owned $Klass): + %2 = alloc_stack $Klass + store %0 to [init] %2 : $*Klass + %3 = integer_literal $Builtin.Int1, 0 + cond_fail %3 : $Builtin.Int1 + store %1 to [assign] %2 : $*Klass + %4 = function_ref @blackhole_spl : $@convention(thin) (@guaranteed Klass) -> () + %5 = load [take] %2 : $*Klass + %6 = apply %4(%5) : $@convention(thin) (@guaranteed Klass) -> () + destroy_value %5 : $Klass + dealloc_stack %2 : $*Klass + %7 = tuple () + return %7 : $() +} + +// CHECK-LABEL: sil [ossa] @multiple_store_vals2 : +// CHECK-NOT: alloc_stack +// CHECK: destroy_value %0 : $Klass +// CHECK: [[FUNC:%.*]] = function_ref @blackhole_spl : +// CHECK: apply [[FUNC]](%1) : $@convention(thin) (@guaranteed Klass) -> () +// CHECK: destroy_value %1 : $Klass +// CHECK-LABEL: } // end sil function 'multiple_store_vals2' +sil [ossa] @multiple_store_vals2 : $@convention(thin) (@owned Klass, @owned Klass) -> () { +bb0(%0 : @owned $Klass, %1 : @owned $Klass): + %2 = alloc_stack $Klass + store %0 to [init] %2 : $*Klass + %3 = integer_literal $Builtin.Int1, 0 + cond_fail %3 : $Builtin.Int1 + destroy_addr %2 : $*Klass + store %1 to [init] %2 : $*Klass + %4 = function_ref @blackhole_spl : $@convention(thin) (@guaranteed Klass) -> () + %5 = load [take] %2 : $*Klass + %6 = apply %4(%5) : $@convention(thin) (@guaranteed Klass) -> () + destroy_value %5 : $Klass + dealloc_stack %2 : $*Klass + %7 = tuple () + return %7 : $() +} + +// CHECK-LABEL: sil [ossa] @multiple_store_vals3 : +// CHECK-NOT: alloc_stack +// CHECK: [[COPY0:%.*]] = copy_value %0 : $Klass +// CHECK: [[COPY1:%.*]] = copy_value %1 : $Klass +// CHECK: destroy_value [[COPY0]] : $Klass +// CHECK: [[FUNC:%.*]] = function_ref @blackhole_spl : +// CHECK: apply [[FUNC]]([[COPY1]]) : $@convention(thin) (@guaranteed Klass) -> () +// CHECK: destroy_value [[COPY1]] : $Klass +// CHECK-LABEL: } // end sil function 'multiple_store_vals3' +sil [ossa] @multiple_store_vals3 : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> () { +bb0(%0 : @guaranteed $Klass, %1 : @guaranteed $Klass): + %2 = alloc_stack $Klass + %copy0 = copy_value %0 : $Klass + store %copy0 to [init] %2 : $*Klass + %3 = integer_literal $Builtin.Int1, 0 + cond_fail %3 : $Builtin.Int1 + %copy1 = copy_value %1 : $Klass + store %copy1 to [assign] %2 : $*Klass + %4 = function_ref @blackhole_spl : $@convention(thin) (@guaranteed Klass) -> () + %5 = load [take] %2 : $*Klass + %6 = apply %4(%5) : $@convention(thin) (@guaranteed Klass) -> () + destroy_value %5 : $Klass + dealloc_stack %2 : $*Klass + %7 = tuple () + return %7 : $() +} + +// CHECK-LABEL: sil [ossa] @multiple_store_vals4 : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'multiple_store_vals4' +sil [ossa] @multiple_store_vals4 : $@convention(thin) (@owned Klass, @owned Klass) -> () { +bb0(%0 : @owned $Klass, %1 : @owned $Klass): + %2 = alloc_stack $Klass + store %0 to [init] %2 : $*Klass + %3 = alloc_box $<τ_0_0> { var τ_0_0 } + %3a = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 + store %1 to [assign] %2 : $*Klass + cond_br undef, bb1, bb2 + +bb1: + destroy_value %3 : $<τ_0_0> { var τ_0_0 } + br bb3 + +bb2: + destroy_value %3 : $<τ_0_0> { var τ_0_0 } + br bb3 + +bb3: + destroy_addr %2 : $*Klass + dealloc_stack %2 : $*Klass + %ret = tuple () + return %ret : $() +} + +// CHECK-LABEL: sil [ossa] @multiple_store_vals5 : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'multiple_store_vals5' +sil [ossa] @multiple_store_vals5 : $@convention(thin) (@owned Klass, @owned Klass, @owned Klass) -> () { +bb0(%0 : @owned $Klass, %1 : @owned $Klass, %2 : @owned $Klass): + %stk = alloc_stack $Klass + store %0 to [init] %stk : $*Klass + %3 = alloc_box $<τ_0_0> { var τ_0_0 } + %3a = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 + store %1 to [assign] %stk : $*Klass + store %2 to [assign] %stk : $*Klass + cond_br undef, bb1, bb2 + +bb1: + destroy_value %3 : $<τ_0_0> { var τ_0_0 } + br bb3 + +bb2: + destroy_value %3 : $<τ_0_0> { var τ_0_0 } + br bb3 + +bb3: + destroy_addr %stk : $*Klass + dealloc_stack %stk : $*Klass + %ret = tuple () + return %ret : $() +} + +// CHECK-LABEL: sil [ossa] @with_loads : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'with_loads' +sil [ossa] @with_loads : $@convention(thin) (@owned Klass, @owned Klass) -> @owned Klass { +bb0(%0 : @owned $Klass, %1 : @owned $Klass): + %2 = alloc_stack $Klass + store %0 to [init] %2 : $*Klass + %3 = alloc_box $<τ_0_0> { var τ_0_0 } + %3a = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 + store %1 to [assign] %2 : $*Klass + cond_br undef, bb1, bb2 + +bb1: + destroy_value %3 : $<τ_0_0> { var τ_0_0 } + br bb3 + +bb2: + destroy_value %3 : $<τ_0_0> { var τ_0_0 } + br bb3 + +bb3: + %ret = load [take] %2 : $*Klass + dealloc_stack %2 : $*Klass + return %ret : $Klass +} + +// CHECK-LABEL: sil [ossa] @basic_block_with_loads_and_stores : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'basic_block_with_loads_and_stores' +sil [ossa] @basic_block_with_loads_and_stores : $@convention(thin) (@owned Klass, @owned Klass) -> () { +bb0(%0 : @owned $Klass, %1 : @owned $Klass): + %2 = alloc_stack $Klass + store %0 to [init] %2 : $*Klass + %3 = alloc_stack $Klass + store %1 to [init] %3 : $*Klass + %local = alloc_ref $Klass + store %local to [assign] %3 : $*Klass + %func = function_ref @blackhole_spl : $@convention(thin) (@guaranteed Klass) -> () + %arg = load [take] %3 : $*Klass + %applyres = apply %func(%arg) : $@convention(thin) (@guaranteed Klass) -> () + destroy_value %arg : $Klass + destroy_addr %2 : $*Klass + dealloc_stack %3 : $*Klass + dealloc_stack %2 : $*Klass + %res = tuple () + return %res : $() +} + +// CHECK-LABEL: sil [ossa] @basic_block_with_loads_copy_and_take : +// CHECK-NOT: alloc_stack +// CHECK: [[COPY:%.*]] = copy_value %0 : $Klass +// CHECK: destroy_value [[COPY]] : $Klass +// CHECK: destroy_value %0 : $Klass +// CHECK-LABEL: } // end sil function 'basic_block_with_loads_copy_and_take' +sil [ossa] @basic_block_with_loads_copy_and_take : $@convention(thin) (@owned Klass) -> () { +bb0(%0 : @owned $Klass): + %1 = alloc_stack $Klass + store %0 to [init] %1 : $*Klass + %copy = load [copy] %1 : $*Klass + %take = load [take] %1 : $*Klass + destroy_value %copy : $Klass + destroy_value %take : $Klass + dealloc_stack %1 : $*Klass + %res = tuple () + return %res : $() +} + +// load [copy] is not used as RunningVal +// StackAllocationPromoter::fixBranchesAndUses will delete the loads and replace with %0 +// CHECK-LABEL: sil [ossa] @multi_basic_block_with_loads_copy_and_take_1 : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'multi_basic_block_with_loads_copy_and_take_1' +sil [ossa] @multi_basic_block_with_loads_copy_and_take_1 : $@convention(thin) (@owned Klass) -> () { +bb0(%0 : @owned $Klass): + %1 = alloc_stack $Klass + store %0 to [init] %1 : $*Klass + br bb1 +bb1: + %copy = load [copy] %1 : $*Klass + %take = load [take] %1 : $*Klass + destroy_value %copy : $Klass + destroy_value %take : $Klass + dealloc_stack %1 : $*Klass + %res = tuple () + return %res : $() +} + +// load [copy] is not used as RunningVal +// StackAllocationPromoter::fixBranchesAndUses will delete the loads and replace with %0 +// CHECK-LABEL: sil [ossa] @multi_basic_block_with_loads_copy_and_take_2 : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'multi_basic_block_with_loads_copy_and_take_2' +sil [ossa] @multi_basic_block_with_loads_copy_and_take_2 : $@convention(thin) (@owned Klass) -> @owned Klass { +bb0(%0 : @owned $Klass): + %1 = alloc_stack $Klass + store %0 to [init] %1 : $*Klass + br bb1 +bb1: + %copy = load [copy] %1 : $*Klass + %take = load [take] %1 : $*Klass + destroy_value %take : $Klass + dealloc_stack %1 : $*Klass + return %copy : $Klass +} + +// load [take] is used as RunningVal in bb1 +// CHECK-LABEL: sil [ossa] @multi_basic_block_with_loads_copy_and_take_3 : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'multi_basic_block_with_loads_copy_and_take_3' +sil [ossa] @multi_basic_block_with_loads_copy_and_take_3 : $@convention(thin) (@owned Klass) -> @owned Klass { +bb0(%0 : @owned $Klass): + %1 = alloc_stack $Klass + store %0 to [init] %1 : $*Klass + br bb1 +bb1: + %take = load [take] %1 : $*Klass + %copy = copy_value %take : $Klass + destroy_value %take : $Klass + dealloc_stack %1 : $*Klass + return %copy : $Klass +} + +// CHECK-LABEL: sil [ossa] @multi_basic_block_with_store_assign : +// CHECK-NOT: alloc_stack +// CHECK: destroy_value %0 : $Klass +// CHECK-LABEL: } // end sil function 'multi_basic_block_with_store_assign' +sil [ossa] @multi_basic_block_with_store_assign : $@convention(thin) (@owned Klass, @owned Klass) -> @owned Klass { +bb0(%0 : @owned $Klass, %1: @owned $Klass): + %stk = alloc_stack $Klass + store %0 to [init] %stk : $*Klass + br bb1 +bb1: + store %1 to [assign] %stk : $*Klass + %res = load [take] %stk : $*Klass + dealloc_stack %stk : $*Klass + return %res : $Klass +} + +// CHECK-LABEL: sil [ossa] @multi_basic_block_with_phiarg : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: bb1: +// CHECK: br bb3(%1 : $Klass) +// CHECK-LABEL: bb2: +// CHECK: br bb3(%0 : $Klass) +// CHECK-LABEL: } // end sil function 'multi_basic_block_with_phiarg' +sil [ossa] @multi_basic_block_with_phiarg : $@convention(thin) (@owned Klass, @owned Klass) -> () { +bb0(%0 : @owned $Klass, %1 : @owned $Klass): + %stk = alloc_stack $Klass + cond_br undef, bb1, bb2 +bb1: + store %1 to [init] %stk : $*Klass + destroy_value %0 : $Klass + br bb3 +bb2: + store %0 to [init] %stk : $*Klass + destroy_value %1 : $Klass + br bb3 +bb3: + %val = load [take] %stk : $*Klass + destroy_value %val : $Klass + dealloc_stack %stk : $*Klass + %res = tuple () + return %res : $() +} + +// CHECK-LABEL: sil [ossa] @multi_asi_basic_block_with_phiarg : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: bb1: +// CHECK: br bb3(%1 : $Klass, %0 : $Klass) +// CHECK-LABEL: bb2: +// CHECK: br bb3(%0 : $Klass, %1 : $Klass) +// CHECK-LABEL: } // end sil function 'multi_asi_basic_block_with_phiarg' +sil [ossa] @multi_asi_basic_block_with_phiarg : $@convention(thin) (@owned Klass, @owned Klass) -> () { +bb0(%0 : @owned $Klass, %1 : @owned $Klass): + %stk1 = alloc_stack $Klass + %stk2 = alloc_stack $Klass + cond_br undef, bb1, bb2 +bb1: + store %1 to [init] %stk1 : $*Klass + store %0 to [init] %stk2 : $*Klass + br bb3 +bb2: + store %1 to [init] %stk2 : $*Klass + store %0 to [init] %stk1 : $*Klass + br bb3 +bb3: + %val1 = load [take] %stk1 : $*Klass + destroy_value %val1 : $Klass + %val2 = load [take] %stk2 : $*Klass + destroy_value %val2 : $Klass + dealloc_stack %stk2 : $*Klass + dealloc_stack %stk1 : $*Klass + %res = tuple () + return %res : $() +} + +// CHECK-LABEL: sil [ossa] @multi_basic_block_stack_deallocated_phiarg : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: bb2: +// CHECK: br bb3(undef : $Klass) +// CHECK: bb3([[PHI:%.*]] : @owned $Klass): +// CHECK-NEXT: end_lifetime [[PHI]] : $Klass +// CHECK-LABEL: } // end sil function 'multi_basic_block_stack_deallocated_phiarg' +sil [ossa] @multi_basic_block_stack_deallocated_phiarg : $@convention(thin) (@owned Klass) -> () { +bb0(%0 : @owned $Klass): + %stk = alloc_stack $Klass + cond_br undef, bb1, bb2 +bb1: + dealloc_stack %stk : $*Klass + destroy_value %0 : $Klass + br bb3 +bb2: + store %0 to [init] %stk : $*Klass + %val = load [take] %stk : $*Klass + dealloc_stack %stk : $*Klass + destroy_value %val : $Klass + br bb3 +bb3: + %res = tuple () + return %res : $() +} + +// CHECK-LABEL: sil [ossa] @multi_asi_basic_block_stack_deallocated_phiarg : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: bb2: +// CHECK: br bb3(undef : $Klass, undef : $Klass) +// CHECK: bb3([[PHI1:%.*]] : @owned $Klass, [[PHI2:%.*]] : @owned $Klass): +// CHECK-NEXT: end_lifetime [[PHI2]] : $Klass +// CHECK-NEXT: end_lifetime [[PHI1]] : $Klass +// CHECK-LABEL: } // end sil function 'multi_asi_basic_block_stack_deallocated_phiarg' +sil [ossa] @multi_asi_basic_block_stack_deallocated_phiarg : $@convention(thin) (@owned Klass, @owned Klass) -> () { +bb0(%0 : @owned $Klass, %1 : @owned $Klass): + %stk1 = alloc_stack $Klass + %stk2 = alloc_stack $Klass + cond_br undef, bb1, bb2 +bb1: + dealloc_stack %stk2 : $*Klass + dealloc_stack %stk1 : $*Klass + destroy_value %0 : $Klass + destroy_value %1 : $Klass + br bb3 +bb2: + store %0 to [init] %stk1 : $*Klass + %val1 = load [take] %stk1 : $*Klass + store %1 to [init] %stk2 : $*Klass + %val2 = load [take] %stk2 : $*Klass + destroy_value %val1 : $Klass + destroy_value %val2 : $Klass + dealloc_stack %stk2 : $*Klass + dealloc_stack %stk1 : $*Klass + br bb3 +bb3: + %res = tuple () + return %res : $() +} + +// CHECK-LABEL: sil [ossa] @multi_basic_block_destroyed_last_stored_val_phiarg : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: bb2: +// CHECK: br bb3(undef : $Klass) +// CHECK-LABEL: } // end sil function 'multi_basic_block_destroyed_last_stored_val_phiarg' +sil [ossa] @multi_basic_block_destroyed_last_stored_val_phiarg : $@convention(thin) (@owned Klass) -> () { +bb0(%0 : @owned $Klass): + %stk = alloc_stack $Klass + cond_br undef, bb1, bb2 +bb1: + destroy_value %0 : $Klass + br bb3 +bb2: + store %0 to [init] %stk : $*Klass + %val = load [take] %stk : $*Klass + destroy_value %val : $Klass + br bb3 +bb3: + dealloc_stack %stk : $*Klass + %res = tuple () + return %res : $() +} + +// CHECK-LABEL: sil [ossa] @mem2reg_debug_value_addr : +// CHECK-NOT: alloc_stack +// CHECK-NOT: debug_value_addr +// CHECK: debug_value %0 +// CHECK-LABEL: } // end sil function 'mem2reg_debug_value_addr' +sil [ossa] @mem2reg_debug_value_addr : $@convention(thin) (@owned Klass) -> @owned Klass { +bb0(%0 : @owned $Klass): + %1 = alloc_stack $Klass + store %0 to [init] %1 : $*Klass + debug_value_addr %1 : $*Klass + %2 = load [take] %1 : $*Klass + dealloc_stack %1 : $*Klass + return %2 : $Klass +} + +// CHECK-LABEL: sil [ossa] @mem2reg_struct_addr : +// CHECK-NOT: alloc_stack +// CHECK: [[BORROW:%.*]] = begin_borrow %0 : $SmallCodesizeStruct +// CHECK: [[ELE:%.*]] = struct_extract [[BORROW]] +// CHECK: [[COPY:%.*]] = copy_value [[ELE]] : $Klass +// CHECK: end_borrow [[BORROW]] : $SmallCodesizeStruct +// CHECK: return [[COPY]] +// CHECK-LABEL: } // end sil function 'mem2reg_struct_addr' +sil [ossa] @mem2reg_struct_addr : $@convention(thin) (@owned SmallCodesizeStruct) -> @owned Klass { +bb0(%0 : @owned $SmallCodesizeStruct): + %1 = alloc_stack $SmallCodesizeStruct + store %0 to [init] %1 : $*SmallCodesizeStruct + %2 = struct_element_addr %1 : $*SmallCodesizeStruct, #SmallCodesizeStruct.cls1 + %3 = load [copy] %2 : $*Klass + destroy_addr %1 : $*SmallCodesizeStruct + dealloc_stack %1 : $*SmallCodesizeStruct + return %3 : $Klass +} + +// SILMem2Reg is disabled when there is a load [take] with struct_elemenet_addr/tuple_element_addr +// CHECK-LABEL: sil [ossa] @mem2reg_struct_addr_load_take : +// CHECK: alloc_stack +// CHECK-LABEL: } // end sil function 'mem2reg_struct_addr_load_take' +sil [ossa] @mem2reg_struct_addr_load_take : $@convention(thin) (@owned WrapperStruct) -> () { +bb0(%0 : @owned $WrapperStruct): + %1 = alloc_stack $WrapperStruct + store %0 to [init] %1 : $*WrapperStruct + %2 = struct_element_addr %1 : $*WrapperStruct, #WrapperStruct.cls + %3 = load [take] %2 : $*Klass + destroy_value %3 : $Klass + dealloc_stack %1 : $*WrapperStruct + %tup = tuple () + return %tup : $() +} + +// CHECK-LABEL: sil [ossa] @mem2reg_tuple_addr : +// CHECK-NOT: alloc_stack +// CHECK: [[TUP:%.*]] = tuple (%0 : $Klass, %1 : $Klass) +// CHECK: [[BORROW:%.*]] = begin_borrow [[TUP]] : $(Klass, Klass) +// CHECK: [[ELE:%.*]] = tuple_extract [[BORROW]] +// CHECK: [[COPY:%.*]] = copy_value [[ELE]] : $Klass +// CHECK: end_borrow [[BORROW]] : $(Klass, Klass) +// CHECK: return [[COPY]] +// CHECK-LABEL: } // end sil function 'mem2reg_tuple_addr' +sil [ossa] @mem2reg_tuple_addr : $@convention(thin) (@owned Klass, @owned Klass) -> @owned Klass { +bb0(%0 : @owned $Klass, %1 : @owned $Klass): + %stk = alloc_stack $(Klass, Klass) + %2 = tuple (%0 : $Klass, %1 : $Klass) + store %2 to [init] %stk : $*(Klass, Klass) + %4 = tuple_element_addr %stk : $*(Klass, Klass), 0 + %5 = load [copy] %4 : $*Klass + destroy_addr %stk : $*(Klass, Klass) + dealloc_stack %stk : $*(Klass, Klass) + return %5 : $Klass +} + +// CHECK-LABEL: sil [ossa] @struct_extract_if_then_else : +// CHECK-NOT: alloc_stack +sil [ossa] @struct_extract_if_then_else : $@convention(thin) (Int64) -> Int64 { +bb0(%0 : $Int64): + %1 = alloc_stack $Int64 + store %0 to [trivial] %1 : $*Int64 + %3 = integer_literal $Builtin.Int64, 2 + %4 = struct_extract %0 : $Int64, #Int64._value + %5 = builtin "cmp_sgt_Int64"(%4 : $Builtin.Int64, %3 : $Builtin.Int64) : $Builtin.Int1 + %6 = struct_element_addr %1 : $*Int64, #Int64._value + cond_br %5, bb1, bb2 + +// CHECK: bb1: +// CHECK: struct_extract %0 +bb1: + %8 = load [trivial] %6 : $*Builtin.Int64 + %9 = integer_literal $Builtin.Int64, 1 + %10 = integer_literal $Builtin.Int1, 0 + %11 = builtin "sadd_with_overflow_Int64"(%8 : $Builtin.Int64, %9 : $Builtin.Int64, %10 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) + %12 = tuple_extract %11 : $(Builtin.Int64, Builtin.Int1), 0 + br bb3(%12 : $Builtin.Int64) + +// CHECK: bb2: +// CHECK: struct_extract %0 +bb2: + %14 = load [trivial] %6 : $*Builtin.Int64 + %15 = integer_literal $Builtin.Int64, 2 + %16 = integer_literal $Builtin.Int1, 0 + %17 = builtin "sadd_with_overflow_Int64"(%14 : $Builtin.Int64, %15 : $Builtin.Int64, %16 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) + %18 = tuple_extract %17 : $(Builtin.Int64, Builtin.Int1), 0 + br bb3(%18 : $Builtin.Int64) + +// CHECK-NOT: dealloc_stack +bb3(%20 : $Builtin.Int64): + dealloc_stack %1 : $*Int64 + %22 = struct $Int64 (%20 : $Builtin.Int64) + return %22 : $Int64 +} +// CHECK-LABEL: } // end sil function 'struct_extract_if_then_else' + +// Test cases where the only use is a debug_value_addr +// CHECK-LABEL: sil [ossa] @no_real_uses : +sil [ossa] @no_real_uses : $@convention(thin) () -> () { +// CHECK: bb0 +bb0: + // CHECK-NOT: alloc_stack + %0 = alloc_stack $Klass + %local = alloc_ref $Klass + store %local to [init] %0 : $*Klass + // CHECK-NOT: debug_value_addr + debug_value_addr %0 : $*Klass + destroy_addr %0 : $*Klass + // CHECK-NOT: dealloc_stack + dealloc_stack %0 : $*Klass + %1 = tuple () + return %1 : $() +} +// CHECK-LABEL: } // end sil function 'no_real_uses' + +// CHECK-LABEL: sil [ossa] @half_trivial +// CHECK: destructure_tuple %0 +// CHECK-NEXT: destroy_value +// CHECK-NEXT: tuple +// CHECK-LABEL: } // end sil function 'half_trivial' +sil [ossa] @half_trivial : $@convention(thin) (@owned (Builtin.BridgeObject, Builtin.Int32)) -> () { +bb0(%0 : @owned $(Builtin.BridgeObject, Builtin.Int32)): + %1 = alloc_stack $(Builtin.BridgeObject, Builtin.Int32) + store %0 to [init] %1 : $*(Builtin.BridgeObject, Builtin.Int32) + %3 = load [copy] %1 : $*(Builtin.BridgeObject, Builtin.Int32) + destroy_value %3 : $(Builtin.BridgeObject, Builtin.Int32) + destroy_addr %1 : $*(Builtin.BridgeObject, Builtin.Int32) + dealloc_stack %1 : $*(Builtin.BridgeObject, Builtin.Int32) + %7 = tuple () + return %7 : $() +} diff --git a/test/SILOptimizer/mem2reg_ossa_nontrivial_casts.sil b/test/SILOptimizer/mem2reg_ossa_nontrivial_casts.sil new file mode 100644 index 0000000000000..12cd17bad6869 --- /dev/null +++ b/test/SILOptimizer/mem2reg_ossa_nontrivial_casts.sil @@ -0,0 +1,81 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -mem2reg | %FileCheck %s + +import Builtin +import Swift + +// We cannot use unchecked_value_cast for conversions to trivial type. +// Since it is forwarding, the ownersip of the src forwards, but we cannot destroy the dst because it is trivial + +// CHECK-LABEL: sil [ossa] @casttotrivial : +// CHECK: [[CAST:%.*]] = unchecked_bitwise_cast %0 : $AnyObject to $UInt8 +// CHECK-NEXT: destroy_value %0 : $AnyObject +// CHECK-NEXT: return [[CAST]] : $UInt8 +// CHECK-LABEL: } // end sil function 'casttotrivial' +sil [ossa] @casttotrivial : $@convention(thin) (@owned AnyObject) -> @owned UInt8 { +bb0(%0 : @owned $AnyObject): + %1 = alloc_stack $AnyObject + store %0 to [init] %1 : $*AnyObject + %2 = unchecked_addr_cast %1 : $*AnyObject to $*UInt8 + %3 = load [trivial] %2 : $*UInt8 + %4 = load [take] %1 : $*AnyObject + destroy_value %4 : $AnyObject + dealloc_stack %1 : $*AnyObject + return %3 : $UInt8 +} + +// We cannot use unchecked_value_cast, because it is forwarding, it forwards the src, and the src can no longer be used as a RunningVal +// To get rid of this issue we need to use a copy_value of the src, and make sure we don't generate copy_value in case of a load [copy]. +// To avoid all this spl handling, just use bitwise cast + +// CHECK-LABEL: sil [ossa] @casttonontrivial : +// CHECK: [[CAST:%.*]] = unchecked_bitwise_cast %0 : $AnyObject to $String +// CHECK: [[COPY:%.*]] = copy_value [[CAST]] : $String +// CHECK-NEXT: destroy_value %0 : $AnyObject +// CHECK-NEXT: return [[COPY]] : $String +// CHECK-LABEL: } // end sil function 'casttonontrivial' +sil [ossa] @casttonontrivial : $@convention(thin) (@owned AnyObject) -> @owned String { +bb0(%0 : @owned $AnyObject): + %1 = alloc_stack $AnyObject + store %0 to [init] %1 : $*AnyObject + %2 = unchecked_addr_cast %1 : $*AnyObject to $*String + %3 = load [copy] %2 : $*String + %4 = load [take] %1 : $*AnyObject + destroy_value %4 : $AnyObject + dealloc_stack %1 : $*AnyObject + return %3 : $String +} + +struct Pair { var lhs: AnyObject; var rhs: AnyObject } + +// CHECK-LABEL: sil [ossa] @shorteningcast : +// CHECK: [[CAST:%.*]] = unchecked_bitwise_cast %0 : $Pair to $AnyObject +// CHECK: [[COPY:%.*]] = copy_value [[CAST]] : $AnyObject +// CHECK-NEXT: destroy_value %0 : $Pair +// CHECK-NEXT: return [[COPY]] : $AnyObject +// CHECK-LABEL: } // end sil function 'shorteningcast' +sil [ossa] @shorteningcast : $@convention(thin) (@owned Pair) -> @owned AnyObject { +bb0(%0 : @owned $Pair): + %1 = alloc_stack $Pair + store %0 to [init] %1 : $*Pair + %2 = unchecked_addr_cast %1 : $*Pair to $*AnyObject + %3 = load [copy] %2 : $*AnyObject + %4 = load [take] %1 : $*Pair + destroy_value %4 : $Pair + dealloc_stack %1 : $*Pair + return %3 : $AnyObject +} + +// CHECK-LABEL: sil [ossa] @deadcast : +// CHECK-LABEL: bb0 +// CHECK-NEXT: destroy_value %0 : $AnyObject +// CHECK-LABEL: } // end sil function 'deadcast' +sil [ossa] @deadcast : $@convention(thin) (@owned AnyObject) -> () { +bb0(%0 : @owned $AnyObject): + %1 = alloc_stack $AnyObject + store %0 to [init] %1 : $*AnyObject + %2 = unchecked_addr_cast %1 : $*AnyObject to $*String + destroy_addr %1 : $*AnyObject + dealloc_stack %1 : $*AnyObject + %4 = tuple() + return %4 : $() +} diff --git a/test/SILOptimizer/mem2reg_resilient_ossa.sil b/test/SILOptimizer/mem2reg_resilient_ossa.sil new file mode 100644 index 0000000000000..25ee0ef094308 --- /dev/null +++ b/test/SILOptimizer/mem2reg_resilient_ossa.sil @@ -0,0 +1,28 @@ + +// RUN: %target-sil-opt -enable-sil-verify-all %s -mem2reg -enable-library-evolution | %FileCheck %s + +import Builtin +import Swift + +public struct ResilientStruct { + var x: AnyObject +} + +// CHECK-LABEL: sil [ossa] @mem2reg_debug_value_addr : +// CHECK: bb0(%0 : $*ResilientStruct): +// CHECK-NEXT: %1 = load [copy] %0 : $*ResilientStruct +// CHECK-NEXT: debug_value %1 : $ResilientStruct +// CHECK-NEXT: destroy_value %1 : $ResilientStruct +// CHECK-LABEL: } // end sil function 'mem2reg_debug_value_addr' +sil [ossa] @mem2reg_debug_value_addr : $@convention(thin) (@in_guaranteed ResilientStruct) -> () { +bb0(%0 : $*ResilientStruct): + %1 = alloc_stack $ResilientStruct + %2 = load [copy] %0 : $*ResilientStruct + store %2 to [init] %1 : $*ResilientStruct + debug_value_addr %1 : $*ResilientStruct + %3 = load [take] %1 : $*ResilientStruct + destroy_value %3 : $ResilientStruct + dealloc_stack %1 : $*ResilientStruct + %4 = tuple () + return %4 : $() +} diff --git a/test/SILOptimizer/mem2reg_simple_ossa.sil b/test/SILOptimizer/mem2reg_simple_ossa.sil new file mode 100644 index 0000000000000..db5a7ceec3103 --- /dev/null +++ b/test/SILOptimizer/mem2reg_simple_ossa.sil @@ -0,0 +1,410 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -mem2reg | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift + +// func foo(v : Int) -> Int { +// var x : Int = 0 +// if v == 3 { x = 3 } else { +// if (v == 2) { x = 2 } +// } +// var i : Int = 0 +// while (i < 10) { i = i+1 } +// return x +// } + +// CHECK: sil [ossa] @place_phi : +// CHECK-NOT: alloc_stack +sil [ossa] @place_phi : $@convention(thin) (Int64) -> Int64 { +bb0(%0 : $Int64): + %1 = alloc_stack $Int64, var, name "v" + store %0 to [trivial] %1 : $*Int64 + %3 = alloc_stack $Int64, var, name "x" + %4 = integer_literal $Builtin.Int64, 0 + %5 = struct $Int64 (%4 : $Builtin.Int64) + store %5 to [trivial] %3 : $*Int64 + %7 = integer_literal $Builtin.Int64, 3 + %9 = struct_extract %0 : $Int64, #Int64._value + %10 = builtin "cmp_eq_Int64"(%9 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1 + cond_br %10, bb1, bb2 + +bb1: + %12 = struct $Int64 (%7 : $Builtin.Int64) + store %12 to [trivial] %3 : $*Int64 + br bb5 + +bb2: + %15 = integer_literal $Builtin.Int64, 2 + %16 = builtin "cmp_eq_Int64"(%9 : $Builtin.Int64, %15 : $Builtin.Int64) : $Builtin.Int1 + cond_br %16, bb3, bb4 + +bb3: + %18 = struct $Int64 (%15 : $Builtin.Int64) + store %18 to [trivial] %3 : $*Int64 + br bb4 + +bb4: + br bb5 + +// CHECK: bb5([[PHI:%[0-9]+]] : $Int64): +// CHECK-NOT: alloc_stack +bb5: + %22 = alloc_stack $Int64, var, name "i" + store %5 to [trivial] %22 : $*Int64 + br bb6 + +// CHECK: bb6([[PHI2:%[0-9]+]] : $Int64): +bb6: + // CHECK: struct_extract [[PHI2]] + %25 = struct_element_addr %22 : $*Int64, #Int64._value + %26 = load [trivial] %25 : $*Builtin.Int64 + %27 = integer_literal $Builtin.Int64, 10 + %29 = builtin "cmp_slt_Int64"(%26 : $Builtin.Int64, %27 : $Builtin.Int64) : $Builtin.Int1 + cond_br %29, bb7, bb8 + +bb7: + // CHECK: struct_extract [[PHI2]] + %31 = struct_element_addr %22 : $*Int64, #Int64._value + %32 = load [trivial] %31 : $*Builtin.Int64 + %33 = integer_literal $Builtin.Int64, 1 + %35 = builtin "sadd_with_overflow_Int64"(%32 : $Builtin.Int64, %33 : $Builtin.Int64, %29 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) + %36 = tuple_extract %35 : $(Builtin.Int64, Builtin.Int1), 0 + %37 = tuple_extract %35 : $(Builtin.Int64, Builtin.Int1), 1 + %38 = struct $Int64 (%36 : $Builtin.Int64) + cond_fail %37 : $Builtin.Int1 + store %38 to [trivial] %22 : $*Int64 + br bb6 + +bb8: + %42 = load [trivial] %3 : $*Int64 + dealloc_stack %22 : $*Int64 + dealloc_stack %3 : $*Int64 + dealloc_stack %1 : $*Int64 + return %42 : $Int64 +} +// CHECK-LABEL: } // end sil function 'place_phi' + +// func loop(c : Int) -> Int { +// var x : Int = 0 +// while (x < c) { x = x + 1 } +// return x +// } + +// CHECK: sil [ossa] @func_loop : +// CHECK-NOT: alloc_stack +sil [ossa] @func_loop: $@convention(thin) (Int64) -> Int64 { +bb0(%0 : $Int64): + %1 = alloc_stack $Int64, var, name "c" + store %0 to [trivial] %1 : $*Int64 + %3 = alloc_stack $Int64, var, name "x" + %4 = integer_literal $Builtin.Int64, 0 + %5 = struct $Int64 (%4 : $Builtin.Int64) + store %5 to [trivial] %3 : $*Int64 + br bb1 + +// CHECK: bb1([[VAR:%[0-9]+]] : $Int64): +bb1: + %8 = load [trivial] %3 : $*Int64 + %10 = struct_extract %8 : $Int64, #Int64._value + %11 = struct_extract %0 : $Int64, #Int64._value + %12 = builtin "cmp_slt_Int64"(%10 : $Builtin.Int64, %11 : $Builtin.Int64) : $Builtin.Int1 + cond_br %12, bb2, bb3 + +bb2: + %14 = load [trivial] %3 : $*Int64 + %15 = integer_literal $Builtin.Int64, 1 + %16 = integer_literal $Builtin.Int1, -1 + %18 = struct_extract %14 : $Int64, #Int64._value + %19 = builtin "sadd_with_overflow_Int64"(%18 : $Builtin.Int64, %15 : $Builtin.Int64, %16 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) + %20 = tuple_extract %19 : $(Builtin.Int64, Builtin.Int1), 0 + %21 = tuple_extract %19 : $(Builtin.Int64, Builtin.Int1), 1 + %22 = struct $Int64 (%20 : $Builtin.Int64) + cond_fail %21 : $Builtin.Int1 + store %22 to [trivial] %3 : $*Int64 + br bb1 + +bb3: + %26 = load [trivial] %3 : $*Int64 + dealloc_stack %3 : $*Int64 + dealloc_stack %1 : $*Int64 +// CHECK-NOT: dealloc_stack + return %26 : $Int64 +} +// CHECK-LABEL: } // end sil function 'func_loop' + +// func nest(c : Int) -> Int { +// var x : Int = 0 +// if (c > 1) { if (c > 2) { if (c > 3) { if (c > 4) { +// if (c > 5) { if (c > 6) { if (c > 7) { if (c > 8) { +// if (c > 9) { if (c > 10) { if (c > 11) { if (c > 12) { +// if (c > 13) { if (c > 14) { if (c > 15) { if (c > 16) { +// if (c > 17) { x = 7 }}}}}}}}}}}}}}}}} return x +// } + +// This test should kill exponential algorithms. +// CHECK: sil [ossa] @high_nest : +// CHECK-NOT: alloc_stack +// CHECK-NOT: dealloc_stack +// CHECK-LABEL: } // end sil function 'high_nest' +sil [ossa] @high_nest : $@convention(thin) (Int64) -> Int64 { +bb0(%0 : $Int64): + %1 = alloc_stack $Int64, var, name "c" + store %0 to [trivial] %1 : $*Int64 + %3 = alloc_stack $Int64, var, name "x" + %4 = integer_literal $Builtin.Int64, 0 + %5 = struct $Int64 (%4 : $Builtin.Int64) + store %5 to [trivial] %3 : $*Int64 + %7 = integer_literal $Builtin.Int64, 1 + %9 = struct_extract %0 : $Int64, #Int64._value + %10 = builtin "cmp_sgt_Int64"(%9 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1 + cond_br %10, bb1, bb34 + +bb1: + %12 = integer_literal $Builtin.Int64, 2 + %14 = struct_extract %0 : $Int64, #Int64._value + %15 = builtin "cmp_sgt_Int64"(%14 : $Builtin.Int64, %12 : $Builtin.Int64) : $Builtin.Int1 + cond_br %15, bb2, bb33 + +bb2: + %17 = integer_literal $Builtin.Int64, 3 + %19 = struct_extract %0 : $Int64, #Int64._value + %20 = builtin "cmp_sgt_Int64"(%19 : $Builtin.Int64, %17 : $Builtin.Int64) : $Builtin.Int1 + cond_br %20, bb3, bb32 + +bb3: + %22 = integer_literal $Builtin.Int64, 4 + %24 = struct_extract %0 : $Int64, #Int64._value + %25 = builtin "cmp_sgt_Int64"(%24 : $Builtin.Int64, %22 : $Builtin.Int64) : $Builtin.Int1 + cond_br %25, bb4, bb31 + +bb4: + %27 = integer_literal $Builtin.Int64, 5 + %29 = struct_extract %0 : $Int64, #Int64._value + %30 = builtin "cmp_sgt_Int64"(%29 : $Builtin.Int64, %27 : $Builtin.Int64) : $Builtin.Int1 + cond_br %30, bb5, bb30 + +bb5: + %32 = integer_literal $Builtin.Int64, 6 + %34 = struct_extract %0 : $Int64, #Int64._value + %35 = builtin "cmp_sgt_Int64"(%34 : $Builtin.Int64, %32 : $Builtin.Int64) : $Builtin.Int1 + cond_br %35, bb6, bb29 + +bb6: + %37 = integer_literal $Builtin.Int64, 7 + %39 = struct_extract %0 : $Int64, #Int64._value + %40 = builtin "cmp_sgt_Int64"(%39 : $Builtin.Int64, %37 : $Builtin.Int64) : $Builtin.Int1 + cond_br %40, bb7, bb28 + +bb7: + %42 = integer_literal $Builtin.Int64, 8 + %44 = struct_extract %0 : $Int64, #Int64._value + %45 = builtin "cmp_sgt_Int64"(%44 : $Builtin.Int64, %42 : $Builtin.Int64) : $Builtin.Int1 + cond_br %45, bb8, bb27 + +bb8: + %47 = integer_literal $Builtin.Int64, 9 + %49 = struct_extract %0 : $Int64, #Int64._value + %50 = builtin "cmp_sgt_Int64"(%49 : $Builtin.Int64, %47 : $Builtin.Int64) : $Builtin.Int1 + cond_br %50, bb9, bb26 + +bb9: + %52 = integer_literal $Builtin.Int64, 10 + %54 = struct_extract %0 : $Int64, #Int64._value + %55 = builtin "cmp_sgt_Int64"(%54 : $Builtin.Int64, %52 : $Builtin.Int64) : $Builtin.Int1 + cond_br %55, bb10, bb25 + +bb10: + %57 = integer_literal $Builtin.Int64, 11 + %59 = struct_extract %0 : $Int64, #Int64._value + %60 = builtin "cmp_sgt_Int64"(%59 : $Builtin.Int64, %57 : $Builtin.Int64) : $Builtin.Int1 + cond_br %60, bb11, bb24 + +bb11: + %62 = integer_literal $Builtin.Int64, 12 + %64 = struct_extract %0 : $Int64, #Int64._value + %65 = builtin "cmp_sgt_Int64"(%64 : $Builtin.Int64, %62 : $Builtin.Int64) : $Builtin.Int1 + cond_br %65, bb12, bb23 + +bb12: + %67 = integer_literal $Builtin.Int64, 13 + %69 = struct_extract %0 : $Int64, #Int64._value + %70 = builtin "cmp_sgt_Int64"(%69 : $Builtin.Int64, %67 : $Builtin.Int64) : $Builtin.Int1 + cond_br %70, bb13, bb22 + + +bb13: + %72 = integer_literal $Builtin.Int64, 14 + %74 = struct_extract %0 : $Int64, #Int64._value + %75 = builtin "cmp_sgt_Int64"(%74 : $Builtin.Int64, %72 : $Builtin.Int64) : $Builtin.Int1 + cond_br %75, bb14, bb21 + +bb14: + %77 = integer_literal $Builtin.Int64, 15 + %79 = struct_extract %0 : $Int64, #Int64._value + %80 = builtin "cmp_sgt_Int64"(%79 : $Builtin.Int64, %77 : $Builtin.Int64) : $Builtin.Int1 + cond_br %80, bb15, bb20 + +bb15: + %82 = integer_literal $Builtin.Int64, 16 + %84 = struct_extract %0 : $Int64, #Int64._value + %85 = builtin "cmp_sgt_Int64"(%84 : $Builtin.Int64, %82 : $Builtin.Int64) : $Builtin.Int1 + cond_br %85, bb16, bb19 + +bb16: + %87 = integer_literal $Builtin.Int64, 17 + %89 = struct_extract %0 : $Int64, #Int64._value + %90 = builtin "cmp_sgt_Int64"(%89 : $Builtin.Int64, %87 : $Builtin.Int64) : $Builtin.Int1 + cond_br %90, bb17, bb18 + +bb17: + %92 = integer_literal $Builtin.Int64, 7 + %93 = struct $Int64 (%92 : $Builtin.Int64) + store %93 to [trivial] %3 : $*Int64 + br bb18 + +bb18: + br bb19 + +bb19: + br bb20 + +bb20: + br bb21 + +bb21: + br bb22 + +bb22: + br bb23 + +bb23: + br bb24 + +bb24: + br bb25 + +bb25: + br bb26 + +bb26: + br bb27 + +bb27: + br bb28 + +bb28: + br bb29 + +bb29: + br bb30 + +bb30: + br bb31 + +bb31: + br bb32 + +bb32: + br bb33 + +bb33: + br bb34 + +bb34: + %112 = load [trivial] %3 : $*Int64 + dealloc_stack %3 : $*Int64 + dealloc_stack %1 : $*Int64 + return %112 : $Int64 +} + +// CHECK-LABEL: sil [ossa] @simple_if : +// CHECK-NOT: alloc_stack +sil [ossa] @simple_if : $@convention(thin) (Int64) -> Int64 { +bb0(%0 : $Int64): + %1 = alloc_stack $Int64, var, name "c" + store %0 to [trivial] %1 : $*Int64 + %3 = alloc_stack $Int64, var, name "x" + %4 = integer_literal $Builtin.Int64, 0 +// CHECK: [[INIT:%[0-9]+]] = struct $Int64 + %5 = struct $Int64 (%4 : $Builtin.Int64) + store %5 to [trivial] %3 : $*Int64 + %8 = struct_extract %0 : $Int64, #Int64._value + %9 = builtin "cmp_sgt_Int64"(%4 : $Builtin.Int64, %8 : $Builtin.Int64) : $Builtin.Int1 +// CHECK: bb2([[INIT]] : $Int64) + cond_br %9, bb1, bb2 + +bb1: + %11 = integer_literal $Builtin.Int64, 2 +// CHECK: [[INIT2:%[0-9]+]] = struct $Int64 + %12 = struct $Int64 (%11 : $Builtin.Int64) + store %12 to [trivial] %3 : $*Int64 +// CHECK: bb2([[INIT2]] : $Int64) + br bb2 + +bb2: + %15 = load [trivial] %3 : $*Int64 + dealloc_stack %3 : $*Int64 + dealloc_stack %1 : $*Int64 + return %15 : $Int64 +} +// CHECK-LABEL: } // end sil function 'simple_if' + +enum X { + case One + case Two + case Three +} + +// CHECK-LABEL: sil [ossa] @test_switch : +// CHECK-NOT: alloc_stack +// CHECK-NOT: dealloc_stack +sil [ossa] @test_switch: $@convention(thin) (Int64, X) -> Int64 { +bb0(%0 : $Int64, %1 : $X): + %2 = alloc_stack $Int64, var, name "xi" + %3 = alloc_stack $X, var, name "c" + store %0 to [trivial] %2 : $*Int64 + store %1 to [trivial] %3 : $*X + %6 = alloc_stack $Int64, var, name "x" + store %0 to [trivial] %6 : $*Int64 + %8 = tuple () + switch_enum %1 : $X, case #X.One!enumelt: bb1, case #X.Two!enumelt: bb3, case #X.Three!enumelt: bb5 + +bb1: + br bb2 + +bb2: + %11 = integer_literal $Builtin.Int64, 3 + %12 = struct $Int64 (%11 : $Builtin.Int64) + store %12 to [trivial] %6 : $*Int64 + br bb7 + +bb3: + br bb4 + +bb4: + %16 = integer_literal $Builtin.Int64, 2 + %17 = struct $Int64 (%16 : $Builtin.Int64) + store %17 to [trivial] %6 : $*Int64 + br bb7 + +bb5: + br bb6 + +bb6: + %21 = integer_literal $Builtin.Int64, 1 + %22 = struct $Int64 (%21 : $Builtin.Int64) + store %22 to [trivial] %6 : $*Int64 + br bb7 + +bb7: + %25 = load [trivial] %6 : $*Int64 + dealloc_stack %6 : $*Int64 + dealloc_stack %3 : $*X + dealloc_stack %2 : $*Int64 + return %25 : $Int64 +} +// CHECK-LABEL: } // end sil function 'test_switch' + + diff --git a/test/SILOptimizer/mem2reg_unreachable_ossa.sil b/test/SILOptimizer/mem2reg_unreachable_ossa.sil new file mode 100644 index 0000000000000..c6cda0dcd2553 --- /dev/null +++ b/test/SILOptimizer/mem2reg_unreachable_ossa.sil @@ -0,0 +1,68 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -mem2reg + +// Make sure we are not crashing on blocks that are not dominated by the entry block. + +sil_stage canonical + +import Builtin +import Swift + +// CHECK-LABEL: sil [ossa] @_TF4main3fooFT1xSi1ySi_Si : +// CHECK-NEXT: alloc_stack +// CHECK-LABEL: } // end sil function '_TF4main3fooFT1xSi1ySi_Si' +sil [ossa] @_TF4main3fooFT1xSi1ySi_Si : $@convention(thin) (Int32, Int32) -> Int32 { +bb0(%0 : $Int32, %1 : $Int32): + debug_value %0 : $Int32, let, name "x" + debug_value %1 : $Int32, let, name "y" + %4 = alloc_stack $Int32, var, name "g" + %6 = struct_extract %1 : $Int32, #Int32._value + %7 = struct_extract %0 : $Int32, #Int32._value + %8 = builtin "cmp_sgt_Word"(%6 : $Builtin.Int32, %7 : $Builtin.Int32) : $Builtin.Int1 + %9 = struct $Bool (%8 : $Builtin.Int1) + %10 = struct_extract %9 : $Bool, #Bool._value + br bb2 + +// bb1 is unreachable +// CHECK-LABEL: bb1: +// CHECK-NEXT: br bb3 +bb1: + %12 = integer_literal $Builtin.Int32, 5 + %13 = struct $Int32 (%12 : $Builtin.Int32) + store %13 to [trivial] %4 : $*Int32 + br bb3 + +bb2: + %16 = integer_literal $Builtin.Int32, 4 + %17 = struct $Int32 (%16 : $Builtin.Int32) + store %17 to [trivial] %4 : $*Int32 + br bb3 + +bb3: + %20 = load [trivial] %4 : $*Int32 + dealloc_stack %4 : $*Int32 + return %20 : $Int32 +} + +struct S {} + +// CHECK-LABEL: sil hidden [ossa] @handle_unreachable : +// CHECK-NOT: alloc_stack +// CHECK: debug_value undef : $S, let, name "newvalue", argno 1 +// CHECK-LABEL: } // end sil function 'handle_unreachable' +sil hidden [ossa] @handle_unreachable : $@convention(thin) () -> () { +bb0: + %0 = alloc_stack $S, var, name "x" + %1 = struct $S () + store %1 to [trivial] %0 : $*S + unreachable + +bb1: + debug_value_addr %0 : $*S, let, name "newvalue", argno 1 + br bb2 + +bb2: + %3 = load [trivial] %0 : $*S + dealloc_stack %0 : $*S + %4 = tuple () + return %4 : $() +} From 0ea5d055a20e2066ba1e034d52b1dead007a4dab Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Mon, 12 Oct 2020 11:20:27 -0700 Subject: [PATCH 601/745] Delete debug_value_addr of stack location, if a debug_value of the RunningVal is already found --- lib/SILOptimizer/Transforms/SILMem2Reg.cpp | 4 +++- test/SILOptimizer/mem2reg_ossa_nontrivial.sil | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp index e0b43fb8e24ac..4c2db62703e36 100644 --- a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp +++ b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp @@ -323,8 +323,10 @@ promoteDebugValueAddr(DebugValueAddrInst *DVAI, SILValue Value, SILBuilder &B) { // Avoid inserting the same debug_value twice. for (Operand *Use : Value->getUses()) if (auto *DVI = dyn_cast(Use->getUser())) - if (*DVI->getVarInfo() == *DVAI->getVarInfo()) + if (*DVI->getVarInfo() == *DVAI->getVarInfo()) { + DVAI->eraseFromParent(); return; + } B.setInsertionPoint(DVAI); B.setCurrentDebugScope(DVAI->getDebugScope()); B.createDebugValue(DVAI->getLoc(), Value, *DVAI->getVarInfo()); diff --git a/test/SILOptimizer/mem2reg_ossa_nontrivial.sil b/test/SILOptimizer/mem2reg_ossa_nontrivial.sil index 33f643644f8ed..5a05d7202733f 100644 --- a/test/SILOptimizer/mem2reg_ossa_nontrivial.sil +++ b/test/SILOptimizer/mem2reg_ossa_nontrivial.sil @@ -613,3 +613,20 @@ bb0(%0 : @owned $(Builtin.BridgeObject, Builtin.Int32)): %7 = tuple () return %7 : $() } + +// CHECK-LABEL: sil [ossa] @multiple_debug_value : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'multiple_debug_value' +sil [ossa] @multiple_debug_value : $@convention(thin) (@owned Klass) -> () { +bb0(%0 : @owned $Klass): + debug_value %0 : $Klass + %2 = alloc_stack $Klass + store %0 to [init] %2 : $*Klass + debug_value_addr %2 : $*Klass + %5 = load [take] %2 : $*Klass + destroy_value %5 : $Klass + dealloc_stack %2 : $*Klass + %7 = tuple () + return %7 : $() +} + From 06eaf6bba4de85631c532cb9bb9cd232758706c4 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Mon, 12 Oct 2020 11:21:01 -0700 Subject: [PATCH 602/745] Disable SILCombine of unchecked_bitwise_cast to unchecked_ref_cast in OSSA unchecked_ref_cast is a forwarding cast while unchecked_bitwise_cast is not. We cannot just convert one to other in OSSA. Disable it now. --- .../SILCombiner/SILCombinerCastVisitors.cpp | 3 +++ test/SILOptimizer/sil_combine.sil | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp index 4230ceaa37d28..35735ba6c9c5c 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp @@ -472,6 +472,9 @@ visitUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *UTBCI) { SILInstruction * SILCombiner:: visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *UBCI) { + if (UBCI->getFunction()->hasOwnership()) + return nullptr; + // (unchecked_bitwise_cast Y->Z (unchecked_bitwise_cast X->Y x)) // OR (unchecked_trivial_cast Y->Z (unchecked_bitwise_cast X->Y x)) // -> diff --git a/test/SILOptimizer/sil_combine.sil b/test/SILOptimizer/sil_combine.sil index 23978c8b8d17c..cdea966bd7e03 100644 --- a/test/SILOptimizer/sil_combine.sil +++ b/test/SILOptimizer/sil_combine.sil @@ -8,6 +8,7 @@ sil_stage canonical import Builtin import Swift +class Klass {} class RawBuffer {} class HeapBufferStorage : RawBuffer {} @@ -4116,3 +4117,16 @@ bb0: %1 = load %0 : $*Int64 return %1 : $Int64 } + +// Check for disabled optimization of unchecked_bitwise_cast to unchecked_ref_cast in ossa +// This test can be optimized when ossa is supported in the SILCombine for unchecked_bitwise_cast +// CHECK-LABEL: sil [ossa] @refcast : +// CHECK: unchecked_bitwise_cast +// CHECK-LABEL: } // end sil function 'refcast' +sil [ossa] @refcast : $@convention(thin) (@owned Klass) -> @owned Optional { +bb0(%0 : @owned $Klass): + %1 = unchecked_bitwise_cast %0 : $Klass to $Optional + %2 = copy_value %1 : $Optional + destroy_value %0 : $Klass + return %2 : $Optional +} From 0d568a93d46973ac29eb1d0f4311e7ffd11df485 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 20 Oct 2020 21:44:09 -0700 Subject: [PATCH 603/745] [SE-0289] Update diagnostics & many other strings to "result builders" --- docs/LibraryEvolution.rst | 2 +- include/swift/AST/Attr.def | 2 +- include/swift/AST/Decl.h | 2 +- include/swift/AST/DiagnosticsSema.def | 102 +++++++++--------- include/swift/AST/EducationalNotes.def | 10 +- include/swift/AST/Expr.h | 2 +- include/swift/AST/TypeCheckRequests.h | 4 +- include/swift/Sema/CSFix.h | 8 +- include/swift/Sema/ConstraintLocator.h | 2 +- .../swift/Sema/ConstraintLocatorPathElts.def | 2 +- include/swift/Sema/ConstraintSystem.h | 20 ++-- include/swift/Sema/IDETypeChecking.h | 6 +- lib/AST/Attr.cpp | 8 +- lib/IDE/CodeCompletion.cpp | 10 +- lib/Parse/ParseDecl.cpp | 2 +- lib/SILGen/SILGenConstructor.cpp | 2 +- lib/Sema/BuilderTransform.cpp | 66 ++++++------ lib/Sema/CSClosure.cpp | 4 +- lib/Sema/CSDiagnostics.cpp | 16 +-- lib/Sema/CSFix.cpp | 2 +- lib/Sema/CSGen.cpp | 4 +- lib/Sema/CSSimplify.cpp | 10 +- lib/Sema/CSSolver.cpp | 2 +- lib/Sema/CodeSynthesis.cpp | 2 +- lib/Sema/ConstraintLocator.cpp | 2 +- lib/Sema/TypeCheckAttr.cpp | 28 ++--- lib/Sema/TypeCheckCodeCompletion.cpp | 2 +- lib/Sema/TypeCheckConstraints.cpp | 2 +- lib/Sema/TypeCheckDeclOverride.cpp | 2 +- lib/Sema/TypeCheckRequestFunctions.cpp | 38 +++---- lib/Sema/TypeChecker.h | 4 +- test/Constraints/function_builder.swift | 6 +- .../function_builder_availability.swift | 4 +- test/Constraints/function_builder_diags.swift | 30 +++--- test/Constraints/function_builder_infer.swift | 32 +++--- test/Constraints/sr13183.swift | 2 +- test/IDE/complete_ambiguous.swift | 2 +- test/IDE/complete_function_builder.swift | 50 ++++----- test/ModuleInterface/function_builders.swift | 2 +- .../function_builder_curry_thunks.swift | 33 ------ test/SILGen/result_builder_curry_thunks.swift | 33 ++++++ ....swift => result_builder_memberwise.swift} | 2 +- ...n_builders.swift => result_builders.swift} | 2 +- test/decl/function_builder_fixits.swift | 14 +-- test/decl/var/function_builders.swift | 52 ++++----- ....swift => result_builder_definition.swift} | 0 .../function_builder_multifile.swift | 9 -- test/multifile/result_builder_multifile.swift | 9 ++ .../diagnostics/function-builder-methods.md | 10 +- 49 files changed, 330 insertions(+), 330 deletions(-) delete mode 100644 test/SILGen/function_builder_curry_thunks.swift create mode 100644 test/SILGen/result_builder_curry_thunks.swift rename test/SILGen/{function_builder_memberwise.swift => result_builder_memberwise.swift} (84%) rename test/Serialization/{function_builders.swift => result_builders.swift} (90%) rename test/multifile/Inputs/{function_builder_definition.swift => result_builder_definition.swift} (100%) delete mode 100644 test/multifile/function_builder_multifile.swift create mode 100644 test/multifile/result_builder_multifile.swift diff --git a/docs/LibraryEvolution.rst b/docs/LibraryEvolution.rst index dac3495fe893a..1fa6741c5e2c0 100644 --- a/docs/LibraryEvolution.rst +++ b/docs/LibraryEvolution.rst @@ -159,7 +159,7 @@ No other changes are permitted; the following are particularly of note: not they have default arguments. - An ABI-public function that throws may not become non-throwing or vice versa. - The ``@escaping`` attribute may not be added to or removed from a parameter. -- Adding or removing a function builder from a parameter is a +- Adding or removing a result builder from a parameter is a `binary-compatible source-breaking change`. diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 685f2a9a14ebe..5f5c30aa73b32 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -493,7 +493,7 @@ SIMPLE_DECL_ATTR(_disfavoredOverload, DisfavoredOverload, OnAbstractFunction | OnVar | OnSubscript | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, 87) -SIMPLE_DECL_ATTR(resultBuilder, FunctionBuilder, +SIMPLE_DECL_ATTR(resultBuilder, ResultBuilder, OnNominalType | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, 88) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index c36f461da6c92..fd5d157328818 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -2402,7 +2402,7 @@ class ValueDecl : public Decl { OpaqueReturnTypeRepr *getOpaqueResultTypeRepr() const; /// Retrieve the attribute associating this declaration with a - /// function builder, if there is one. + /// result builder, if there is one. CustomAttr *getAttachedFunctionBuilder() const; /// Retrieve the @functionBuilder type attached to this declaration, diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 6feed045acaf1..9f0dcf1acf124 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5352,83 +5352,83 @@ ERROR(property_wrapper_missing_arg_init, none, "missing argument for parameter " "arguments in '@%1(...)'", (Identifier, StringRef)) //------------------------------------------------------------------------------ -// MARK: function builder diagnostics +// MARK: result builder diagnostics //------------------------------------------------------------------------------ -ERROR(function_builder_decl, none, - "closure containing a declaration cannot be used with function " +ERROR(result_builder_decl, none, + "closure containing a declaration cannot be used with result " "builder %0", (DeclName)) -NOTE(note_function_builder_decl, none, - "closure containing a declaration cannot be used with function " +NOTE(note_result_builder_decl, none, + "closure containing a declaration cannot be used with result " "builder %0", (DeclName)) -ERROR(function_builder_control_flow, none, - "closure containing control flow statement cannot be used with function " +ERROR(result_builder_control_flow, none, + "closure containing control flow statement cannot be used with result " "builder %0", (DeclName)) -NOTE(note_function_builder_control_flow, none, - "closure containing control flow statement cannot be used with function " +NOTE(note_result_builder_control_flow, none, + "closure containing control flow statement cannot be used with result " "builder %0", (DeclName)) -ERROR(function_builder_attribute_not_allowed_here, none, - "function builder attribute %0 can only be applied to a parameter, " +ERROR(result_builder_attribute_not_allowed_here, none, + "result builder attribute %0 can only be applied to a parameter, " "function, or computed property", (Identifier)) -ERROR(function_builder_attribute_on_storage_without_getter, none, - "function builder attribute %0 can only be applied to a " +ERROR(result_builder_attribute_on_storage_without_getter, none, + "result builder attribute %0 can only be applied to a " "%select{subscript|property|constant|variable}1 if it defines a getter", (DeclName, unsigned)) -ERROR(function_builder_parameter_not_of_function_type, none, - "function builder attribute %0 can only be applied to a parameter of " +ERROR(result_builder_parameter_not_of_function_type, none, + "result builder attribute %0 can only be applied to a parameter of " "function type", (Identifier)) -ERROR(function_builder_parameter_autoclosure, none, - "function builder attribute %0 cannot be applied to an autoclosure " +ERROR(result_builder_parameter_autoclosure, none, + "result builder attribute %0 cannot be applied to an autoclosure " "parameter", (Identifier)) -ERROR(function_builder_multiple, none, - "only one function builder attribute can be attached to a " +ERROR(result_builder_multiple, none, + "only one result builder attribute can be attached to a " "%select{declaration|parameter}0", (bool)) -NOTE(previous_function_builder_here, none, - "previous function builder specified here", ()) -ERROR(function_builder_arguments, none, - "function builder attributes cannot have arguments", ()) -WARNING(function_builder_disabled_by_return, none, - "application of function builder %0 disabled by explicit 'return' " +NOTE(previous_result_builder_here, none, + "previous result builder specified here", ()) +ERROR(result_builder_arguments, none, + "result builder attributes cannot have arguments", ()) +WARNING(result_builder_disabled_by_return, none, + "application of result builder %0 disabled by explicit 'return' " "statement", (Type)) -NOTE(function_builder_remove_attr, none, - "remove the attribute to explicitly disable the function builder", ()) -NOTE(function_builder_remove_returns, none, - "remove 'return' statements to apply the function builder", ()) -ERROR(function_builder_infer_ambig, none, - "ambiguous function builder inferred for %0: %1 or %2", +NOTE(result_builder_remove_attr, none, + "remove the attribute to explicitly disable the result builder", ()) +NOTE(result_builder_remove_returns, none, + "remove 'return' statements to apply the result builder", ()) +ERROR(result_builder_infer_ambig, none, + "ambiguous result builder inferred for %0: %1 or %2", (DeclName, Type, Type)) -NOTE(function_builder_infer_add_return, none, - "add an explicit 'return' statement to not use a function builder", ()) -NOTE(function_builder_infer_pick_specific, none, - "apply function builder %0 (inferred from %select{protocol|dynamic replacement of}1 %2)", +NOTE(result_builder_infer_add_return, none, + "add an explicit 'return' statement to not use a result builder", ()) +NOTE(result_builder_infer_pick_specific, none, + "apply result builder %0 (inferred from %select{protocol|dynamic replacement of}1 %2)", (Type, unsigned, DeclName)) -WARNING(function_builder_missing_limited_availability, none, - "function builder %0 does not implement 'buildLimitedAvailability'; " +WARNING(result_builder_missing_limited_availability, none, + "result builder %0 does not implement 'buildLimitedAvailability'; " "this code may crash on earlier versions of the OS", (Type)) -ERROR(function_builder_static_buildblock, none, - "function builder must provide at least one static 'buildBlock' " +ERROR(result_builder_static_buildblock, none, + "result builder must provide at least one static 'buildBlock' " "method", ()) -NOTE(function_builder_non_static_buildblock, none, +NOTE(result_builder_non_static_buildblock, none, "did you mean to make instance method 'buildBlock' static?", ()) -NOTE(function_builder_buildblock_enum_case, none, - "enum case 'buildBlock' cannot be used to satisfy the function builder " +NOTE(result_builder_buildblock_enum_case, none, + "enum case 'buildBlock' cannot be used to satisfy the result builder " "requirement", ()) -NOTE(function_builder_buildblock_not_static_method, none, +NOTE(result_builder_buildblock_not_static_method, none, "potential match 'buildBlock' is not a static method", ()) -NOTE(function_builder_missing_build_optional, none, - "add 'buildOptional(_:)' to the function builder %0 to add support for " +NOTE(result_builder_missing_build_optional, none, + "add 'buildOptional(_:)' to the result builder %0 to add support for " "'if' statements without an 'else'", (Type)) -NOTE(function_builder_missing_build_either, none, - "add 'buildEither(first:)' and 'buildEither(second:)' to the function " +NOTE(result_builder_missing_build_either, none, + "add 'buildEither(first:)' and 'buildEither(second:)' to the result " "builder %0 to add support for 'if'-'else' and 'switch'", (Type)) -NOTE(function_builder_missing_build_array, none, - "add 'buildArray(_:)' to the function builder %0 to add support for " +NOTE(result_builder_missing_build_array, none, + "add 'buildArray(_:)' to the result builder %0 to add support for " "'for'..'in' loops", (Type)) -NOTE(function_builder_missing_build_limited_availability, none, - "add 'buildLimitedAvailability(_:)' to the function " +NOTE(result_builder_missing_build_limited_availability, none, + "add 'buildLimitedAvailability(_:)' to the result " "builder %0 to erase type information for less-available types", (Type)) //------------------------------------------------------------------------------ diff --git a/include/swift/AST/EducationalNotes.def b/include/swift/AST/EducationalNotes.def index 720eebade0c55..7a79d86239386 100644 --- a/include/swift/AST/EducationalNotes.def +++ b/include/swift/AST/EducationalNotes.def @@ -81,15 +81,15 @@ EDUCATIONAL_NOTES(type_cannot_conform, "protocol-type-non-conformance.md") EDUCATIONAL_NOTES(unlabeled_trailing_closure_deprecated, "trailing-closure-matching.md") -EDUCATIONAL_NOTES(function_builder_static_buildblock, +EDUCATIONAL_NOTES(result_builder_static_buildblock, "function-builder-methods.md") -EDUCATIONAL_NOTES(function_builder_missing_limited_availability, +EDUCATIONAL_NOTES(result_builder_missing_limited_availability, "function-builder-methods.md") -EDUCATIONAL_NOTES(function_builder_missing_build_optional, +EDUCATIONAL_NOTES(result_builder_missing_build_optional, "function-builder-methods.md") -EDUCATIONAL_NOTES(function_builder_missing_build_either, +EDUCATIONAL_NOTES(result_builder_missing_build_either, "function-builder-methods.md") -EDUCATIONAL_NOTES(function_builder_missing_build_array, +EDUCATIONAL_NOTES(result_builder_missing_build_array, "function-builder-methods.md") #undef EDUCATIONAL_NOTES diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 1d5205dcb48f3..30934bb682671 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -3695,7 +3695,7 @@ class ClosureExpr : public AbstractClosureExpr { ReadyForTypeChecking, /// The body was typechecked with the enclosing closure. - /// i.e. single expression closure or function builder closure. + /// i.e. single expression closure or result builder closure. TypeCheckedWithSignature, /// The body was type checked separately from the enclosing closure. diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 9a667ecebe218..187bc9c47c1f3 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -735,7 +735,7 @@ void simple_display(llvm::raw_ostream &out, FragileFunctionKind value); void simple_display(llvm::raw_ostream &out, ResilienceExpansion value); -/// Request the custom attribute which attaches a function builder to the +/// Request the custom attribute which attaches a result builder to the /// given declaration. class AttachedFunctionBuilderRequest : public SimpleRequest> Conformances; - /// The set of functions that have been transformed by a function builder. + /// The set of functions that have been transformed by a result builder. llvm::MapVector functionBuilderTransformed; @@ -2125,11 +2125,11 @@ class ConstraintSystem { /// from declared parameters/result and body. llvm::MapVector ClosureTypes; - /// This is a *global* list of all function builder bodies that have + /// This is a *global* list of all result builder bodies that have /// been determined to be incorrect by failing constraint generation. /// /// Tracking this information is useful to avoid producing duplicate - /// diagnostics when function builder has multiple overloads. + /// diagnostics when result builder has multiple overloads. llvm::SmallDenseSet InvalidFunctionBuilderBodies; /// Maps node types used within all portions of the constraint @@ -2218,7 +2218,7 @@ class ConstraintSystem { std::vector> CheckedConformances; - /// The set of functions that have been transformed by a function builder. + /// The set of functions that have been transformed by a result builder. std::vector> functionBuilderTransformed; @@ -2555,7 +2555,7 @@ class ConstraintSystem { case ConstraintSystemPhase::Solving: // We can come back to constraint generation phase while - // processing function builder body. + // processing result builder body. assert(newPhase == ConstraintSystemPhase::ConstraintGeneration || newPhase == ConstraintSystemPhase::Diagnostics || newPhase == ConstraintSystemPhase::Finalization); @@ -4603,10 +4603,10 @@ class ConstraintSystem { /// Simplify the given disjunction choice. void simplifyDisjunctionChoice(Constraint *choice); - /// Apply the given function builder to the closure expression. + /// Apply the given result builder to the closure expression. /// - /// \returns \c None when the function builder cannot be applied at all, - /// otherwise the result of applying the function builder. + /// \returns \c None when the result builder cannot be applied at all, + /// otherwise the result of applying the result builder. Optional matchFunctionBuilder( AnyFunctionRef fn, Type builderType, Type bodyResultType, ConstraintKind bodyResultConstraintKind, @@ -5990,7 +5990,7 @@ bool isSIMDOperator(ValueDecl *value); std::string describeGenericType(ValueDecl *GP, bool includeName = false); -/// Apply the given function builder transform within a specific solution +/// Apply the given result builder transform within a specific solution /// to produce the rewritten body. /// /// \param solution The solution to use during application, providing the diff --git a/include/swift/Sema/IDETypeChecking.h b/include/swift/Sema/IDETypeChecking.h index 23ff7e450c6ad..83ab16d1738a2 100644 --- a/include/swift/Sema/IDETypeChecking.h +++ b/include/swift/Sema/IDETypeChecking.h @@ -268,11 +268,11 @@ namespace swift { BuildFinalResult, }; - /// Try to infer the component type of a function builder from the type + /// Try to infer the component type of a result builder from the type /// of buildBlock or buildExpression, if it was there. Type inferFunctionBuilderComponentType(NominalTypeDecl *builder); - /// Print the declaration for a function builder "build" function, for use + /// Print the declaration for a result builder "build" function, for use /// in Fix-Its, code completion, and so on. void printFunctionBuilderBuildFunction( NominalTypeDecl *builder, Type componentType, @@ -280,7 +280,7 @@ namespace swift { Optional stubIndent, llvm::raw_ostream &out); /// Compute the insertion location, indentation string, and component type - /// for a Fix-It that adds a new build* function to a function builder. + /// for a Fix-It that adds a new build* function to a result builder. std::tuple determineFunctionBuilderBuildFixItInfo(NominalTypeDecl *builder); } diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 8920fd7c6c59c..fb1e31f6a9d6a 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -636,12 +636,12 @@ void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options, FuncBuilderAttr = VD->getAttachedFunctionBuilder(); } for (auto DA : llvm::reverse(FlattenedAttrs)) { - // Always print function builder attribute. - bool isFunctionBuilderAttr = DA == FuncBuilderAttr; + // Always print result builder attribute. + bool isResultBuilderAttr = DA == FuncBuilderAttr; if (!Options.PrintImplicitAttrs && DA->isImplicit()) continue; if (!Options.PrintUserInaccessibleAttrs && - !isFunctionBuilderAttr && + !isResultBuilderAttr && DeclAttribute::isUserInaccessible(DA->getKind())) continue; if (Options.excludeAttrKind(DA->getKind())) @@ -740,7 +740,7 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, case DAK_Custom: { if (!Options.IsForSwiftInterface) break; - // For Swift interface, we should print function builder attributes + // For Swift interface, we should print result builder attributes // on parameter decls and on protocol requirements. // Printing the attribute elsewhere isn't ABI relevant. if (auto *VD = dyn_cast(D)) { diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 04652d841150d..dfbbff44f9587 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -5128,11 +5128,11 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { FunctionBuilderBuildFunction function) { switch (function) { case FunctionBuilderBuildFunction::BuildArray: - return "Enables support for..in loops in a function builder by " + return "Enables support for..in loops in a result builder by " "combining the results of all iterations into a single result"; case FunctionBuilderBuildFunction::BuildBlock: - return "Required by every function builder to build combined results " + return "Required by every result builder to build combined results " "from statement blocks"; case FunctionBuilderBuildFunction::BuildEitherFirst: @@ -5155,7 +5155,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { case FunctionBuilderBuildFunction::BuildLimitedAvailability: return "If declared, this will be called on the partial result of " - "an 'if #available' block to allow the function builder to erase " + "an 'if #available' block to allow the result builder to erase " "type information"; case FunctionBuilderBuildFunction::BuildOptional: @@ -5195,7 +5195,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { Builder.setBriefDocComment(getFunctionBuilderDocComment(function)); } - /// Add completions for the various "build" functions in a function builder. + /// Add completions for the various "build" functions in a result builder. void addFunctionBuilderBuildCompletions(NominalTypeDecl *builder) { Type componentType = inferFunctionBuilderComponentType(builder); @@ -5237,7 +5237,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { addAssociatedTypes(NTD); } - if (NTD && NTD->getAttrs().hasAttribute()) { + if (NTD && NTD->getAttrs().hasAttribute()) { addFunctionBuilderBuildCompletions(NTD); } } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index dbf2a7df088fa..6c1da992b19ee 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2710,7 +2710,7 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc At // Historical name for result builders. checkInvalidAttrName( - "_functionBuilder", "resultBuilder", DAK_FunctionBuilder, + "_functionBuilder", "resultBuilder", DAK_ResultBuilder, diag::attr_renamed_warning); if (DK == DAK_Count && Tok.getText() == "warn_unused_result") { diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 84cd1816d8924..1efe9b6340b0f 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -216,7 +216,7 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, RValue arg = std::move(*elti); - // If the stored property has an attached function builder and its + // If the stored property has an attached result builder and its // type is not a function type, the argument is a noescape closure // that needs to be called. if (field->getFunctionBuilderType()) { diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 7bde9305edb86..6b85c1db3ca60 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -251,7 +251,7 @@ class BuilderClosureVisitor return std::move(applied); } - /// Check whether the function builder can be applied to this statement. + /// Check whether the result builder can be applied to this statement. /// \returns the node that cannot be handled by this builder on failure. SkipUnhandledConstructInFunctionBuilder::UnhandledNode check(Stmt *stmt) { (void)visit(stmt); @@ -863,7 +863,7 @@ class BuilderClosureVisitor } // Form a final variable for the for-each expression itself, which will - // be initialized with the call to the function builder's buildArray(_:). + // be initialized with the call to the result builder's buildArray(_:). auto finalForEachVar = buildVar(loc); cs->setType(finalForEachVar, cs->getType(buildArrayCall)); applied.capturedStmts.insert( @@ -909,7 +909,7 @@ class BuilderClosureVisitor }; /// Describes the target into which the result of a particular statement in -/// a closure involving a function builder should be written. +/// a closure involving a result builder should be written. struct FunctionBuilderTarget { enum Kind { /// The resulting value is returned from the closure. @@ -938,7 +938,7 @@ struct FunctionBuilderTarget { } }; -/// Handles the rewrite of the body of a closure to which a function builder +/// Handles the rewrite of the body of a closure to which a result builder /// has been applied. class BuilderClosureRewriter : public StmtVisitor { @@ -1045,7 +1045,7 @@ class BuilderClosureRewriter // Execute the expression. return rewriteExpr(capturedExpr); } - llvm_unreachable("invalid function builder target"); + llvm_unreachable("invalid result builder target"); } /// Declare the given temporary variable, adding the appropriate @@ -1232,7 +1232,7 @@ class BuilderClosureRewriter // // Note that this is for staging in support for buildLimitedAvailability(); // the diagnostic is currently a warning, so that existing code that - // compiles today will continue to compile. Once function builder types + // compiles today will continue to compile. Once result builder types // have had the chance to adopt buildLimitedAvailability(), we'll upgrade // this warning to an error. if (auto availabilityCond = findAvailabilityCondition(ifStmt->getCond())) { @@ -1247,10 +1247,10 @@ class BuilderClosureRewriter if (auto reason = TypeChecker::checkDeclarationAvailability( nominal, loc, dc)) { ctx.Diags.diagnose( - loc, diag::function_builder_missing_limited_availability, + loc, diag::result_builder_missing_limited_availability, builderTransform.builderType); - // Add a note to the function builder with a stub for + // Add a note to the result builder with a stub for // buildLimitedAvailability(). if (auto builder = builderTransform.builderType->getAnyNominal()) { SourceLoc buildInsertionLoc; @@ -1268,7 +1268,7 @@ class BuilderClosureRewriter stubIndent, out); builder->diagnose( - diag::function_builder_missing_build_limited_availability, + diag::result_builder_missing_build_limited_availability, builderTransform.builderType) .fixItInsert(buildInsertionLoc, fixItString); } @@ -1451,7 +1451,7 @@ class BuilderClosureRewriter // Step 3. Perform the buildArray() call to turn the array of results // collected from the iterations into a single value under the control of - // the function builder. + // the result builder. outerBodySteps.push_back( initializeTarget( FunctionBuilderTarget::forAssign(finalForEachVar, {buildArrayCall}))); @@ -1478,25 +1478,25 @@ class BuilderClosureRewriter llvm_unreachable("Throw statements produce no value"); } -#define UNHANDLED_FUNCTION_BUILDER_STMT(STMT) \ +#define UNHANDLED_RESULT_BUILDER_STMT(STMT) \ Stmt *visit##STMT##Stmt(STMT##Stmt *stmt, FunctionBuilderTarget target) { \ llvm_unreachable("Function builders do not allow statement of kind " \ #STMT); \ } - UNHANDLED_FUNCTION_BUILDER_STMT(Return) - UNHANDLED_FUNCTION_BUILDER_STMT(Yield) - UNHANDLED_FUNCTION_BUILDER_STMT(Guard) - UNHANDLED_FUNCTION_BUILDER_STMT(While) - UNHANDLED_FUNCTION_BUILDER_STMT(Defer) - UNHANDLED_FUNCTION_BUILDER_STMT(DoCatch) - UNHANDLED_FUNCTION_BUILDER_STMT(RepeatWhile) - UNHANDLED_FUNCTION_BUILDER_STMT(Break) - UNHANDLED_FUNCTION_BUILDER_STMT(Continue) - UNHANDLED_FUNCTION_BUILDER_STMT(Fallthrough) - UNHANDLED_FUNCTION_BUILDER_STMT(Fail) - UNHANDLED_FUNCTION_BUILDER_STMT(PoundAssert) -#undef UNHANDLED_FUNCTION_BUILDER_STMT + UNHANDLED_RESULT_BUILDER_STMT(Return) + UNHANDLED_RESULT_BUILDER_STMT(Yield) + UNHANDLED_RESULT_BUILDER_STMT(Guard) + UNHANDLED_RESULT_BUILDER_STMT(While) + UNHANDLED_RESULT_BUILDER_STMT(Defer) + UNHANDLED_RESULT_BUILDER_STMT(DoCatch) + UNHANDLED_RESULT_BUILDER_STMT(RepeatWhile) + UNHANDLED_RESULT_BUILDER_STMT(Break) + UNHANDLED_RESULT_BUILDER_STMT(Continue) + UNHANDLED_RESULT_BUILDER_STMT(Fallthrough) + UNHANDLED_RESULT_BUILDER_STMT(Fail) + UNHANDLED_RESULT_BUILDER_STMT(PoundAssert) +#undef UNHANDLED_RESULT_BUILDER_STMT }; } // end anonymous namespace @@ -1541,15 +1541,15 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( case FunctionBuilderBodyPreCheck::HasReturnStmt: { // One or more explicit 'return' statements were encountered, which - // disables the function builder transform. Warn when we do this. + // disables the result builder transform. Warn when we do this. auto returnStmts = findReturnStatements(func); assert(!returnStmts.empty()); ctx.Diags.diagnose( returnStmts.front()->getReturnLoc(), - diag::function_builder_disabled_by_return, builderType); + diag::result_builder_disabled_by_return, builderType); - // Note that one can remove the function builder attribute. + // Note that one can remove the result builder attribute. auto attr = func->getAttachedFunctionBuilder(); if (!attr) { if (auto accessor = dyn_cast(func)) { @@ -1559,7 +1559,7 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( if (attr) { ctx.Diags.diagnose( - attr->getLocation(), diag::function_builder_remove_attr) + attr->getLocation(), diag::result_builder_remove_attr) .fixItRemove(attr->getRangeWithAt()); attr->setInvalid(); } @@ -1568,7 +1568,7 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( { auto diag = ctx.Diags.diagnose( returnStmts.front()->getReturnLoc(), - diag::function_builder_remove_returns); + diag::result_builder_remove_returns); for (auto returnStmt : returnStmts) { diag.fixItRemove(returnStmt->getReturnLoc()); } @@ -1658,8 +1658,8 @@ ConstraintSystem::matchFunctionBuilder( ConstraintKind bodyResultConstraintKind, ConstraintLocatorBuilder locator) { auto builder = builderType->getAnyNominal(); - assert(builder && "Bad function builder type"); - assert(builder->getAttrs().hasAttribute()); + assert(builder && "Bad result builder type"); + assert(builder->getAttrs().hasAttribute()); if (InvalidFunctionBuilderBodies.count(fn)) { (void)recordFix( @@ -1699,12 +1699,12 @@ ConstraintSystem::matchFunctionBuilder( // function-builder translation at all. auto dc = fn.getAsDeclContext(); { - // Check whether we can apply this specific function builder. + // Check whether we can apply this specific result builder. BuilderClosureVisitor visitor(getASTContext(), nullptr, dc, builderType, bodyResultType); // If we saw a control-flow statement or declaration that the builder - // cannot handle, we don't have a well-formed function builder application. + // cannot handle, we don't have a well-formed result builder application. if (auto unhandledNode = visitor.check(fn.getBody())) { // If we aren't supposed to attempt fixes, fail. if (!shouldAttemptFixes()) { diff --git a/lib/Sema/CSClosure.cpp b/lib/Sema/CSClosure.cpp index f6a26225b7923..0ee0674cd7524 100644 --- a/lib/Sema/CSClosure.cpp +++ b/lib/Sema/CSClosure.cpp @@ -313,9 +313,9 @@ SolutionApplicationToFunctionResult ConstraintSystem::applySolution( // transformations. llvm::SaveAndRestore savedDC(currentDC, fn.getAsDeclContext()); - // Apply the function builder transform, if there is one. + // Apply the result builder transform, if there is one. if (auto transform = solution.getAppliedBuilderTransform(fn)) { - // Apply the function builder to the closure. We want to be in the + // Apply the result builder to the closure. We want to be in the // context of the closure for subsequent transforms. auto newBody = applyFunctionBuilderTransform( solution, *transform, fn.getBody(), fn.getAsDeclContext(), diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index ac67fde1b76d1..b347887e4c022 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -242,7 +242,7 @@ ValueDecl *RequirementFailure::getDeclRef() const { return type->getAnyGeneric(); }; - // If the locator is for a function builder body result type, the requirement + // If the locator is for a result builder body result type, the requirement // came from the function's return type. if (getLocator()->isForFunctionBuilderBodyResult()) { auto *func = getAsDecl(getAnchor()); @@ -5522,8 +5522,8 @@ static bool hasMissingElseInChain(IfStmt *ifStmt) { void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary( bool asNote) { if (auto stmt = unhandled.dyn_cast()) { - emitDiagnostic(asNote ? diag::note_function_builder_control_flow - : diag::function_builder_control_flow, + emitDiagnostic(asNote ? diag::note_result_builder_control_flow + : diag::result_builder_control_flow, builder->getName()); // Emit custom notes to help the user introduce the appropriate 'build' @@ -5538,7 +5538,7 @@ void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary( // Do nothing. } else if (isa(stmt) && hasMissingElseInChain(cast(stmt))) { auto diag = emitDiagnosticAt( - builder->getLoc(), diag::function_builder_missing_build_optional, + builder->getLoc(), diag::result_builder_missing_build_optional, builder->getDeclaredInterfaceType()); std::string fixItString; @@ -5552,7 +5552,7 @@ void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary( diag.fixItInsert(buildInsertionLoc, fixItString); } else if (isa(stmt) || isa(stmt)) { auto diag = emitDiagnosticAt( - builder->getLoc(), diag::function_builder_missing_build_either, + builder->getLoc(), diag::result_builder_missing_build_either, builder->getDeclaredInterfaceType()); std::string fixItString; @@ -5572,7 +5572,7 @@ void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary( diag.fixItInsert(buildInsertionLoc, fixItString); } else if (isa(stmt)) { auto diag = emitDiagnosticAt( - builder->getLoc(), diag::function_builder_missing_build_array, + builder->getLoc(), diag::result_builder_missing_build_array, builder->getDeclaredInterfaceType()); std::string fixItString; @@ -5586,8 +5586,8 @@ void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary( diag.fixItInsert(buildInsertionLoc, fixItString); } } else { - emitDiagnostic(asNote ? diag::note_function_builder_decl - : diag::function_builder_decl, + emitDiagnostic(asNote ? diag::note_result_builder_decl + : diag::result_builder_decl, builder->getName()); } } diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index da0e1296427f3..2d50c051c7d01 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -1582,7 +1582,7 @@ bool IgnoreInvalidFunctionBuilderBody::diagnose(const Solution &solution, return std::make_pair(true, S); } - // Ignore patterns because function builder pre-check does so as well. + // Ignore patterns because result builder pre-check does so as well. std::pair walkToPatternPre(Pattern *P) override { return std::make_pair(false, P); } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index eaccb486df849..febd7890cf256 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -998,7 +998,7 @@ namespace { : CS(CS), CurDC(DC ? DC : CS.DC), CurrPhase(CS.getPhase()) { // Although constraint system is initialized in `constraint // generation` phase, we have to set it here manually because e.g. - // function builders could generate constraints for its body + // result builders could generate constraints for its body // in the middle of the solving. CS.setPhase(ConstraintSystemPhase::ConstraintGeneration); } @@ -1317,7 +1317,7 @@ namespace { type = CS.openUnboundGenericTypes(type, locator); } else if (CS.hasType(E)) { // If there's a type already set into the constraint system, honor it. - // FIXME: This supports the function builder transform, which sneakily + // FIXME: This supports the result builder transform, which sneakily // stashes a type in the constraint system through a TypeExpr in order // to pass it down to the rest of CSGen. This is a terribly // unprincipled thing to do. diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index ce6ae4d6826bd..76e8578ce4514 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -7657,7 +7657,7 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, auto *closure = castToExpr(closureLocator->getAnchor()); auto *inferredClosureType = getClosureType(closure); - // Determine whether a function builder will be applied. + // Determine whether a result builder will be applied. auto functionBuilderType = getOpenedFunctionBuilderTypeFor(*this, locator); // Determine whether to introduce one-way constraints between the parameter's @@ -7730,7 +7730,7 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, inferredClosureType->getExtInfo()); assignFixedType(typeVar, closureType, closureLocator); - // If there is a function builder to apply, do so now. + // If there is a result builder to apply, do so now. if (functionBuilderType) { if (auto result = matchFunctionBuilder( closure, functionBuilderType, closureType->getResult(), @@ -10367,14 +10367,14 @@ ConstraintSystem::addArgumentConversionConstraintImpl( // If we have an unresolved closure argument, form an unsolved argument // conversion constraint, making sure to reference the type variables for - // a function builder if applicable. This ensures we properly connect the - // closure type variable with any type variables in the function builder, as + // a result builder if applicable. This ensures we properly connect the + // closure type variable with any type variables in the result builder, as // such type variables will be accessible within the body of the closure when // we open it. first = getFixedTypeRecursive(first, /*rvalue*/ false); if (auto *argTypeVar = first->getAs()) { if (argTypeVar->getImpl().isClosureType()) { - // Extract any type variables present in the parameter's function builder. + // Extract any type variables present in the parameter's result builder. SmallVector typeVars; if (auto builderTy = getOpenedFunctionBuilderTypeFor(*this, locator)) builderTy->getTypeVariables(typeVars); diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index bdc519dec4758..726f0b3dd5344 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -560,7 +560,7 @@ ConstraintSystem::SolverScope::~SolverScope() { /// Remove any builder transformed closures. truncate(cs.functionBuilderTransformed, numFunctionBuilderTransformed); - // Remove any inferred closure types (e.g. used in function builder body). + // Remove any inferred closure types (e.g. used in result builder body). truncate(cs.ClosureTypes, numInferredClosureTypes); // Remove any contextual types. diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index ea1c2d5eac3d7..2008591cc249d 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -287,7 +287,7 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, // Don't allow the parameter to accept temporary pointer conversions. arg->setNonEphemeralIfPossible(); - // Attach a function builder attribute if needed. + // Attach a result builder attribute if needed. if (functionBuilderType) { auto typeExpr = TypeExpr::createImplicit(functionBuilderType, ctx); auto attr = CustomAttr::create( diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index afe82c87230de..e26103fc8c2c6 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -301,7 +301,7 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { break; case FunctionBuilderBodyResult: - out << "function builder body result"; + out << "result builder body result"; break; case SequenceElementType: diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 6bc7a1e2babb5..7569036e0c9b2 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -250,7 +250,7 @@ class AttributeChecker : public AttributeVisitor { void visitCustomAttr(CustomAttr *attr); void visitPropertyWrapperAttr(PropertyWrapperAttr *attr); - void visitFunctionBuilderAttr(FunctionBuilderAttr *attr); + void visitResultBuilderAttr(ResultBuilderAttr *attr); void visitImplementationOnlyAttr(ImplementationOnlyAttr *attr); void visitNonEphemeralAttr(NonEphemeralAttr *attr); @@ -3034,9 +3034,9 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { return; } - // If the nominal type is a function builder type, verify that D is a + // If the nominal type is a result builder type, verify that D is a // function, storage with an explicit getter, or parameter of function type. - if (nominal->getAttrs().hasAttribute()) { + if (nominal->getAttrs().hasAttribute()) { ValueDecl *decl; if (auto param = dyn_cast(D)) { decl = param; @@ -3046,7 +3046,7 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { decl = storage; // Check whether this is a storage declaration that is not permitted - // to have a function builder attached. + // to have a result builder attached. auto shouldDiagnose = [&]() -> bool { // An uninitialized stored property in a struct can have a function // builder attached. @@ -3076,7 +3076,7 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { if (shouldDiagnose()) { diagnose(attr->getLocation(), - diag::function_builder_attribute_on_storage_without_getter, + diag::result_builder_attribute_on_storage_without_getter, nominal->getName(), isa(storage) ? 0 : storage->getDeclContext()->isTypeContext() ? 1 @@ -3086,7 +3086,7 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { } } else { diagnose(attr->getLocation(), - diag::function_builder_attribute_not_allowed_here, + diag::result_builder_attribute_not_allowed_here, nominal->getName()); attr->setInvalid(); return; @@ -3094,16 +3094,16 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { // Diagnose and ignore arguments. if (attr->getArg()) { - diagnose(attr->getLocation(), diag::function_builder_arguments) + diagnose(attr->getLocation(), diag::result_builder_arguments) .highlight(attr->getArg()->getSourceRange()); } // Complain if this isn't the primary function-builder attribute. auto attached = decl->getAttachedFunctionBuilder(); if (attached != attr) { - diagnose(attr->getLocation(), diag::function_builder_multiple, + diagnose(attr->getLocation(), diag::result_builder_multiple, isa(decl)); - diagnose(attached->getLocation(), diag::previous_function_builder_here); + diagnose(attached->getLocation(), diag::previous_result_builder_here); attr->setInvalid(); return; } else { @@ -3140,7 +3140,7 @@ void AttributeChecker::visitPropertyWrapperAttr(PropertyWrapperAttr *attr) { (void)nominal->getPropertyWrapperTypeInfo(); } -void AttributeChecker::visitFunctionBuilderAttr(FunctionBuilderAttr *attr) { +void AttributeChecker::visitResultBuilderAttr(ResultBuilderAttr *attr) { auto *nominal = dyn_cast(D); SmallVector potentialMatches; bool supportsBuildBlock = TypeChecker::typeSupportsBuilderOp( @@ -3150,7 +3150,7 @@ void AttributeChecker::visitFunctionBuilderAttr(FunctionBuilderAttr *attr) { if (!supportsBuildBlock) { { auto diag = diagnose( - nominal->getLoc(), diag::function_builder_static_buildblock); + nominal->getLoc(), diag::result_builder_static_buildblock); // If there were no close matches, propose adding a stub. SourceLoc buildInsertionLoc; @@ -3180,13 +3180,13 @@ void AttributeChecker::visitFunctionBuilderAttr(FunctionBuilderAttr *attr) { if (isa(member) && member->getDeclContext()->getSelfNominalTypeDecl() == nominal) - diagnose(member->getLoc(), diag::function_builder_non_static_buildblock) + diagnose(member->getLoc(), diag::result_builder_non_static_buildblock) .fixItInsert(member->getAttributeInsertionLoc(true), "static "); else if (isa(member)) - diagnose(member->getLoc(), diag::function_builder_buildblock_enum_case); + diagnose(member->getLoc(), diag::result_builder_buildblock_enum_case); else diagnose(member->getLoc(), - diag::function_builder_buildblock_not_static_method); + diag::result_builder_buildblock_not_static_method); } } } diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index 663d0d75c7478..ef3746a6db34e 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -863,7 +863,7 @@ bool TypeChecker::typeCheckForCodeCompletion( return false; // FIXME: There is currently no way to distinguish between - // multi-statement closures which are function builder bodies + // multi-statement closures which are result builder bodies // (that are type-checked together with enclosing context) // and regular closures which are type-checked separately. diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 982326728bcec..0af4245017c8f 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -222,7 +222,7 @@ void ParentConditionalConformance::diagnoseConformanceStack( namespace { /// Produce any additional syntactic diagnostics for the body of a function -/// that had a function builder applied. +/// that had a result builder applied. class FunctionSyntacticDiagnosticWalker : public ASTWalker { SmallVector dcStack; diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 11c7985877a54..17b11524208a1 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1510,7 +1510,7 @@ namespace { UNINTERESTING_ATTR(Custom) UNINTERESTING_ATTR(PropertyWrapper) UNINTERESTING_ATTR(DisfavoredOverload) - UNINTERESTING_ATTR(FunctionBuilder) + UNINTERESTING_ATTR(ResultBuilder) UNINTERESTING_ATTR(ProjectedValueProperty) UNINTERESTING_ATTR(OriginallyDefinedIn) UNINTERESTING_ATTR(Actor) diff --git a/lib/Sema/TypeCheckRequestFunctions.cpp b/lib/Sema/TypeCheckRequestFunctions.cpp index a721e026d7f64..8df0bf41c62c6 100644 --- a/lib/Sema/TypeCheckRequestFunctions.cpp +++ b/lib/Sema/TypeCheckRequestFunctions.cpp @@ -176,15 +176,15 @@ AttachedFunctionBuilderRequest::evaluate(Evaluator &evaluator, if (!nominal) continue; - // Return the first custom attribute that is a function builder type. - if (nominal->getAttrs().hasAttribute()) + // Return the first custom attribute that is a result builder type. + if (nominal->getAttrs().hasAttribute()) return mutableAttr; } return nullptr; } -/// Attempt to infer the function builder type for a declaration. +/// Attempt to infer the result builder type for a declaration. static Type inferFunctionBuilderType(ValueDecl *decl) { auto dc = decl->getDeclContext(); if (!dc->isTypeContext() || isa(dc)) @@ -196,12 +196,12 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { return Type(); // Check whether there are any return statements in the function's body. - // If there are, the function builder transform will be disabled, - // so don't infer a function builder. + // If there are, the result builder transform will be disabled, + // so don't infer a result builder. if (!TypeChecker::findReturnStatements(funcDecl).empty()) return Type(); - // Only getters can have function builders. When we find one, look at + // Only getters can have result builders. When we find one, look at // the storage declaration for the purposes of witness matching. auto lookupDecl = decl; if (auto accessor = dyn_cast(funcDecl)) { @@ -211,7 +211,7 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { lookupDecl = accessor->getStorage(); } - // Find all of the potentially inferred function builder types. + // Find all of the potentially inferred result builder types. struct Match { enum Kind { Conformance, @@ -262,13 +262,13 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { } }; - // The set of matches from which we can infer function builder types. + // The set of matches from which we can infer result builder types. SmallVector matches; // Determine all of the conformances within the same context as // this declaration. If this declaration is a witness to any - // requirement within one of those protocols that has a function builder - // attached, use that function builder type. + // requirement within one of those protocols that has a result builder + // attached, use that result builder type. auto addConformanceMatches = [&matches](ValueDecl *lookupDecl) { DeclContext *dc = lookupDecl->getDeclContext(); auto idc = cast(dc->getAsDecl()); @@ -294,7 +294,7 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { if (witness != lookupDecl) continue; - // Substitute into the function builder type. + // Substitute into the result builder type. auto subs = conformance->getSubstitutions(lookupDecl->getModuleContext()); Type subFunctionBuilderType = functionBuilderType.subst(subs); @@ -308,7 +308,7 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { addConformanceMatches(lookupDecl); - // Look for function builder types inferred through dynamic replacements. + // Look for result builder types inferred through dynamic replacements. if (auto replaced = lookupDecl->getDynamicallyReplacedDecl()) { if (auto functionBuilderType = replaced->getFunctionBuilderType()) { matches.push_back( @@ -321,7 +321,7 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { if (matches.size() == 0) return Type(); - // Determine whether there is more than one actual function builder type. + // Determine whether there is more than one actual result builder type. Type functionBuilderType = matches[0].functionBuilderType; for (const auto &match : matches) { // If the types were the same anyway, there's nothing to do. @@ -329,16 +329,16 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { if (functionBuilderType->isEqual(otherFunctionBuilderType)) continue; - // We have at least two different function builder types. + // We have at least two different result builder types. // Diagnose the ambiguity and provide potential solutions. decl->diagnose( - diag::function_builder_infer_ambig, lookupDecl->getName(), + diag::result_builder_infer_ambig, lookupDecl->getName(), functionBuilderType, otherFunctionBuilderType); - decl->diagnose(diag::function_builder_infer_add_return) + decl->diagnose(diag::result_builder_infer_add_return) .fixItInsert(funcDecl->getBodySourceRange().End, "return <#expr#>\n"); for (const auto &match : matches) { decl->diagnose( - diag::function_builder_infer_pick_specific, + diag::result_builder_infer_pick_specific, match.functionBuilderType, static_cast(match.kind), match.getSourceName()) @@ -386,7 +386,7 @@ Type FunctionBuilderTypeRequest::evaluate(Evaluator &evaluator, // Require the parameter to be an interface type. if (!paramFnType) { ctx.Diags.diagnose(attr->getLocation(), - diag::function_builder_parameter_not_of_function_type, + diag::result_builder_parameter_not_of_function_type, nominal->getName()); mutableAttr->setInvalid(); return Type(); @@ -395,7 +395,7 @@ Type FunctionBuilderTypeRequest::evaluate(Evaluator &evaluator, // Forbid the parameter to be an autoclosure. if (param->isAutoClosure()) { ctx.Diags.diagnose(attr->getLocation(), - diag::function_builder_parameter_autoclosure, + diag::result_builder_parameter_autoclosure, nominal->getName()); mutableAttr->setInvalid(); return Type(); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index e384ae6fc9dae..742d8c6e636d0 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -422,7 +422,7 @@ Expr *substituteInputSugarTypeForResult(ApplyExpr *E); void typeCheckASTNode(ASTNode &node, DeclContext *DC, bool LeaveBodyUnchecked = false); -/// Try to apply the function builder transform of the given builder type +/// Try to apply the result builder transform of the given builder type /// to the body of the function. /// /// \returns \c None if the builder transformation cannot be applied at all, @@ -1172,7 +1172,7 @@ bool requireArrayLiteralIntrinsics(ASTContext &ctx, SourceLoc loc); /// an \c UnresolvedMemberExpr, \c nullptr is returned. UnresolvedMemberExpr *getUnresolvedMemberChainBase(Expr *expr); -/// Checks whether a function builder type has a well-formed function builder +/// Checks whether a result builder type has a well-formed result builder /// method with the given name. If provided and non-empty, the argument labels /// are verified against any candidates. bool typeSupportsBuilderOp(Type builderType, DeclContext *dc, Identifier fnName, diff --git a/test/Constraints/function_builder.swift b/test/Constraints/function_builder.swift index 82dffb9863582..54abc88e774b9 100644 --- a/test/Constraints/function_builder.swift +++ b/test/Constraints/function_builder.swift @@ -478,7 +478,7 @@ testIfConditions(cond: true, c1: true, i1: 1, i2: 1) // CHECK: testIfConditions // CHECK-SAME: hello -// Use a "let" declaration within a function builder. +// Use a "let" declaration within a result builder. tuplify(true) { c in "testLetDeclarations" let (a, b) = (c, c && true) @@ -685,7 +685,7 @@ tuplify(true) { c in } } -// Test the use of function builders partly implemented through a protocol. +// Test the use of result builders partly implemented through a protocol. indirect enum FunctionBuilder { case expression(Expression) case block([FunctionBuilder]) @@ -749,7 +749,7 @@ let a = buildArray { // CHECK: ["1", "2" print(a) -// Throwing in function builders. +// Throwing in result builders. enum MyError: Error { case boom } diff --git a/test/Constraints/function_builder_availability.swift b/test/Constraints/function_builder_availability.swift index a823845b2091f..e7bf8235d8299 100644 --- a/test/Constraints/function_builder_availability.swift +++ b/test/Constraints/function_builder_availability.swift @@ -8,7 +8,7 @@ enum Either { } @resultBuilder -struct TupleBuilder { // expected-note{{add 'buildLimitedAvailability(_:)' to the function builder 'TupleBuilder' to erase type information for less-available types}}{{22-22=\n static func buildLimitedAvailability(_ component: <#Component#>) -> <#Component#> {\n <#code#>\n \}}} +struct TupleBuilder { // expected-note{{add 'buildLimitedAvailability(_:)' to the result builder 'TupleBuilder' to erase type information for less-available types}}{{22-22=\n static func buildLimitedAvailability(_ component: <#Component#>) -> <#Component#> {\n <#code#>\n \}}} static func buildBlock(_ t1: T1) -> (T1) { return (t1) } @@ -65,7 +65,7 @@ tuplify(true) { cond in if #available(OSX 10.51, *) { globalFuncAvailableOn10_51() tuplify(false) { cond2 in - if cond, #available(OSX 10.52, *) { // expected-warning{{function builder 'TupleBuilder' does not implement 'buildLimitedAvailability'; this code may crash on earlier versions of the OS}} + if cond, #available(OSX 10.52, *) { // expected-warning{{result builder 'TupleBuilder' does not implement 'buildLimitedAvailability'; this code may crash on earlier versions of the OS}} cond2 globalFuncAvailableOn10_52() } else { diff --git a/test/Constraints/function_builder_diags.swift b/test/Constraints/function_builder_diags.swift index fd206e68ede41..13153cb1146cb 100644 --- a/test/Constraints/function_builder_diags.swift +++ b/test/Constraints/function_builder_diags.swift @@ -46,9 +46,9 @@ struct TupleBuilder { // expected-note 2 {{struct 'TupleBuilder' declared here}} @resultBuilder struct TupleBuilderWithoutIf { // expected-note 3{{struct 'TupleBuilderWithoutIf' declared here}} - // expected-note@-1{{add 'buildOptional(_:)' to the function builder 'TupleBuilderWithoutIf' to add support for 'if' statements without an 'else'}} - // expected-note@-2{{add 'buildEither(first:)' and 'buildEither(second:)' to the function builder 'TupleBuilderWithoutIf' to add support for 'if'-'else' and 'switch'}} - // expected-note@-3{{add 'buildArray(_:)' to the function builder 'TupleBuilderWithoutIf' to add support for 'for'..'in' loops}} + // expected-note@-1{{add 'buildOptional(_:)' to the result builder 'TupleBuilderWithoutIf' to add support for 'if' statements without an 'else'}} + // expected-note@-2{{add 'buildEither(first:)' and 'buildEither(second:)' to the result builder 'TupleBuilderWithoutIf' to add support for 'if'-'else' and 'switch'}} + // expected-note@-3{{add 'buildArray(_:)' to the result builder 'TupleBuilderWithoutIf' to add support for 'for'..'in' loops}} static func buildBlock() -> () { } static func buildBlock(_ t1: T1) -> T1 { @@ -99,26 +99,26 @@ func testDiags() { tuplify(true) { _ in 17 let x = 17 - let y: Int // expected-error{{closure containing a declaration cannot be used with function builder 'TupleBuilder'}} + let y: Int // expected-error{{closure containing a declaration cannot be used with result builder 'TupleBuilder'}} x + 25 } // Statements unsupported by the particular builder. tuplifyWithoutIf(true) { - if $0 { // expected-error{{closure containing control flow statement cannot be used with function builder 'TupleBuilderWithoutIf'}} + if $0 { // expected-error{{closure containing control flow statement cannot be used with result builder 'TupleBuilderWithoutIf'}} "hello" } } tuplifyWithoutIf(true) { - if $0 { // expected-error{{closure containing control flow statement cannot be used with function builder 'TupleBuilderWithoutIf'}} + if $0 { // expected-error{{closure containing control flow statement cannot be used with result builder 'TupleBuilderWithoutIf'}} "hello" } else { } } tuplifyWithoutIf(true) { a in - for x in 0..<100 { // expected-error{{closure containing control flow statement cannot be used with function builder 'TupleBuilderWithoutIf'}} + for x in 0..<100 { // expected-error{{closure containing control flow statement cannot be used with result builder 'TupleBuilderWithoutIf'}} x } } @@ -218,7 +218,7 @@ struct SR11440 { } func foo() { - // This is okay, we apply the function builder for the subscript arg. + // This is okay, we apply the result builder for the subscript arg. self[{ 5 5 @@ -276,10 +276,10 @@ tuplify(true) { x in struct MyTuplifiedStruct { var condition: Bool - @TupleBuilder var computed: some Any { // expected-note{{remove the attribute to explicitly disable the function builder}}{{3-17=}} + @TupleBuilder var computed: some Any { // expected-note{{remove the attribute to explicitly disable the result builder}}{{3-17=}} if condition { - return 17 // expected-warning{{application of function builder 'TupleBuilder' disabled by explicit 'return' statement}} - // expected-note@-1{{remove 'return' statements to apply the function builder}}{{7-14=}}{{12-19=}} + return 17 // expected-warning{{application of result builder 'TupleBuilder' disabled by explicit 'return' statement}} + // expected-note@-1{{remove 'return' statements to apply the result builder}}{{7-14=}}{{12-19=}} } else { return 42 } @@ -314,7 +314,7 @@ func checkConditions(cond: Bool) { } } -// Check that a closure with a single "return" works with function builders. +// Check that a closure with a single "return" works with result builders. func checkSingleReturn(cond: Bool) { tuplify(cond) { value in return (value, 17) @@ -386,7 +386,7 @@ func testSwitch(e: E) { case .b(let i, let s?): i * 2 s + "!" - fallthrough // expected-error{{closure containing control flow statement cannot be used with function builder 'TupleBuilder'}} + fallthrough // expected-error{{closure containing control flow statement cannot be used with result builder 'TupleBuilder'}} case .b(let i, nil): "just \(i)" } @@ -577,7 +577,7 @@ func testWrapperBuilder() { let _: Int = x // expected-error{{cannot convert value of type 'Wrapper<(Double, String)>' to specified type 'Int'}} } -// rdar://problem/61347993 - empty function builder doesn't compile +// rdar://problem/61347993 - empty result builder doesn't compile func rdar61347993() { struct Result {} @@ -649,7 +649,7 @@ struct MyView { } } -// Make sure throwing function builder closures are implied. +// Make sure throwing result builder closures are implied. enum MyError: Error { case boom } diff --git a/test/Constraints/function_builder_infer.swift b/test/Constraints/function_builder_infer.swift index 3deeff29a4473..08150d32a528e 100644 --- a/test/Constraints/function_builder_infer.swift +++ b/test/Constraints/function_builder_infer.swift @@ -55,7 +55,7 @@ protocol Tupled { struct TupleMe: Tupled { var condition: Bool - // Okay: applies the function builder @TupleBuilder. + // Okay: applies the result builder @TupleBuilder. var tuple: some Any { "hello" if condition { @@ -67,7 +67,7 @@ struct TupleMe: Tupled { } // Witness is separated from the context declaring conformance, so don't infer -// the function builder. +// the result builder. struct DoNotTupleMe { var condition: Bool @@ -129,8 +129,8 @@ protocol Tupled2 { struct TupleMe2: Tupled, Tupled2 { var condition: Bool - // Okay: applies the function builder @TupleBuilder, even though it satisfies - // two requirements. (They have the same function builder) + // Okay: applies the result builder @TupleBuilder, even though it satisfies + // two requirements. (They have the same result builder) var tuple: some Any { "hello" if condition { @@ -152,10 +152,10 @@ struct AmbigTupleMe: Tupled, OtherTupled { // Ambiguous internal - var tuple: Void { // expected-error{{ambiguous function builder inferred for 'tuple': 'TupleBuilder' or 'OtherTupleBuilder'}} - // expected-note@-1{{add an explicit 'return' statement to not use a function builder}}{{3-3=return <#expr#>\n}} - // expected-note@-2{{apply function builder 'TupleBuilder' (inferred from protocol 'Tupled')}}{{3-3=@TupleBuilder }} - // expected-note@-3{{apply function builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}}{{3-3=@OtherTupleBuilder }} + var tuple: Void { // expected-error{{ambiguous result builder inferred for 'tuple': 'TupleBuilder' or 'OtherTupleBuilder'}} + // expected-note@-1{{add an explicit 'return' statement to not use a result builder}}{{3-3=return <#expr#>\n}} + // expected-note@-2{{apply result builder 'TupleBuilder' (inferred from protocol 'Tupled')}}{{3-3=@TupleBuilder }} + // expected-note@-3{{apply result builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}}{{3-3=@OtherTupleBuilder }} "hello" // expected-warning{{string literal is unused}} "world" // expected-warning{{string literal is unused}} } @@ -209,10 +209,10 @@ struct DynamicTupled2: Tupled, OtherTupled { extension DynamicTupled2 { @_dynamicReplacement(for: tuple) - var replacementTuple: some Any { // expected-error{{ambiguous function builder inferred for 'replacementTuple': 'TupleBuilder' or 'OtherTupleBuilder'}} - // expected-note@-1{{add an explicit 'return' statement to not use a function builder}} - // expected-note@-2{{apply function builder 'TupleBuilder' (inferred from protocol 'Tupled')}} - // expected-note@-3{{apply function builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}} + var replacementTuple: some Any { // expected-error{{ambiguous result builder inferred for 'replacementTuple': 'TupleBuilder' or 'OtherTupleBuilder'}} + // expected-note@-1{{add an explicit 'return' statement to not use a result builder}} + // expected-note@-2{{apply result builder 'TupleBuilder' (inferred from protocol 'Tupled')}} + // expected-note@-3{{apply result builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}} 1 } } @@ -225,10 +225,10 @@ struct DynamicTupled3 { extension DynamicTupled3: OtherTupled { @_dynamicReplacement(for: dynamicTuple) - var tuple: some Any { // expected-error{{ambiguous function builder inferred for 'tuple': 'OtherTupleBuilder' or 'TupleBuilder'}} - // expected-note@-1{{add an explicit 'return' statement to not use a function builder}} - // expected-note@-2{{apply function builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}} - // expected-note@-3{{apply function builder 'TupleBuilder' (inferred from dynamic replacement of 'dynamicTuple')}} + var tuple: some Any { // expected-error{{ambiguous result builder inferred for 'tuple': 'OtherTupleBuilder' or 'TupleBuilder'}} + // expected-note@-1{{add an explicit 'return' statement to not use a result builder}} + // expected-note@-2{{apply result builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}} + // expected-note@-3{{apply result builder 'TupleBuilder' (inferred from dynamic replacement of 'dynamicTuple')}} 0 } } diff --git a/test/Constraints/sr13183.swift b/test/Constraints/sr13183.swift index 0cd8a4106c6be..2ab0551b36f94 100644 --- a/test/Constraints/sr13183.swift +++ b/test/Constraints/sr13183.swift @@ -1,7 +1,7 @@ // RUN: %target-typecheck-verify-swift // SR-13183: Make sure we don't incorrectly split the constraint system without -// considering that a function builder type var may connect the inside of a +// considering that a result builder type var may connect the inside of a // closure body with the enclosing expression. struct New { diff --git a/test/IDE/complete_ambiguous.swift b/test/IDE/complete_ambiguous.swift index 5793ce6633b1a..08a15e560f442 100644 --- a/test/IDE/complete_ambiguous.swift +++ b/test/IDE/complete_ambiguous.swift @@ -300,7 +300,7 @@ CreateThings { } } -// FIXME: No results in multi-statement closure with erroreous sibling function builder element +// FIXME: No results in multi-statement closure with erroreous sibling result builder element CreateThings { Thing { point in print("hello") diff --git a/test/IDE/complete_function_builder.swift b/test/IDE/complete_function_builder.swift index 519e916a1708b..9038b18a904cb 100644 --- a/test/IDE/complete_function_builder.swift +++ b/test/IDE/complete_function_builder.swift @@ -1,8 +1,8 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_CLOSURE_TOP | %FileCheck %s -check-prefix=IN_CLOSURE_TOP // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_CLOSURE_NONTOP | %FileCheck %s -check-prefix=IN_CLOSURE_TOP // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_CLOSURE_COLOR_CONTEXT | %FileCheck %s -check-prefix=IN_CLOSURE_COLOR_CONTEXT -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FUNCTION_BUILDER_DECL -code-completion-comments=true | %FileCheck %s -check-prefix=IN_FUNCTION_BUILDER_DECL -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FUNCTION_BUILDER_DECL_PREFIX -code-completion-comments=true | %FileCheck %s -check-prefix=IN_FUNCTION_BUILDER_DECL_PREFIX +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_RESULT_BUILDER_DECL -code-completion-comments=true | %FileCheck %s -check-prefix=IN_RESULT_BUILDER_DECL +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_RESULT_BUILDER_DECL_PREFIX -code-completion-comments=true | %FileCheck %s -check-prefix=IN_RESULT_BUILDER_DECL_PREFIX struct Tagged { let tag: Tag @@ -100,29 +100,29 @@ func acceptBuilder(@EnumToVoidBuilder body: () -> Void) {} struct AnyBuilder { static func buildBlock(_ components: Any...) -> Any { 5 } - #^IN_FUNCTION_BUILDER_DECL_PREFIX^# + #^IN_RESULT_BUILDER_DECL_PREFIX^# - static func #^IN_FUNCTION_BUILDER_DECL^# + static func #^IN_RESULT_BUILDER_DECL^# } -// IN_FUNCTION_BUILDER_DECL: Begin completions, 8 items -// IN_FUNCTION_BUILDER_DECL: Pattern/CurrNominal: buildBlock(_ components: Any...) -> Any {|}; name=buildBlock(_ components: Any...) -> Any; comment=Required by every -// IN_FUNCTION_BUILDER_DECL: Pattern/CurrNominal: buildExpression(_ expression: <#Expression#>) -> Any {|}; name=buildExpression(_ expression: <#Expression#>) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL: Pattern/CurrNominal: buildOptional(_ component: Any?) -> Any {|}; name=buildOptional(_ component: Any?) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL: Pattern/CurrNominal: buildEither(first component: Any) -> Any {|}; name=buildEither(first component: Any) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL: Pattern/CurrNominal: buildEither(second component: Any) -> Any {|}; name=buildEither(second component: Any) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL: Pattern/CurrNominal: buildArray(_ components: [Any]) -> Any {|}; name=buildArray(_ components: [Any]) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL: Pattern/CurrNominal: buildLimitedAvailability(_ component: Any) -> Any {|}; name=buildLimitedAvailability(_ component: Any) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL: Pattern/CurrNominal: buildFinalResult(_ component: Any) -> <#Result#> {|}; name=buildFinalResult(_ component: Any) -> <#Result#>; comment= -// IN_FUNCTION_BUILDER_DECL: End completions - -// IN_FUNCTION_BUILDER_DECL_PREFIX: Begin completions -// IN_FUNCTION_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildBlock(_ components: Any...) -> Any {|}; name=static func buildBlock(_ components: Any...) -> Any; comment=Required by every -// IN_FUNCTION_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildExpression(_ expression: <#Expression#>) -> Any {|}; name=static func buildExpression(_ expression: <#Expression#>) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildOptional(_ component: Any?) -> Any {|}; name=static func buildOptional(_ component: Any?) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildEither(first component: Any) -> Any {|}; name=static func buildEither(first component: Any) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildEither(second component: Any) -> Any {|}; name=static func buildEither(second component: Any) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildArray(_ components: [Any]) -> Any {|}; name=static func buildArray(_ components: [Any]) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildLimitedAvailability(_ component: Any) -> Any {|}; name=static func buildLimitedAvailability(_ component: Any) -> Any; comment= -// IN_FUNCTION_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildFinalResult(_ component: Any) -> <#Result#> {|}; name=static func buildFinalResult(_ component: Any) -> <#Result#>; comment= -// IN_FUNCTION_BUILDER_DECL_PREFIX: End completions +// IN_RESULT_BUILDER_DECL: Begin completions, 8 items +// IN_RESULT_BUILDER_DECL: Pattern/CurrNominal: buildBlock(_ components: Any...) -> Any {|}; name=buildBlock(_ components: Any...) -> Any; comment=Required by every +// IN_RESULT_BUILDER_DECL: Pattern/CurrNominal: buildExpression(_ expression: <#Expression#>) -> Any {|}; name=buildExpression(_ expression: <#Expression#>) -> Any; comment= +// IN_RESULT_BUILDER_DECL: Pattern/CurrNominal: buildOptional(_ component: Any?) -> Any {|}; name=buildOptional(_ component: Any?) -> Any; comment= +// IN_RESULT_BUILDER_DECL: Pattern/CurrNominal: buildEither(first component: Any) -> Any {|}; name=buildEither(first component: Any) -> Any; comment= +// IN_RESULT_BUILDER_DECL: Pattern/CurrNominal: buildEither(second component: Any) -> Any {|}; name=buildEither(second component: Any) -> Any; comment= +// IN_RESULT_BUILDER_DECL: Pattern/CurrNominal: buildArray(_ components: [Any]) -> Any {|}; name=buildArray(_ components: [Any]) -> Any; comment= +// IN_RESULT_BUILDER_DECL: Pattern/CurrNominal: buildLimitedAvailability(_ component: Any) -> Any {|}; name=buildLimitedAvailability(_ component: Any) -> Any; comment= +// IN_RESULT_BUILDER_DECL: Pattern/CurrNominal: buildFinalResult(_ component: Any) -> <#Result#> {|}; name=buildFinalResult(_ component: Any) -> <#Result#>; comment= +// IN_RESULT_BUILDER_DECL: End completions + +// IN_RESULT_BUILDER_DECL_PREFIX: Begin completions +// IN_RESULT_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildBlock(_ components: Any...) -> Any {|}; name=static func buildBlock(_ components: Any...) -> Any; comment=Required by every +// IN_RESULT_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildExpression(_ expression: <#Expression#>) -> Any {|}; name=static func buildExpression(_ expression: <#Expression#>) -> Any; comment= +// IN_RESULT_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildOptional(_ component: Any?) -> Any {|}; name=static func buildOptional(_ component: Any?) -> Any; comment= +// IN_RESULT_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildEither(first component: Any) -> Any {|}; name=static func buildEither(first component: Any) -> Any; comment= +// IN_RESULT_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildEither(second component: Any) -> Any {|}; name=static func buildEither(second component: Any) -> Any; comment= +// IN_RESULT_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildArray(_ components: [Any]) -> Any {|}; name=static func buildArray(_ components: [Any]) -> Any; comment= +// IN_RESULT_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildLimitedAvailability(_ component: Any) -> Any {|}; name=static func buildLimitedAvailability(_ component: Any) -> Any; comment= +// IN_RESULT_BUILDER_DECL_PREFIX: Pattern/CurrNominal: static func buildFinalResult(_ component: Any) -> <#Result#> {|}; name=static func buildFinalResult(_ component: Any) -> <#Result#>; comment= +// IN_RESULT_BUILDER_DECL_PREFIX: End completions diff --git a/test/ModuleInterface/function_builders.swift b/test/ModuleInterface/function_builders.swift index 6f20cf5ecc687..79a89dc5932bd 100644 --- a/test/ModuleInterface/function_builders.swift +++ b/test/ModuleInterface/function_builders.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -typecheck -module-name FunctionBuilders -emit-module-interface-path %t/FunctionBuilders.swiftinterface %s // RUN: %FileCheck %s < %t/FunctionBuilders.swiftinterface -// RUN: %target-swift-frontend -I %t -typecheck -verify %S/Inputs/function_builders_client.swift +// RUN: %target-swift-frontend -I %t -typecheck -verify %S/Inputs/result_builders_client.swift // RUN: %target-swift-frontend -compile-module-from-interface %t/FunctionBuilders.swiftinterface -o %t/FunctionBuilders.swiftmodule @resultBuilder diff --git a/test/SILGen/function_builder_curry_thunks.swift b/test/SILGen/function_builder_curry_thunks.swift deleted file mode 100644 index 57764b21cb244..0000000000000 --- a/test/SILGen/function_builder_curry_thunks.swift +++ /dev/null @@ -1,33 +0,0 @@ -// RUN: %target-swift-emit-silgen %s | %FileCheck %s - -@resultBuilder -struct Builder { - static func buildBlock(_ t1: T1) -> (T1) { - return (t1) - } -} - -struct Handler { - var firstHandler: () -> () - var secondHandler: () -> () -} - -// We were neglecting to assign discriminators and re-parent -// autoclosures, which would manifest as curried method references -// producing a bogus diagnostic about captures from inside a -// nested type. -class Outer { - struct Inner { - @Builder - var build: Handler { - Handler(firstHandler: self.handler, secondHandler: self.handler) - } - - private func handler() {} - } -} - -// CHECK-LABEL: sil private [ossa] @$s29function_builder_curry_thunks5OuterC5InnerV5buildAA7HandlerVvgyycAEcfu_ : $@convention(thin) (Outer.Inner) -> @owned @callee_guaranteed () -> () -// CHECK-LABEL: sil private [ossa] @$s29function_builder_curry_thunks5OuterC5InnerV5buildAA7HandlerVvgyycAEcfu_yycfu0_ : $@convention(thin) (Outer.Inner) -> () -// CHECK-LABEL: sil private [ossa] @$s29function_builder_curry_thunks5OuterC5InnerV7handler33_DC254A3F89F9C7E65D25434E199F17A4LLyyF : $@convention(method) (Outer.Inner) -> () -// CHECK-LABEL: sil private [ossa] @$s29function_builder_curry_thunks5OuterC5InnerV5buildAA7HandlerVvgyycAEcfu1_ : $@convention(thin) (Outer.Inner) -> @owned @callee_guaranteed () -> () diff --git a/test/SILGen/result_builder_curry_thunks.swift b/test/SILGen/result_builder_curry_thunks.swift new file mode 100644 index 0000000000000..165ba06bf23ef --- /dev/null +++ b/test/SILGen/result_builder_curry_thunks.swift @@ -0,0 +1,33 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +@resultBuilder +struct Builder { + static func buildBlock(_ t1: T1) -> (T1) { + return (t1) + } +} + +struct Handler { + var firstHandler: () -> () + var secondHandler: () -> () +} + +// We were neglecting to assign discriminators and re-parent +// autoclosures, which would manifest as curried method references +// producing a bogus diagnostic about captures from inside a +// nested type. +class Outer { + struct Inner { + @Builder + var build: Handler { + Handler(firstHandler: self.handler, secondHandler: self.handler) + } + + private func handler() {} + } +} + +// CHECK-LABEL: sil private [ossa] @$s27result_builder_curry_thunks5OuterC5InnerV5buildAA7HandlerVvgyycAEcfu_ : $@convention(thin) (Outer.Inner) -> @owned @callee_guaranteed () -> () +// CHECK-LABEL: sil private [ossa] @$s27result_builder_curry_thunks5OuterC5InnerV5buildAA7HandlerVvgyycAEcfu_yycfu0_ : $@convention(thin) (Outer.Inner) -> () +// CHECK-LABEL: sil private [ossa] @$s27result_builder_curry_thunks5OuterC5InnerV7handler33_{{.*}} : $@convention(method) (Outer.Inner) -> () +// CHECK-LABEL: sil private [ossa] @$s27result_builder_curry_thunks5OuterC5InnerV5buildAA7HandlerVvgyycAEcfu1_ : $@convention(thin) (Outer.Inner) -> @owned @callee_guaranteed () -> () diff --git a/test/SILGen/function_builder_memberwise.swift b/test/SILGen/result_builder_memberwise.swift similarity index 84% rename from test/SILGen/function_builder_memberwise.swift rename to test/SILGen/result_builder_memberwise.swift index f445f8f90a520..b4c0ec6027466 100644 --- a/test/SILGen/function_builder_memberwise.swift +++ b/test/SILGen/result_builder_memberwise.swift @@ -35,7 +35,7 @@ struct MyTupleStruct { // CHECK: init(@TupleBuilder first: @escaping () -> T, @TupleBuilder second: () -> U) } -// CHECK-LABEL: sil hidden [ossa] @$s27function_builder_memberwise13MyTupleStructV5first6secondACyxq_Gxyc_q_yXEtcfC : $@convention(method) (@owned @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for , @noescape @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for , @thin MyTupleStruct.Type) -> @out MyTupleStruct { +// CHECK-LABEL: sil hidden [ossa] @$s25result_builder_memberwise13MyTupleStructV5first6secondACyxq_Gxyc_q_yXEtcfC : $@convention(method) (@owned @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for , @noescape @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for , @thin MyTupleStruct.Type) -> @out MyTupleStruct { // CHECK: bb0([[SELF:%.*]] : $*MyTupleStruct, [[FIRST:%.*]] : @owned $@callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for , [[SECOND:%.*]] : $@noescape @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for , [[META:%.*]] : $@thin MyTupleStruct.Type): // CHECK-NEXT: [[FIRST_ADDR:%.*]] = struct_element_addr [[SELF]] : $*MyTupleStruct, #MyTupleStruct.first // CHECK-NEXT: store [[FIRST]] to [init] [[FIRST_ADDR]] : $*@callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for diff --git a/test/Serialization/function_builders.swift b/test/Serialization/result_builders.swift similarity index 90% rename from test/Serialization/function_builders.swift rename to test/Serialization/result_builders.swift index 17f62f41af1d8..1ca10925efece 100644 --- a/test/Serialization/function_builders.swift +++ b/test/Serialization/result_builders.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t %s -// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -skip-deinit=false -module-to-print=function_builders -I %t -source-filename=%s | %FileCheck %s +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -skip-deinit=false -module-to-print=result_builders -I %t -source-filename=%s | %FileCheck %s @resultBuilder public struct TupleBuilder { diff --git a/test/decl/function_builder_fixits.swift b/test/decl/function_builder_fixits.swift index a4502938cde4a..9c49ff29d6d3d 100644 --- a/test/decl/function_builder_fixits.swift +++ b/test/decl/function_builder_fixits.swift @@ -3,13 +3,13 @@ // Line-feeds in Fix-Its fail to check on Windows. @resultBuilder -struct Maker {} // expected-error {{function builder must provide at least one static 'buildBlock' method}}{{15-15=\n static func buildBlock(_ components: <#Component#>...) -> <#Component#> {\n <#code#>\n \}}} +struct Maker {} // expected-error {{result builder must provide at least one static 'buildBlock' method}}{{15-15=\n static func buildBlock(_ components: <#Component#>...) -> <#Component#> {\n <#code#>\n \}}} @resultBuilder struct TupleBuilderWithoutIf { // expected-note 3{{struct 'TupleBuilderWithoutIf' declared here}} - // expected-note@-1{{add 'buildOptional(_:)' to the function builder 'TupleBuilderWithoutIf' to add support for 'if' statements without an 'else'}}{{31-31=\n static func buildOptional(_ component: <#Component#>?) -> <#Component#> {\n <#code#>\n \}}} - // expected-note@-2{{add 'buildEither(first:)' and 'buildEither(second:)' to the function builder 'TupleBuilderWithoutIf' to add support for 'if'-'else' and 'switch'}}{{31-31=\n static func buildEither(first component: <#Component#>) -> <#Component#> {\n <#code#>\n \}\n\n static func buildEither(second component: <#Component#>) -> <#Component#> {\n <#code#>\n \}}} - // expected-note@-3{{add 'buildArray(_:)' to the function builder 'TupleBuilderWithoutIf' to add support for 'for'..'in' loops}}{{31-31=\n static func buildArray(_ components: [<#Component#>]) -> <#Component#> {\n <#code#>\n \}}} + // expected-note@-1{{add 'buildOptional(_:)' to the result builder 'TupleBuilderWithoutIf' to add support for 'if' statements without an 'else'}}{{31-31=\n static func buildOptional(_ component: <#Component#>?) -> <#Component#> {\n <#code#>\n \}}} + // expected-note@-2{{add 'buildEither(first:)' and 'buildEither(second:)' to the result builder 'TupleBuilderWithoutIf' to add support for 'if'-'else' and 'switch'}}{{31-31=\n static func buildEither(first component: <#Component#>) -> <#Component#> {\n <#code#>\n \}\n\n static func buildEither(second component: <#Component#>) -> <#Component#> {\n <#code#>\n \}}} + // expected-note@-3{{add 'buildArray(_:)' to the result builder 'TupleBuilderWithoutIf' to add support for 'for'..'in' loops}}{{31-31=\n static func buildArray(_ components: [<#Component#>]) -> <#Component#> {\n <#code#>\n \}}} static func buildBlock() -> () { } static func buildBlock(_ t1: T1) -> T1 { @@ -44,20 +44,20 @@ func tuplifyWithoutIf(_ cond: Bool, @TupleBuilderWithoutIf body: (Bool) -> T) func testDiags() { // Statements unsupported by the particular builder. tuplifyWithoutIf(true) { - if $0 { // expected-error{{closure containing control flow statement cannot be used with function builder 'TupleBuilderWithoutIf'}} + if $0 { // expected-error{{closure containing control flow statement cannot be used with result builder 'TupleBuilderWithoutIf'}} "hello" } } tuplifyWithoutIf(true) { - if $0 { // expected-error{{closure containing control flow statement cannot be used with function builder 'TupleBuilderWithoutIf'}} + if $0 { // expected-error{{closure containing control flow statement cannot be used with result builder 'TupleBuilderWithoutIf'}} "hello" } else { } } tuplifyWithoutIf(true) { a in - for x in 0..<100 { // expected-error{{closure containing control flow statement cannot be used with function builder 'TupleBuilderWithoutIf'}} + for x in 0..<100 { // expected-error{{closure containing control flow statement cannot be used with result builder 'TupleBuilderWithoutIf'}} x } } diff --git a/test/decl/var/function_builders.swift b/test/decl/var/function_builders.swift index d0bb1b8cb8abd..bb74051ca867c 100644 --- a/test/decl/var/function_builders.swift +++ b/test/decl/var/function_builders.swift @@ -7,22 +7,22 @@ var globalBuilder: Int func globalBuilderFunction() -> Int { return 0 } @resultBuilder -struct Maker {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} +struct Maker {} // expected-error {{result builder must provide at least one static 'buildBlock' method}} @resultBuilder -class Inventor {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} +class Inventor {} // expected-error {{result builder must provide at least one static 'buildBlock' method}} -@Maker // expected-error {{function builder attribute 'Maker' can only be applied to a parameter, function, or computed property}} +@Maker // expected-error {{result builder attribute 'Maker' can only be applied to a parameter, function, or computed property}} typealias typename = Inventor -@Maker // expected-error {{function builder attribute 'Maker' can only be applied to a variable if it defines a getter}} +@Maker // expected-error {{result builder attribute 'Maker' can only be applied to a variable if it defines a getter}} var global: Int // FIXME: should this be allowed? @Maker var globalWithEmptyImplicitGetter: Int {} // expected-error@-1 {{computed property must have accessors specified}} -// expected-error@-3 {{function builder attribute 'Maker' can only be applied to a variable if it defines a getter}} +// expected-error@-3 {{result builder attribute 'Maker' can only be applied to a variable if it defines a getter}} @Maker var globalWithEmptyExplicitGetter: Int { get {} } // expected-error{{type 'Maker' has no member 'buildBlock'}} @@ -43,12 +43,12 @@ func makerParam(@Maker fn: () -> ()) {} // FIXME: these diagnostics are reversed? -func makerParamRedundant(@Maker // expected-error {{only one function builder attribute can be attached to a parameter}} - @Maker // expected-note {{previous function builder specified here}} +func makerParamRedundant(@Maker // expected-error {{only one result builder attribute can be attached to a parameter}} + @Maker // expected-note {{previous result builder specified here}} fn: () -> ()) {} -func makerParamConflict(@Maker // expected-error {{only one function builder attribute can be attached to a parameter}} - @Inventor // expected-note {{previous function builder specified here}} +func makerParamConflict(@Maker // expected-error {{only one result builder attribute can be attached to a parameter}} + @Inventor // expected-note {{previous result builder specified here}} fn: () -> ()) {} func makerParamMissing1(@Missing // expected-error {{unknown attribute 'Missing'}} @@ -59,18 +59,18 @@ func makerParamMissing2(@Maker @Missing // expected-error {{unknown attribute 'Missing'}} fn: () -> ()) {} -func makerParamExtra(@Maker(5) // expected-error {{function builder attributes cannot have arguments}} +func makerParamExtra(@Maker(5) // expected-error {{result builder attributes cannot have arguments}} fn: () -> ()) {} -func makerParamAutoclosure(@Maker // expected-error {{function builder attribute 'Maker' cannot be applied to an autoclosure parameter}} +func makerParamAutoclosure(@Maker // expected-error {{result builder attribute 'Maker' cannot be applied to an autoclosure parameter}} fn: @autoclosure () -> ()) {} @resultBuilder -struct GenericMaker {} // expected-note {{generic type 'GenericMaker' declared here}} expected-error {{function builder must provide at least one static 'buildBlock' method}} +struct GenericMaker {} // expected-note {{generic type 'GenericMaker' declared here}} expected-error {{result builder must provide at least one static 'buildBlock' method}} struct GenericContainer { // expected-note {{generic type 'GenericContainer' declared here}} @resultBuilder - struct Maker {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} + struct Maker {} // expected-error {{result builder must provide at least one static 'buildBlock' method}} } func makeParamUnbound(@GenericMaker // expected-error {{reference to generic type 'GenericMaker' requires arguments}} @@ -89,7 +89,7 @@ func makeParamNestedBound(@GenericContainer.Maker protocol P { } @resultBuilder -struct ConstrainedGenericMaker {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} +struct ConstrainedGenericMaker {} // expected-error {{result builder must provide at least one static 'buildBlock' method}} struct WithinGeneric { @@ -133,20 +133,20 @@ struct ValidBuilder5 { } @resultBuilder -struct InvalidBuilder1 {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} +struct InvalidBuilder1 {} // expected-error {{result builder must provide at least one static 'buildBlock' method}} @resultBuilder -struct InvalidBuilder2 { // expected-error {{function builder must provide at least one static 'buildBlock' method}} +struct InvalidBuilder2 { // expected-error {{result builder must provide at least one static 'buildBlock' method}} func buildBlock(_ exprs: Any...) -> Int { return exprs.count } // expected-note {{did you mean to make instance method 'buildBlock' static?}} {{3-3=static }} } @resultBuilder -struct InvalidBuilder3 { // expected-error {{function builder must provide at least one static 'buildBlock' method}} +struct InvalidBuilder3 { // expected-error {{result builder must provide at least one static 'buildBlock' method}} var buildBlock: (Any...) -> Int = { return $0.count } // expected-note {{potential match 'buildBlock' is not a static method}} } @resultBuilder -struct InvalidBuilder4 {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} +struct InvalidBuilder4 {} // expected-error {{result builder must provide at least one static 'buildBlock' method}} extension InvalidBuilder4 { func buildBlock(_ exprs: Any...) -> Int { return exprs.count } // expected-note {{did you mean to make instance method 'buildBlock' static?}} {{3-3=static }} } @@ -157,10 +157,10 @@ extension InvalidBuilderHelper { } @resultBuilder -struct InvalidBuilder5: InvalidBuilderHelper {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} +struct InvalidBuilder5: InvalidBuilderHelper {} // expected-error {{result builder must provide at least one static 'buildBlock' method}} @resultBuilder -struct InvalidBuilder6 { // expected-error {{function builder must provide at least one static 'buildBlock' method}} +struct InvalidBuilder6 { // expected-error {{result builder must provide at least one static 'buildBlock' method}} static var buildBlock: Int = 0 // expected-note {{potential match 'buildBlock' is not a static method}} } @@ -169,7 +169,7 @@ struct Callable { } @resultBuilder -struct InvalidBuilder7 { // expected-error {{function builder must provide at least one static 'buildBlock' method}} +struct InvalidBuilder7 { // expected-error {{result builder must provide at least one static 'buildBlock' method}} static var buildBlock = Callable() // expected-note {{potential match 'buildBlock' is not a static method}} } @@ -178,7 +178,7 @@ class BuilderVarBase { } @resultBuilder -class InvalidBuilder8: BuilderVarBase {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} +class InvalidBuilder8: BuilderVarBase {} // expected-error {{result builder must provide at least one static 'buildBlock' method}} protocol BuilderVarHelper {} @@ -187,16 +187,16 @@ extension BuilderVarHelper { } @resultBuilder -struct InvalidBuilder9: BuilderVarHelper {} // expected-error {{function builder must provide at least one static 'buildBlock' method}} +struct InvalidBuilder9: BuilderVarHelper {} // expected-error {{result builder must provide at least one static 'buildBlock' method}} @resultBuilder -struct InvalidBuilder10 { // expected-error {{function builder must provide at least one static 'buildBlock' method}} +struct InvalidBuilder10 { // expected-error {{result builder must provide at least one static 'buildBlock' method}} static var buildBlock: (Any...) -> Int = { return $0.count } // expected-note {{potential match 'buildBlock' is not a static method}} } @resultBuilder -enum InvalidBuilder11 { // expected-error {{function builder must provide at least one static 'buildBlock' method}} - case buildBlock(Any) // expected-note {{enum case 'buildBlock' cannot be used to satisfy the function builder requirement}} +enum InvalidBuilder11 { // expected-error {{result builder must provide at least one static 'buildBlock' method}} + case buildBlock(Any) // expected-note {{enum case 'buildBlock' cannot be used to satisfy the result builder requirement}} } struct S { diff --git a/test/multifile/Inputs/function_builder_definition.swift b/test/multifile/Inputs/result_builder_definition.swift similarity index 100% rename from test/multifile/Inputs/function_builder_definition.swift rename to test/multifile/Inputs/result_builder_definition.swift diff --git a/test/multifile/function_builder_multifile.swift b/test/multifile/function_builder_multifile.swift deleted file mode 100644 index c3c7038823aca..0000000000000 --- a/test/multifile/function_builder_multifile.swift +++ /dev/null @@ -1,9 +0,0 @@ -// RUN: %target-swift-frontend -typecheck %S/Inputs/function_builder_definition.swift -primary-file %s - -func test0() -> (Int, Double, String) { - return tuplify { - 17 - 3.14159 - "Hello" - } -} diff --git a/test/multifile/result_builder_multifile.swift b/test/multifile/result_builder_multifile.swift new file mode 100644 index 0000000000000..b0d34214748f9 --- /dev/null +++ b/test/multifile/result_builder_multifile.swift @@ -0,0 +1,9 @@ +// RUN: %target-swift-frontend -typecheck %S/Inputs/result_builder_definition.swift -primary-file %s + +func test0() -> (Int, Double, String) { + return tuplify { + 17 + 3.14159 + "Hello" + } +} diff --git a/userdocs/diagnostics/function-builder-methods.md b/userdocs/diagnostics/function-builder-methods.md index a9e7970421a2b..86f4664b7fc4c 100644 --- a/userdocs/diagnostics/function-builder-methods.md +++ b/userdocs/diagnostics/function-builder-methods.md @@ -1,9 +1,9 @@ # Function Builder Methods -To be useful as a function builder, a function builder type must provide a +To be useful as a result builder, a result builder type must provide a sufficient subset of function-building methods that enable the transformation of various statement kinds (`if`, `switch`, `for`..`in`, etc.). The following -example function builder illustrates the various function-building methods one +example result builder illustrates the various function-building methods one can define: ```swift @@ -21,7 +21,7 @@ struct ExampleFunctionBuilder { /// buildFinalResult() is not provided. typealias Result = ... - /// Required by every function builder to build combined results from + /// Required by every result builder to build combined results from /// statement blocks. static func buildBlock(_ components: Component...) -> Component { ... } @@ -40,12 +40,12 @@ struct ExampleFunctionBuilder { /// statements by folding conditional results into a single result. static func buildEither(second component: Component) -> Component { ... } - /// Enables support for..in loops in a function builder by combining the + /// Enables support for..in loops in a result builder by combining the /// results of all iterations into a single result. static func buildArray(_ components: [Component]) -> Component { ... } /// If declared, this will be called on the partial result of an 'if - /// #available' block to allow the function builder to erase type + /// #available' block to allow the result builder to erase type /// information. static func buildLimitedAvailability(_ component: Component) -> Component { ... } From 6d41524fe66c21c98165a6836848736fb0aef929 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 20 Oct 2020 22:18:51 -0700 Subject: [PATCH 604/745] [SE-0289] Finish renaming source code, tests to "result builders" --- include/swift/AST/ASTTypeIDZone.def | 2 +- include/swift/AST/ASTTypeIDs.h | 2 +- include/swift/AST/Decl.h | 6 +- include/swift/AST/EducationalNotes.def | 10 +- include/swift/AST/TypeCheckRequests.h | 42 ++-- include/swift/AST/TypeCheckerTypeIDZone.def | 8 +- include/swift/Sema/CSFix.h | 27 ++- include/swift/Sema/ConstraintLocator.h | 2 +- .../swift/Sema/ConstraintLocatorPathElts.def | 2 +- include/swift/Sema/ConstraintSystem.h | 20 +- include/swift/Sema/IDETypeChecking.h | 12 +- lib/AST/Attr.cpp | 4 +- lib/AST/Decl.cpp | 8 +- lib/AST/TypeCheckRequests.cpp | 14 +- lib/IDE/CodeCompletion.cpp | 68 +++--- lib/SILGen/SILGenConstructor.cpp | 2 +- lib/Sema/BuilderTransform.cpp | 200 +++++++++--------- lib/Sema/CSBindings.cpp | 2 +- lib/Sema/CSClosure.cpp | 2 +- lib/Sema/CSDiagnostics.cpp | 30 +-- lib/Sema/CSDiagnostics.h | 4 +- lib/Sema/CSFix.cpp | 18 +- lib/Sema/CSSimplify.cpp | 22 +- lib/Sema/CSSolver.cpp | 12 +- lib/Sema/CodeSynthesis.cpp | 8 +- lib/Sema/ConstraintLocator.cpp | 8 +- lib/Sema/ConstraintSystem.cpp | 2 +- lib/Sema/TypeCheckAttr.cpp | 14 +- lib/Sema/TypeCheckRequestFunctions.cpp | 48 ++--- lib/Sema/TypeCheckStmt.cpp | 14 +- lib/Sema/TypeChecker.h | 2 +- ...ion_builder.swift => result_builder.swift} | 4 +- ...wift => result_builder_availability.swift} | 0 ...diags.swift => result_builder_diags.swift} | 0 ...infer.swift => result_builder_infer.swift} | 0 ...way.swift => result_builder_one_way.swift} | 0 ...ift => result_builder_opaque_result.swift} | 0 ...er.swift => complete_result_builder.swift} | 0 ...n_builders.swift => result_builders.swift} | 0 ...ent.swift => result_builders_client.swift} | 0 ...n_builders.swift => result_builders.swift} | 0 ...r.swift => converage_result_builder.swift} | 0 test/Sema/type_eraser.swift | 4 +- ...ion_builder.swift => result_builder.swift} | 0 ...xits.swift => result_builder_fixits.swift} | 0 ...n_builders.swift => result_builders.swift} | 0 ...ift => result_builders_availability.swift} | 0 tools/swift-ast-script/ASTScriptEvaluator.cpp | 2 +- ...r-methods.md => result-builder-methods.md} | 4 +- 49 files changed, 314 insertions(+), 315 deletions(-) rename test/Constraints/{function_builder.swift => result_builder.swift} (99%) rename test/Constraints/{function_builder_availability.swift => result_builder_availability.swift} (100%) rename test/Constraints/{function_builder_diags.swift => result_builder_diags.swift} (100%) rename test/Constraints/{function_builder_infer.swift => result_builder_infer.swift} (100%) rename test/Constraints/{function_builder_one_way.swift => result_builder_one_way.swift} (100%) rename test/Constraints/{function_builder_opaque_result.swift => result_builder_opaque_result.swift} (100%) rename test/IDE/{complete_function_builder.swift => complete_result_builder.swift} (100%) rename test/Index/{function_builders.swift => result_builders.swift} (100%) rename test/ModuleInterface/Inputs/{function_builders_client.swift => result_builders_client.swift} (100%) rename test/ModuleInterface/{function_builders.swift => result_builders.swift} (100%) rename test/Profiler/{coverage_function_builder.swift => converage_result_builder.swift} (100%) rename test/SourceKit/CursorInfo/{function_builder.swift => result_builder.swift} (100%) rename test/decl/{function_builder_fixits.swift => result_builder_fixits.swift} (100%) rename test/decl/var/{function_builders.swift => result_builders.swift} (100%) rename test/decl/var/{function_builders_availability.swift => result_builders_availability.swift} (100%) rename userdocs/diagnostics/{function-builder-methods.md => result-builder-methods.md} (97%) diff --git a/include/swift/AST/ASTTypeIDZone.def b/include/swift/AST/ASTTypeIDZone.def index d4ebede4903ec..ab542436be917 100644 --- a/include/swift/AST/ASTTypeIDZone.def +++ b/include/swift/AST/ASTTypeIDZone.def @@ -20,7 +20,7 @@ SWIFT_TYPEID(AncestryFlags) SWIFT_TYPEID(BodyInitKind) SWIFT_TYPEID(BodyInitKindAndExpr) SWIFT_TYPEID(CtorInitializerKind) -SWIFT_TYPEID(FunctionBuilderBodyPreCheck) +SWIFT_TYPEID(ResultBuilderBodyPreCheck) SWIFT_TYPEID(GenericSignature) SWIFT_TYPEID(ImplicitImportList) SWIFT_TYPEID(ImplicitMemberAction) diff --git a/include/swift/AST/ASTTypeIDs.h b/include/swift/AST/ASTTypeIDs.h index 94323e9353913..76bc732b975d1 100644 --- a/include/swift/AST/ASTTypeIDs.h +++ b/include/swift/AST/ASTTypeIDs.h @@ -35,7 +35,7 @@ class CustomAttr; class Decl; class EnumDecl; class FuncDecl; -enum class FunctionBuilderBodyPreCheck : uint8_t; +enum class ResultBuilderBodyPreCheck : uint8_t; class GenericParamList; class GenericSignature; class GenericTypeParamType; diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index fd5d157328818..de0cc5c196baa 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -2403,11 +2403,11 @@ class ValueDecl : public Decl { /// Retrieve the attribute associating this declaration with a /// result builder, if there is one. - CustomAttr *getAttachedFunctionBuilder() const; + CustomAttr *getAttachedResultBuilder() const; - /// Retrieve the @functionBuilder type attached to this declaration, + /// Retrieve the @resultBuilder type attached to this declaration, /// if there is one. - Type getFunctionBuilderType() const; + Type getResultBuilderType() const; /// If this value or its backing storage is annotated /// @_dynamicReplacement(for: ...), compute the original declaration diff --git a/include/swift/AST/EducationalNotes.def b/include/swift/AST/EducationalNotes.def index 7a79d86239386..b2a2914e85afb 100644 --- a/include/swift/AST/EducationalNotes.def +++ b/include/swift/AST/EducationalNotes.def @@ -82,14 +82,14 @@ EDUCATIONAL_NOTES(unlabeled_trailing_closure_deprecated, "trailing-closure-matching.md") EDUCATIONAL_NOTES(result_builder_static_buildblock, - "function-builder-methods.md") + "result-builder-methods.md") EDUCATIONAL_NOTES(result_builder_missing_limited_availability, - "function-builder-methods.md") + "result-builder-methods.md") EDUCATIONAL_NOTES(result_builder_missing_build_optional, - "function-builder-methods.md") + "result-builder-methods.md") EDUCATIONAL_NOTES(result_builder_missing_build_either, - "function-builder-methods.md") + "result-builder-methods.md") EDUCATIONAL_NOTES(result_builder_missing_build_array, - "function-builder-methods.md") + "result-builder-methods.md") #undef EDUCATIONAL_NOTES diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 187bc9c47c1f3..d053f58890e61 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -737,8 +737,8 @@ void simple_display(llvm::raw_ostream &out, ResilienceExpansion value); /// Request the custom attribute which attaches a result builder to the /// given declaration. -class AttachedFunctionBuilderRequest : - public SimpleRequest { public: @@ -758,8 +758,8 @@ class AttachedFunctionBuilderRequest : /// Request the result builder type attached to the given declaration, /// if any. -class FunctionBuilderTypeRequest : - public SimpleRequest { public: @@ -1960,7 +1960,7 @@ class ValueWitnessRequest void cacheResult(Witness value) const; }; -struct PreCheckFunctionBuilderDescriptor { +struct PreCheckResultBuilderDescriptor { AnyFunctionRef Fn; bool SuppressDiagnostics; @@ -1972,35 +1972,35 @@ struct PreCheckFunctionBuilderDescriptor { BraceStmt *Body; public: - PreCheckFunctionBuilderDescriptor(AnyFunctionRef Fn, bool suppressDiagnostics) + PreCheckResultBuilderDescriptor(AnyFunctionRef Fn, bool suppressDiagnostics) : Fn(Fn), SuppressDiagnostics(suppressDiagnostics), Body(Fn.getBody()) {} friend llvm::hash_code - hash_value(const PreCheckFunctionBuilderDescriptor &owner) { + hash_value(const PreCheckResultBuilderDescriptor &owner) { return llvm::hash_combine(owner.Fn, owner.Body); } - friend bool operator==(const PreCheckFunctionBuilderDescriptor &lhs, - const PreCheckFunctionBuilderDescriptor &rhs) { + friend bool operator==(const PreCheckResultBuilderDescriptor &lhs, + const PreCheckResultBuilderDescriptor &rhs) { return lhs.Fn == rhs.Fn && lhs.Body == rhs.Body; } - friend bool operator!=(const PreCheckFunctionBuilderDescriptor &lhs, - const PreCheckFunctionBuilderDescriptor &rhs) { + friend bool operator!=(const PreCheckResultBuilderDescriptor &lhs, + const PreCheckResultBuilderDescriptor &rhs) { return !(lhs == rhs); } - friend SourceLoc extractNearestSourceLoc(PreCheckFunctionBuilderDescriptor d) { + friend SourceLoc extractNearestSourceLoc(PreCheckResultBuilderDescriptor d) { return extractNearestSourceLoc(d.Fn); } friend void simple_display(llvm::raw_ostream &out, - const PreCheckFunctionBuilderDescriptor &d) { + const PreCheckResultBuilderDescriptor &d) { simple_display(out, d.Fn); } }; -enum class FunctionBuilderBodyPreCheck : uint8_t { +enum class ResultBuilderBodyPreCheck : uint8_t { /// There were no problems pre-checking the closure. Okay, @@ -2011,10 +2011,10 @@ enum class FunctionBuilderBodyPreCheck : uint8_t { HasReturnStmt, }; -class PreCheckFunctionBuilderRequest - : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -2023,8 +2023,8 @@ class PreCheckFunctionBuilderRequest friend SimpleRequest; // Evaluation. - FunctionBuilderBodyPreCheck - evaluate(Evaluator &evaluator, PreCheckFunctionBuilderDescriptor owner) const; + ResultBuilderBodyPreCheck + evaluate(Evaluator &evaluator, PreCheckResultBuilderDescriptor owner) const; public: // Separate caching. @@ -2759,7 +2759,7 @@ AnyValue::Holder::equals(const HolderBase &other) const { void simple_display(llvm::raw_ostream &out, Type value); void simple_display(llvm::raw_ostream &out, const TypeRepr *TyR); void simple_display(llvm::raw_ostream &out, ImplicitMemberAction action); -void simple_display(llvm::raw_ostream &out, FunctionBuilderBodyPreCheck pck); +void simple_display(llvm::raw_ostream &out, ResultBuilderBodyPreCheck pck); #define SWIFT_TYPEID_ZONE TypeChecker #define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def" diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index b5a33543cfb06..ebfafc30dd6c5 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -20,7 +20,7 @@ SWIFT_REQUEST(TypeChecker, AbstractGenericSignatureRequest, SmallVector, SmallVector), Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, AttachedFunctionBuilderRequest, +SWIFT_REQUEST(TypeChecker, AttachedResultBuilderRequest, CustomAttr *(ValueDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, AttachedPropertyWrapperTypeRequest, Type(VarDecl *, unsigned), Cached, NoLocationInfo) @@ -81,7 +81,7 @@ SWIFT_REQUEST(TypeChecker, ExistentialTypeSupportedRequest, bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ExtendedTypeRequest, Type(ExtensionDecl *), Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, FunctionBuilderTypeRequest, Type(ValueDecl *), +SWIFT_REQUEST(TypeChecker, ResultBuilderTypeRequest, Type(ValueDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, IsAsyncHandlerRequest, bool(FuncDecl *), Cached, NoLocationInfo) @@ -249,8 +249,8 @@ SWIFT_REQUEST(TypeChecker, HasUserDefinedDesignatedInitRequest, bool(NominalTypeDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, HasMemberwiseInitRequest, bool(StructDecl *), Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, PreCheckFunctionBuilderRequest, - FunctionBuilderClosurePreCheck(PreCheckFunctionBuilderDescriptor), +SWIFT_REQUEST(TypeChecker, PreCheckResultBuilderRequest, + ResultBuilderBodyPreCheck(PreCheckResultBuilderDescriptor), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ResolveImplicitMemberRequest, evaluator::SideEffect(NominalTypeDecl *, ImplicitMemberAction), diff --git a/include/swift/Sema/CSFix.h b/include/swift/Sema/CSFix.h index d41fe99c49770..f65cbaf9c702a 100644 --- a/include/swift/Sema/CSFix.h +++ b/include/swift/Sema/CSFix.h @@ -193,9 +193,8 @@ enum class FixKind : uint8_t { DefaultGenericArgument, /// Skip any unhandled constructs that occur within a closure argument that - /// matches up with a - /// parameter that has a result builder. - SkipUnhandledConstructInFunctionBuilder, + /// matches up with a parameter that has a result builder. + SkipUnhandledConstructInResultBuilder, /// Allow invalid reference to a member declared as `mutating` /// when base is an r-value type. @@ -278,7 +277,7 @@ enum class FixKind : uint8_t { AllowKeyPathWithoutComponents, /// Ignore result builder body which fails `pre-check` call. - IgnoreInvalidFunctionBuilderBody, + IgnoreInvalidResultBuilderBody, /// Resolve type of `nil` by providing a contextual type. SpecifyContextualTypeForNil, @@ -1500,7 +1499,7 @@ class DefaultGenericArgument final : public ConstraintFix { ConstraintLocator *locator); }; -class SkipUnhandledConstructInFunctionBuilder final : public ConstraintFix { +class SkipUnhandledConstructInResultBuilder final : public ConstraintFix { public: using UnhandledNode = llvm::PointerUnion; @@ -1508,11 +1507,11 @@ class SkipUnhandledConstructInFunctionBuilder final : public ConstraintFix { UnhandledNode unhandled; NominalTypeDecl *builder; - SkipUnhandledConstructInFunctionBuilder(ConstraintSystem &cs, + SkipUnhandledConstructInResultBuilder(ConstraintSystem &cs, UnhandledNode unhandled, NominalTypeDecl *builder, ConstraintLocator *locator) - : ConstraintFix(cs, FixKind::SkipUnhandledConstructInFunctionBuilder, + : ConstraintFix(cs, FixKind::SkipUnhandledConstructInResultBuilder, locator), unhandled(unhandled), builder(builder) { } @@ -1523,7 +1522,7 @@ class SkipUnhandledConstructInFunctionBuilder final : public ConstraintFix { bool diagnose(const Solution &solution, bool asNote = false) const override; - static SkipUnhandledConstructInFunctionBuilder * + static SkipUnhandledConstructInResultBuilder * create(ConstraintSystem &cs, UnhandledNode unhandledNode, NominalTypeDecl *builder, ConstraintLocator *locator); }; @@ -1997,7 +1996,7 @@ class AllowKeyPathWithoutComponents final : public ConstraintFix { ConstraintLocator *locator); }; -class IgnoreInvalidFunctionBuilderBody final : public ConstraintFix { +class IgnoreInvalidResultBuilderBody final : public ConstraintFix { enum class ErrorInPhase { PreCheck, ConstraintGeneration, @@ -2005,9 +2004,9 @@ class IgnoreInvalidFunctionBuilderBody final : public ConstraintFix { ErrorInPhase Phase; - IgnoreInvalidFunctionBuilderBody(ConstraintSystem &cs, ErrorInPhase phase, + IgnoreInvalidResultBuilderBody(ConstraintSystem &cs, ErrorInPhase phase, ConstraintLocator *locator) - : ConstraintFix(cs, FixKind::IgnoreInvalidFunctionBuilderBody, locator), + : ConstraintFix(cs, FixKind::IgnoreInvalidResultBuilderBody, locator), Phase(phase) {} public: @@ -2021,18 +2020,18 @@ class IgnoreInvalidFunctionBuilderBody final : public ConstraintFix { return diagnose(*commonFixes.front().first); } - static IgnoreInvalidFunctionBuilderBody * + static IgnoreInvalidResultBuilderBody * duringPreCheck(ConstraintSystem &cs, ConstraintLocator *locator) { return create(cs, ErrorInPhase::PreCheck, locator); } - static IgnoreInvalidFunctionBuilderBody * + static IgnoreInvalidResultBuilderBody * duringConstraintGeneration(ConstraintSystem &cs, ConstraintLocator *locator) { return create(cs, ErrorInPhase::ConstraintGeneration, locator); } private: - static IgnoreInvalidFunctionBuilderBody * + static IgnoreInvalidResultBuilderBody * create(ConstraintSystem &cs, ErrorInPhase phase, ConstraintLocator *locator); }; diff --git a/include/swift/Sema/ConstraintLocator.h b/include/swift/Sema/ConstraintLocator.h index 9bfca53fb3f98..2d1b01586a2ed 100644 --- a/include/swift/Sema/ConstraintLocator.h +++ b/include/swift/Sema/ConstraintLocator.h @@ -223,7 +223,7 @@ class ConstraintLocator : public llvm::FoldingSetNode { bool isForOptionalTry() const; /// Determine whether this locator is for a result builder body result type. - bool isForFunctionBuilderBodyResult() const; + bool isForResultBuilderBodyResult() const; /// Determine whether this locator points directly to a given expression. template bool directlyAt() const { diff --git a/include/swift/Sema/ConstraintLocatorPathElts.def b/include/swift/Sema/ConstraintLocatorPathElts.def index ad5da60f8e7a5..bafa8c4a0bd78 100644 --- a/include/swift/Sema/ConstraintLocatorPathElts.def +++ b/include/swift/Sema/ConstraintLocatorPathElts.def @@ -76,7 +76,7 @@ SIMPLE_LOCATOR_PATH_ELT(FunctionArgument) SIMPLE_LOCATOR_PATH_ELT(FunctionResult) /// The result type of a result builder body. -SIMPLE_LOCATOR_PATH_ELT(FunctionBuilderBodyResult) +SIMPLE_LOCATOR_PATH_ELT(ResultBuilderBodyResult) /// A generic argument. /// FIXME: Add support for named generic arguments? diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index dc65254554fdf..543582d078485 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -76,7 +76,7 @@ class SemaTest; // so they could be made friends of ConstraintSystem. namespace TypeChecker { -Optional applyFunctionBuilderBodyTransform(FuncDecl *func, +Optional applyResultBuilderBodyTransform(FuncDecl *func, Type builderType); Optional @@ -1186,7 +1186,7 @@ class Solution { /// The set of functions that have been transformed by a result builder. llvm::MapVector - functionBuilderTransformed; + resultBuilderTransformed; /// Simplify the given type by substituting all occurrences of /// type variables for their fixed types. @@ -1300,8 +1300,8 @@ class Solution { /// Retrieve the builder transform that was applied to this function, if any. const AppliedBuilderTransform *getAppliedBuilderTransform( AnyFunctionRef fn) const { - auto known = functionBuilderTransformed.find(fn); - return known != functionBuilderTransformed.end() + auto known = resultBuilderTransformed.find(fn); + return known != resultBuilderTransformed.end() ? &known->second : nullptr; } @@ -2130,7 +2130,7 @@ class ConstraintSystem { /// /// Tracking this information is useful to avoid producing duplicate /// diagnostics when result builder has multiple overloads. - llvm::SmallDenseSet InvalidFunctionBuilderBodies; + llvm::SmallDenseSet InvalidResultBuilderBodies; /// Maps node types used within all portions of the constraint /// system, instead of directly using the types on the @@ -2220,7 +2220,7 @@ class ConstraintSystem { /// The set of functions that have been transformed by a result builder. std::vector> - functionBuilderTransformed; + resultBuilderTransformed; /// Cache of the effects any closures visited. llvm::SmallDenseMap closureEffectsCache; @@ -2687,7 +2687,7 @@ class ConstraintSystem { unsigned numFavoredConstraints; - unsigned numFunctionBuilderTransformed; + unsigned numResultBuilderTransformed; /// The length of \c ResolvedOverloads. unsigned numResolvedOverloads; @@ -2759,7 +2759,7 @@ class ConstraintSystem { // FIXME: Perhaps these belong on ConstraintSystem itself. friend Optional - swift::TypeChecker::applyFunctionBuilderBodyTransform(FuncDecl *func, + swift::TypeChecker::applyResultBuilderBodyTransform(FuncDecl *func, Type builderType); friend Optional @@ -4607,7 +4607,7 @@ class ConstraintSystem { /// /// \returns \c None when the result builder cannot be applied at all, /// otherwise the result of applying the result builder. - Optional matchFunctionBuilder( + Optional matchResultBuilder( AnyFunctionRef fn, Type builderType, Type bodyResultType, ConstraintKind bodyResultConstraintKind, ConstraintLocatorBuilder locator); @@ -6002,7 +6002,7 @@ std::string describeGenericType(ValueDecl *GP, bool includeName = false); /// type-checked version. /// /// \returns the transformed body -BraceStmt *applyFunctionBuilderTransform( +BraceStmt *applyResultBuilderTransform( const constraints::Solution &solution, constraints::AppliedBuilderTransform applied, BraceStmt *body, diff --git a/include/swift/Sema/IDETypeChecking.h b/include/swift/Sema/IDETypeChecking.h index 83ab16d1738a2..dc8e9cbe3a22a 100644 --- a/include/swift/Sema/IDETypeChecking.h +++ b/include/swift/Sema/IDETypeChecking.h @@ -255,9 +255,9 @@ namespace swift { bool IncludeProtocolRequirements = true, bool Transitive = false); - /// Enumerates the various kinds of "build" functions within a function + /// Enumerates the various kinds of "build" functions within a result /// builder. - enum class FunctionBuilderBuildFunction { + enum class ResultBuilderBuildFunction { BuildBlock, BuildExpression, BuildOptional, @@ -270,19 +270,19 @@ namespace swift { /// Try to infer the component type of a result builder from the type /// of buildBlock or buildExpression, if it was there. - Type inferFunctionBuilderComponentType(NominalTypeDecl *builder); + Type inferResultBuilderComponentType(NominalTypeDecl *builder); /// Print the declaration for a result builder "build" function, for use /// in Fix-Its, code completion, and so on. - void printFunctionBuilderBuildFunction( + void printResultBuilderBuildFunction( NominalTypeDecl *builder, Type componentType, - FunctionBuilderBuildFunction function, + ResultBuilderBuildFunction function, Optional stubIndent, llvm::raw_ostream &out); /// Compute the insertion location, indentation string, and component type /// for a Fix-It that adds a new build* function to a result builder. std::tuple - determineFunctionBuilderBuildFixItInfo(NominalTypeDecl *builder); + determineResultBuilderBuildFixItInfo(NominalTypeDecl *builder); } #endif diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index fb1e31f6a9d6a..8539adeecbc91 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -633,7 +633,7 @@ void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options, CustomAttr *FuncBuilderAttr = nullptr; if (auto *VD = dyn_cast_or_null(D)) { - FuncBuilderAttr = VD->getAttachedFunctionBuilder(); + FuncBuilderAttr = VD->getAttachedResultBuilder(); } for (auto DA : llvm::reverse(FlattenedAttrs)) { // Always print result builder attribute. @@ -744,7 +744,7 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, // on parameter decls and on protocol requirements. // Printing the attribute elsewhere isn't ABI relevant. if (auto *VD = dyn_cast(D)) { - if (VD->getAttachedFunctionBuilder() == this) { + if (VD->getAttachedResultBuilder() == this) { if (!isa(D) && !(isa(D) && isa(D->getDeclContext()))) return false; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 29079e477b1ef..e487fb81866cc 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6254,19 +6254,19 @@ void ParamDecl::setStoredProperty(VarDecl *var) { defaultInfo->DefaultArg = var; } -Type ValueDecl::getFunctionBuilderType() const { +Type ValueDecl::getResultBuilderType() const { auto &ctx = getASTContext(); auto mutableThis = const_cast(this); return evaluateOrDefault(ctx.evaluator, - FunctionBuilderTypeRequest{mutableThis}, + ResultBuilderTypeRequest{mutableThis}, Type()); } -CustomAttr *ValueDecl::getAttachedFunctionBuilder() const { +CustomAttr *ValueDecl::getAttachedResultBuilder() const { auto &ctx = getASTContext(); auto mutableThis = const_cast(this); return evaluateOrDefault(ctx.evaluator, - AttachedFunctionBuilderRequest{mutableThis}, + AttachedResultBuilderRequest{mutableThis}, nullptr); } diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 5626e803ce416..44fe6df453662 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -593,10 +593,10 @@ void swift::simple_display(llvm::raw_ostream &out, } //----------------------------------------------------------------------------// -// FunctionBuilder-related requests. +// ResultBuilder-related requests. //----------------------------------------------------------------------------// -bool AttachedFunctionBuilderRequest::isCached() const { +bool AttachedResultBuilderRequest::isCached() const { // Only needs to be cached if there are any custom attributes. auto var = std::get<0>(getStorage()); return var->getAttrs().hasAttribute(); @@ -1105,19 +1105,19 @@ void ValueWitnessRequest::cacheResult(Witness type) const { } //----------------------------------------------------------------------------// -// PreCheckFunctionBuilderRequest computation. +// PreCheckResultBuilderRequest computation. //----------------------------------------------------------------------------// void swift::simple_display(llvm::raw_ostream &out, - FunctionBuilderBodyPreCheck value) { + ResultBuilderBodyPreCheck value) { switch (value) { - case FunctionBuilderBodyPreCheck::Okay: + case ResultBuilderBodyPreCheck::Okay: out << "okay"; break; - case FunctionBuilderBodyPreCheck::HasReturnStmt: + case ResultBuilderBodyPreCheck::HasReturnStmt: out << "has return statement"; break; - case FunctionBuilderBodyPreCheck::Error: + case ResultBuilderBodyPreCheck::Error: out << "error"; break; } diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index dfbbff44f9587..c49be13a8a909 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -5124,48 +5124,48 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { } } - static StringRef getFunctionBuilderDocComment( - FunctionBuilderBuildFunction function) { + static StringRef getResultBuilderDocComment( + ResultBuilderBuildFunction function) { switch (function) { - case FunctionBuilderBuildFunction::BuildArray: + case ResultBuilderBuildFunction::BuildArray: return "Enables support for..in loops in a result builder by " "combining the results of all iterations into a single result"; - case FunctionBuilderBuildFunction::BuildBlock: + case ResultBuilderBuildFunction::BuildBlock: return "Required by every result builder to build combined results " "from statement blocks"; - case FunctionBuilderBuildFunction::BuildEitherFirst: + case ResultBuilderBuildFunction::BuildEitherFirst: return "With buildEither(second:), enables support for 'if-else' and " "'switch' statements by folding conditional results into a single " "result"; - case FunctionBuilderBuildFunction::BuildEitherSecond: + case ResultBuilderBuildFunction::BuildEitherSecond: return "With buildEither(first:), enables support for 'if-else' and " "'switch' statements by folding conditional results into a single " "result"; - case FunctionBuilderBuildFunction::BuildExpression: + case ResultBuilderBuildFunction::BuildExpression: return "If declared, provides contextual type information for statement " "expressions to translate them into partial results"; - case FunctionBuilderBuildFunction::BuildFinalResult: + case ResultBuilderBuildFunction::BuildFinalResult: return "If declared, this will be called on the partial result from the " "outermost block statement to produce the final returned result"; - case FunctionBuilderBuildFunction::BuildLimitedAvailability: + case ResultBuilderBuildFunction::BuildLimitedAvailability: return "If declared, this will be called on the partial result of " "an 'if #available' block to allow the result builder to erase " "type information"; - case FunctionBuilderBuildFunction::BuildOptional: + case ResultBuilderBuildFunction::BuildOptional: return "Enables support for `if` statements that do not have an `else`"; } } - void addFunctionBuilderBuildCompletion( + void addResultBuilderBuildCompletion( NominalTypeDecl *builder, Type componentType, - FunctionBuilderBuildFunction function) { + ResultBuilderBuildFunction function) { CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Pattern, @@ -5187,35 +5187,35 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { std::string declStringWithoutFunc; { llvm::raw_string_ostream out(declStringWithoutFunc); - printFunctionBuilderBuildFunction( + printResultBuilderBuildFunction( builder, componentType, function, None, out); } Builder.addTextChunk(declStringWithoutFunc); Builder.addBraceStmtWithCursor(); - Builder.setBriefDocComment(getFunctionBuilderDocComment(function)); + Builder.setBriefDocComment(getResultBuilderDocComment(function)); } /// Add completions for the various "build" functions in a result builder. - void addFunctionBuilderBuildCompletions(NominalTypeDecl *builder) { - Type componentType = inferFunctionBuilderComponentType(builder); - - addFunctionBuilderBuildCompletion( - builder, componentType, FunctionBuilderBuildFunction::BuildBlock); - addFunctionBuilderBuildCompletion( - builder, componentType, FunctionBuilderBuildFunction::BuildExpression); - addFunctionBuilderBuildCompletion( - builder, componentType, FunctionBuilderBuildFunction::BuildOptional); - addFunctionBuilderBuildCompletion( - builder, componentType, FunctionBuilderBuildFunction::BuildEitherFirst); - addFunctionBuilderBuildCompletion( - builder, componentType, FunctionBuilderBuildFunction::BuildEitherSecond); - addFunctionBuilderBuildCompletion( - builder, componentType, FunctionBuilderBuildFunction::BuildArray); - addFunctionBuilderBuildCompletion( + void addResultBuilderBuildCompletions(NominalTypeDecl *builder) { + Type componentType = inferResultBuilderComponentType(builder); + + addResultBuilderBuildCompletion( + builder, componentType, ResultBuilderBuildFunction::BuildBlock); + addResultBuilderBuildCompletion( + builder, componentType, ResultBuilderBuildFunction::BuildExpression); + addResultBuilderBuildCompletion( + builder, componentType, ResultBuilderBuildFunction::BuildOptional); + addResultBuilderBuildCompletion( + builder, componentType, ResultBuilderBuildFunction::BuildEitherFirst); + addResultBuilderBuildCompletion( + builder, componentType, ResultBuilderBuildFunction::BuildEitherSecond); + addResultBuilderBuildCompletion( + builder, componentType, ResultBuilderBuildFunction::BuildArray); + addResultBuilderBuildCompletion( builder, componentType, - FunctionBuilderBuildFunction::BuildLimitedAvailability); - addFunctionBuilderBuildCompletion( - builder, componentType, FunctionBuilderBuildFunction::BuildFinalResult); + ResultBuilderBuildFunction::BuildLimitedAvailability); + addResultBuilderBuildCompletion( + builder, componentType, ResultBuilderBuildFunction::BuildFinalResult); } void getOverrideCompletions(SourceLoc Loc) { @@ -5238,7 +5238,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { } if (NTD && NTD->getAttrs().hasAttribute()) { - addFunctionBuilderBuildCompletions(NTD); + addResultBuilderBuildCompletions(NTD); } } }; diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 1efe9b6340b0f..98559a7b45912 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -219,7 +219,7 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, // If the stored property has an attached result builder and its // type is not a function type, the argument is a noescape closure // that needs to be called. - if (field->getFunctionBuilderType()) { + if (field->getResultBuilderType()) { if (!field->getValueInterfaceType() ->lookThroughAllOptionalTypes()->is()) { auto resultTy = cast(arg.getType()).getResult(); diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 6b85c1db3ca60..e81d7f65057bf 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -1,4 +1,4 @@ -//===--- BuilderTransform.cpp - Function-builder transformation -----------===// +//===--- BuilderTransform.cpp - Result-builder transformation -----------===// // // This source file is part of the Swift.org open source project // @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// // -// This file implements routines associated with the function-builder +// This file implements routines associated with the result-builder // transformation. // //===----------------------------------------------------------------------===// @@ -73,7 +73,7 @@ class BuilderClosureVisitor Identifier buildOptionalId; llvm::SmallDenseMap supportedOps; - SkipUnhandledConstructInFunctionBuilder::UnhandledNode unhandledNode; + SkipUnhandledConstructInResultBuilder::UnhandledNode unhandledNode; /// Whether an error occurred during application of the builder closure, /// e.g., during constraint generation. @@ -253,7 +253,7 @@ class BuilderClosureVisitor /// Check whether the result builder can be applied to this statement. /// \returns the node that cannot be handled by this builder on failure. - SkipUnhandledConstructInFunctionBuilder::UnhandledNode check(Stmt *stmt) { + SkipUnhandledConstructInResultBuilder::UnhandledNode check(Stmt *stmt) { (void)visit(stmt); return unhandledNode; } @@ -770,7 +770,7 @@ class BuilderClosureVisitor // For-each statements require the Sequence protocol. If we don't have // it (which generally means the standard library isn't loaded), fall - // out of the function-builder path entirely to let normal type checking + // out of the result-builder path entirely to let normal type checking // take care of this. auto sequenceProto = TypeChecker::getProtocol( dc->getASTContext(), forEachStmt->getForLoc(), @@ -910,7 +910,7 @@ class BuilderClosureVisitor /// Describes the target into which the result of a particular statement in /// a closure involving a result builder should be written. -struct FunctionBuilderTarget { +struct ResultBuilderTarget { enum Kind { /// The resulting value is returned from the closure. ReturnValue, @@ -924,24 +924,24 @@ struct FunctionBuilderTarget { /// Captured variable information. std::pair> captured; - static FunctionBuilderTarget forReturn(Expr *expr) { - return FunctionBuilderTarget{ReturnValue, {nullptr, {expr}}}; + static ResultBuilderTarget forReturn(Expr *expr) { + return ResultBuilderTarget{ReturnValue, {nullptr, {expr}}}; } - static FunctionBuilderTarget forAssign(VarDecl *temporaryVar, + static ResultBuilderTarget forAssign(VarDecl *temporaryVar, llvm::TinyPtrVector exprs) { - return FunctionBuilderTarget{TemporaryVar, {temporaryVar, exprs}}; + return ResultBuilderTarget{TemporaryVar, {temporaryVar, exprs}}; } - static FunctionBuilderTarget forExpression(Expr *expr) { - return FunctionBuilderTarget{Expression, { nullptr, { expr }}}; + static ResultBuilderTarget forExpression(Expr *expr) { + return ResultBuilderTarget{Expression, { nullptr, { expr }}}; } }; /// Handles the rewrite of the body of a closure to which a result builder /// has been applied. class BuilderClosureRewriter - : public StmtVisitor { + : public StmtVisitor { ASTContext &ctx; const Solution &solution; DeclContext *dc; @@ -1001,12 +1001,12 @@ class BuilderClosureRewriter private: /// Build the statement or expression to initialize the target. - ASTNode initializeTarget(FunctionBuilderTarget target) { + ASTNode initializeTarget(ResultBuilderTarget target) { assert(target.captured.second.size() == 1); auto capturedExpr = target.captured.second.front(); SourceLoc implicitLoc = capturedExpr->getEndLoc(); switch (target.kind) { - case FunctionBuilderTarget::ReturnValue: { + case ResultBuilderTarget::ReturnValue: { // Return the expression. Type bodyResultType = solution.simplifyType(builderTransform.bodyResultType); @@ -1021,7 +1021,7 @@ class BuilderClosureRewriter return new (ctx) ReturnStmt(implicitLoc, resultExpr); } - case FunctionBuilderTarget::TemporaryVar: { + case ResultBuilderTarget::TemporaryVar: { // Assign the expression into a variable. auto temporaryVar = target.captured.first; auto declRef = new (ctx) DeclRefExpr( @@ -1041,7 +1041,7 @@ class BuilderClosureRewriter return assign; } - case FunctionBuilderTarget::Expression: + case ResultBuilderTarget::Expression: // Execute the expression. return rewriteExpr(capturedExpr); } @@ -1102,8 +1102,8 @@ class BuilderClosureRewriter solution(solution), dc(dc), builderTransform(builderTransform), rewriteTarget(rewriteTarget) { } - Stmt *visitBraceStmt(BraceStmt *braceStmt, FunctionBuilderTarget target, - Optional innerTarget = None) { + Stmt *visitBraceStmt(BraceStmt *braceStmt, ResultBuilderTarget target, + Optional innerTarget = None) { std::vector newElements; // If there is an "inner" target corresponding to this brace, declare @@ -1156,7 +1156,7 @@ class BuilderClosureRewriter Stmt *finalStmt = visit( stmt, - FunctionBuilderTarget{FunctionBuilderTarget::TemporaryVar, + ResultBuilderTarget{ResultBuilderTarget::TemporaryVar, std::move(captured)}); newElements.push_back(finalStmt); continue; @@ -1208,21 +1208,21 @@ class BuilderClosureRewriter braceStmt->getRBraceLoc()); } - Stmt *visitIfStmt(IfStmt *ifStmt, FunctionBuilderTarget target) { + Stmt *visitIfStmt(IfStmt *ifStmt, ResultBuilderTarget target) { // Rewrite the condition. if (auto condition = rewriteTarget( SolutionApplicationTarget(ifStmt->getCond(), dc))) ifStmt->setCond(*condition->getAsStmtCondition()); - assert(target.kind == FunctionBuilderTarget::TemporaryVar); + assert(target.kind == ResultBuilderTarget::TemporaryVar); auto temporaryVar = target.captured.first; // Translate the "then" branch. auto capturedThen = takeCapturedStmt(ifStmt->getThenStmt()); auto newThen = visitBraceStmt(cast(ifStmt->getThenStmt()), - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( temporaryVar, {target.captured.second[0]}), - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( capturedThen.first, {capturedThen.second.front()})); ifStmt->setThenStmt(newThen); @@ -1257,14 +1257,14 @@ class BuilderClosureRewriter std::string stubIndent; Type componentType; std::tie(buildInsertionLoc, stubIndent, componentType) = - determineFunctionBuilderBuildFixItInfo(builder); + determineResultBuilderBuildFixItInfo(builder); if (buildInsertionLoc.isValid()) { std::string fixItString; { llvm::raw_string_ostream out(fixItString); - printFunctionBuilderBuildFunction( + printResultBuilderBuildFunction( builder, componentType, - FunctionBuilderBuildFunction::BuildLimitedAvailability, + ResultBuilderBuildFunction::BuildLimitedAvailability, stubIndent, out); builder->diagnose( @@ -1288,9 +1288,9 @@ class BuilderClosureRewriter auto capturedElse = takeCapturedStmt(elseBraceStmt); Stmt *newElse = visitBraceStmt( elseBraceStmt, - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( temporaryVar, {target.captured.second[1]}), - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( capturedElse.first, {capturedElse.second.front()})); ifStmt->setElseStmt(newElse); } else if (auto elseIfStmt = cast_or_null(ifStmt->getElseStmt())){ @@ -1301,11 +1301,11 @@ class BuilderClosureRewriter newElseElements.push_back( visitIfStmt( elseIfStmt, - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( capturedElse.first, capturedElse.second))); newElseElements.push_back( initializeTarget( - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( temporaryVar, {target.captured.second[1]}))); Stmt *newElse = BraceStmt::create( @@ -1316,7 +1316,7 @@ class BuilderClosureRewriter // Form an "else" brace containing an assignment to the temporary // variable. auto init = initializeTarget( - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( temporaryVar, {target.captured.second[1]})); auto newElse = BraceStmt::create( ctx, ifStmt->getEndLoc(), { init }, ifStmt->getEndLoc()); @@ -1326,7 +1326,7 @@ class BuilderClosureRewriter return ifStmt; } - Stmt *visitDoStmt(DoStmt *doStmt, FunctionBuilderTarget target) { + Stmt *visitDoStmt(DoStmt *doStmt, ResultBuilderTarget target) { // Each statement turns into a (potential) temporary variable // binding followed by the statement itself. auto body = cast(doStmt->getBody()); @@ -1336,13 +1336,13 @@ class BuilderClosureRewriter visitBraceStmt( body, target, - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( captured.first, {captured.second.front()}))); doStmt->setBody(newInnerBody); return doStmt; } - Stmt *visitSwitchStmt(SwitchStmt *switchStmt, FunctionBuilderTarget target) { + Stmt *visitSwitchStmt(SwitchStmt *switchStmt, ResultBuilderTarget target) { // Translate the subject expression. ConstraintSystem &cs = solution.getConstraintSystem(); auto subjectTarget = @@ -1362,13 +1362,13 @@ class BuilderClosureRewriter // Translate all of the cases. bool limitExhaustivityChecks = false; - assert(target.kind == FunctionBuilderTarget::TemporaryVar); + assert(target.kind == ResultBuilderTarget::TemporaryVar); auto temporaryVar = target.captured.first; unsigned caseIndex = 0; for (auto caseStmt : switchStmt->getCases()) { if (!visitCaseStmt( caseStmt, - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( temporaryVar, {target.captured.second[caseIndex]}))) return nullptr; @@ -1387,7 +1387,7 @@ class BuilderClosureRewriter return switchStmt; } - Stmt *visitCaseStmt(CaseStmt *caseStmt, FunctionBuilderTarget target) { + Stmt *visitCaseStmt(CaseStmt *caseStmt, ResultBuilderTarget target) { // Translate the patterns and guard expressions for each case label item. for (auto &caseLabelItem : caseStmt->getMutableCaseLabelItems()) { SolutionApplicationTarget caseLabelTarget(&caseLabelItem, dc); @@ -1402,7 +1402,7 @@ class BuilderClosureRewriter visitBraceStmt( body, target, - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forAssign( captured.first, {captured.second.front()}))); caseStmt->setBody(newInnerBody); @@ -1410,7 +1410,7 @@ class BuilderClosureRewriter } Stmt *visitForEachStmt( - ForEachStmt *forEachStmt, FunctionBuilderTarget target) { + ForEachStmt *forEachStmt, ResultBuilderTarget target) { // Translate the for-each loop header. ConstraintSystem &cs = solution.getConstraintSystem(); auto forEachTarget = @@ -1443,8 +1443,8 @@ class BuilderClosureRewriter auto newBody = cast( visitBraceStmt( body, - FunctionBuilderTarget::forExpression(arrayAppendCall), - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forExpression(arrayAppendCall), + ResultBuilderTarget::forAssign( capturedBody.first, {capturedBody.second.front()}))); forEachStmt->setBody(newBody); outerBodySteps.push_back(forEachStmt); @@ -1454,7 +1454,7 @@ class BuilderClosureRewriter // the result builder. outerBodySteps.push_back( initializeTarget( - FunctionBuilderTarget::forAssign(finalForEachVar, {buildArrayCall}))); + ResultBuilderTarget::forAssign(finalForEachVar, {buildArrayCall}))); // Form a brace statement to put together the three main steps for the // for-each loop translation outlined above. @@ -1474,12 +1474,12 @@ class BuilderClosureRewriter return throwStmt; } - Stmt *visitThrowStmt(ThrowStmt *throwStmt, FunctionBuilderTarget target) { + Stmt *visitThrowStmt(ThrowStmt *throwStmt, ResultBuilderTarget target) { llvm_unreachable("Throw statements produce no value"); } #define UNHANDLED_RESULT_BUILDER_STMT(STMT) \ - Stmt *visit##STMT##Stmt(STMT##Stmt *stmt, FunctionBuilderTarget target) { \ + Stmt *visit##STMT##Stmt(STMT##Stmt *stmt, ResultBuilderTarget target) { \ llvm_unreachable("Function builders do not allow statement of kind " \ #STMT); \ } @@ -1501,7 +1501,7 @@ class BuilderClosureRewriter } // end anonymous namespace -BraceStmt *swift::applyFunctionBuilderTransform( +BraceStmt *swift::applyResultBuilderTransform( const Solution &solution, AppliedBuilderTransform applied, BraceStmt *body, @@ -1514,12 +1514,12 @@ BraceStmt *swift::applyFunctionBuilderTransform( return cast( rewriter.visitBraceStmt( body, - FunctionBuilderTarget::forReturn(applied.returnExpr), - FunctionBuilderTarget::forAssign( + ResultBuilderTarget::forReturn(applied.returnExpr), + ResultBuilderTarget::forAssign( captured.first, captured.second))); } -Optional TypeChecker::applyFunctionBuilderBodyTransform( +Optional TypeChecker::applyResultBuilderBodyTransform( FuncDecl *func, Type builderType) { // Pre-check the body: pre-check any expressions in it and look // for return statements. @@ -1528,18 +1528,18 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( // bail out and report that to the caller. auto &ctx = func->getASTContext(); auto request = - PreCheckFunctionBuilderRequest{{AnyFunctionRef(func), + PreCheckResultBuilderRequest{{AnyFunctionRef(func), /*SuppressDiagnostics=*/false}}; switch (evaluateOrDefault(ctx.evaluator, request, - FunctionBuilderBodyPreCheck::Error)) { - case FunctionBuilderBodyPreCheck::Okay: - // If the pre-check was okay, apply the function-builder transform. + ResultBuilderBodyPreCheck::Error)) { + case ResultBuilderBodyPreCheck::Okay: + // If the pre-check was okay, apply the result-builder transform. break; - case FunctionBuilderBodyPreCheck::Error: + case ResultBuilderBodyPreCheck::Error: return nullptr; - case FunctionBuilderBodyPreCheck::HasReturnStmt: { + case ResultBuilderBodyPreCheck::HasReturnStmt: { // One or more explicit 'return' statements were encountered, which // disables the result builder transform. Warn when we do this. auto returnStmts = findReturnStatements(func); @@ -1550,10 +1550,10 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( diag::result_builder_disabled_by_return, builderType); // Note that one can remove the result builder attribute. - auto attr = func->getAttachedFunctionBuilder(); + auto attr = func->getAttachedResultBuilder(); if (!attr) { if (auto accessor = dyn_cast(func)) { - attr = accessor->getStorage()->getAttachedFunctionBuilder(); + attr = accessor->getStorage()->getAttachedResultBuilder(); } } @@ -1594,7 +1594,7 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( // Build a constraint system in which we can check the body of the function. ConstraintSystem cs(func, options); - if (auto result = cs.matchFunctionBuilder( + if (auto result = cs.matchResultBuilder( func, builderType, resultContextType, resultConstraintKind, cs.getConstraintLocator(func->getBody()))) { if (result->isFailure()) @@ -1653,7 +1653,7 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( } Optional -ConstraintSystem::matchFunctionBuilder( +ConstraintSystem::matchResultBuilder( AnyFunctionRef fn, Type builderType, Type bodyResultType, ConstraintKind bodyResultConstraintKind, ConstraintLocatorBuilder locator) { @@ -1661,9 +1661,9 @@ ConstraintSystem::matchFunctionBuilder( assert(builder && "Bad result builder type"); assert(builder->getAttrs().hasAttribute()); - if (InvalidFunctionBuilderBodies.count(fn)) { + if (InvalidResultBuilderBodies.count(fn)) { (void)recordFix( - IgnoreInvalidFunctionBuilderBody::duringConstraintGeneration( + IgnoreInvalidResultBuilderBody::duringConstraintGeneration( *this, getConstraintLocator(fn.getBody()))); return getTypeMatchSuccess(); } @@ -1671,32 +1671,32 @@ ConstraintSystem::matchFunctionBuilder( // Pre-check the body: pre-check any expressions in it and look // for return statements. auto request = - PreCheckFunctionBuilderRequest{{fn, /*SuppressDiagnostics=*/true}}; + PreCheckResultBuilderRequest{{fn, /*SuppressDiagnostics=*/true}}; switch (evaluateOrDefault(getASTContext().evaluator, request, - FunctionBuilderBodyPreCheck::Error)) { - case FunctionBuilderBodyPreCheck::Okay: - // If the pre-check was okay, apply the function-builder transform. + ResultBuilderBodyPreCheck::Error)) { + case ResultBuilderBodyPreCheck::Okay: + // If the pre-check was okay, apply the result-builder transform. break; - case FunctionBuilderBodyPreCheck::Error: { + case ResultBuilderBodyPreCheck::Error: { if (!shouldAttemptFixes()) return getTypeMatchFailure(locator); - if (recordFix(IgnoreInvalidFunctionBuilderBody::duringPreCheck( + if (recordFix(IgnoreInvalidResultBuilderBody::duringPreCheck( *this, getConstraintLocator(fn.getBody())))) return getTypeMatchFailure(locator); return getTypeMatchSuccess(); } - case FunctionBuilderBodyPreCheck::HasReturnStmt: + case ResultBuilderBodyPreCheck::HasReturnStmt: // If the body has a return statement, suppress the transform but // continue solving the constraint system. return None; } // Check the form of this body to see if we can apply the - // function-builder translation at all. + // result-builder translation at all. auto dc = fn.getAsDeclContext(); { // Check whether we can apply this specific result builder. @@ -1713,7 +1713,7 @@ ConstraintSystem::matchFunctionBuilder( // Record the first unhandled construct as a fix. if (recordFix( - SkipUnhandledConstructInFunctionBuilder::create( + SkipUnhandledConstructInResultBuilder::create( *this, unhandledNode, builder, getConstraintLocator(locator)))) { return getTypeMatchFailure(locator); @@ -1733,10 +1733,10 @@ ConstraintSystem::matchFunctionBuilder( return getTypeMatchFailure(locator); if (transaction.hasErrors()) { - InvalidFunctionBuilderBodies.insert(fn); + InvalidResultBuilderBodies.insert(fn); if (recordFix( - IgnoreInvalidFunctionBuilderBody::duringConstraintGeneration( + IgnoreInvalidResultBuilderBody::duringConstraintGeneration( *this, getConstraintLocator(fn.getBody())))) return getTypeMatchFailure(locator); @@ -1749,13 +1749,13 @@ ConstraintSystem::matchFunctionBuilder( // Record the transformation. assert(std::find_if( - functionBuilderTransformed.begin(), - functionBuilderTransformed.end(), + resultBuilderTransformed.begin(), + resultBuilderTransformed.end(), [&](const std::pair &elt) { return elt.first == fn; - }) == functionBuilderTransformed.end() && + }) == resultBuilderTransformed.end() && "already transformed this body along this path!?!"); - functionBuilderTransformed.push_back( + resultBuilderTransformed.push_back( std::make_pair(fn, std::move(*applied))); // If builder is applied to the closure expression then @@ -1765,7 +1765,7 @@ ConstraintSystem::matchFunctionBuilder( locator = getConstraintLocator(closure, ConstraintLocator::ClosureResult); } else { locator = getConstraintLocator(fn.getAbstractFunctionDecl(), - ConstraintLocator::FunctionBuilderBodyResult); + ConstraintLocator::ResultBuilderBodyResult); } // Bind the body result type to the type of the transformed expression. @@ -1777,7 +1777,7 @@ ConstraintSystem::matchFunctionBuilder( namespace { /// Pre-check all the expressions in the body. -class PreCheckFunctionBuilderApplication : public ASTWalker { +class PreCheckResultBuilderApplication : public ASTWalker { AnyFunctionRef Fn; bool SkipPrecheck = false; bool SuppressDiagnostics = false; @@ -1787,14 +1787,14 @@ class PreCheckFunctionBuilderApplication : public ASTWalker { bool hasReturnStmt() const { return !ReturnStmts.empty(); } public: - PreCheckFunctionBuilderApplication(AnyFunctionRef fn, bool skipPrecheck, + PreCheckResultBuilderApplication(AnyFunctionRef fn, bool skipPrecheck, bool suppressDiagnostics) : Fn(fn), SkipPrecheck(skipPrecheck), SuppressDiagnostics(suppressDiagnostics) {} const std::vector getReturnStmts() const { return ReturnStmts; } - FunctionBuilderBodyPreCheck run() { + ResultBuilderBodyPreCheck run() { Stmt *oldBody = Fn.getBody(); Stmt *newBody = oldBody->walk(*this); @@ -1803,14 +1803,14 @@ class PreCheckFunctionBuilderApplication : public ASTWalker { assert((newBody == nullptr) == HasError && "unexpected short-circuit while walking body"); if (HasError) - return FunctionBuilderBodyPreCheck::Error; + return ResultBuilderBodyPreCheck::Error; assert(oldBody == newBody && "pre-check walk wasn't in-place?"); if (hasReturnStmt()) - return FunctionBuilderBodyPreCheck::HasReturnStmt; + return ResultBuilderBodyPreCheck::HasReturnStmt; - return FunctionBuilderBodyPreCheck::Okay; + return ResultBuilderBodyPreCheck::Okay; } std::pair walkToExprPre(Expr *E) override { @@ -1859,8 +1859,8 @@ class PreCheckFunctionBuilderApplication : public ASTWalker { } -FunctionBuilderBodyPreCheck PreCheckFunctionBuilderRequest::evaluate( - Evaluator &evaluator, PreCheckFunctionBuilderDescriptor owner) const { +ResultBuilderBodyPreCheck PreCheckResultBuilderRequest::evaluate( + Evaluator &evaluator, PreCheckResultBuilderDescriptor owner) const { // We don't want to do the precheck if it will already have happened in // the enclosing expression. bool skipPrecheck = false; @@ -1868,14 +1868,14 @@ FunctionBuilderBodyPreCheck PreCheckFunctionBuilderRequest::evaluate( owner.Fn.getAbstractClosureExpr())) skipPrecheck = shouldTypeCheckInEnclosingExpression(closure); - return PreCheckFunctionBuilderApplication( + return PreCheckResultBuilderApplication( owner.Fn, /*skipPrecheck=*/false, /*suppressDiagnostics=*/owner.SuppressDiagnostics) .run(); } std::vector TypeChecker::findReturnStatements(AnyFunctionRef fn) { - PreCheckFunctionBuilderApplication precheck(fn, /*skipPreCheck=*/true, + PreCheckResultBuilderApplication precheck(fn, /*skipPreCheck=*/true, /*SuppressDiagnostics=*/true); (void)precheck.run(); return precheck.getReturnStmts(); @@ -1914,7 +1914,7 @@ bool TypeChecker::typeSupportsBuilderOp( return foundMatch; } -Type swift::inferFunctionBuilderComponentType(NominalTypeDecl *builder) { +Type swift::inferResultBuilderComponentType(NominalTypeDecl *builder) { Type componentType; SmallVector potentialMatches; @@ -1946,7 +1946,7 @@ Type swift::inferFunctionBuilderComponentType(NominalTypeDecl *builder) { } std::tuple -swift::determineFunctionBuilderBuildFixItInfo(NominalTypeDecl *builder) { +swift::determineResultBuilderBuildFixItInfo(NominalTypeDecl *builder) { SourceLoc buildInsertionLoc = builder->getBraces().Start; std::string stubIndent; Type componentType; @@ -1963,13 +1963,13 @@ swift::determineFunctionBuilderBuildFixItInfo(NominalTypeDecl *builder) { ctx.SourceMgr, buildInsertionLoc, &extraIndent); stubIndent = (currentIndent + extraIndent).str(); - componentType = inferFunctionBuilderComponentType(builder); + componentType = inferResultBuilderComponentType(builder); return std::make_tuple(buildInsertionLoc, stubIndent, componentType); } -void swift::printFunctionBuilderBuildFunction( +void swift::printResultBuilderBuildFunction( NominalTypeDecl *builder, Type componentType, - FunctionBuilderBuildFunction function, + ResultBuilderBuildFunction function, Optional stubIndent, llvm::raw_ostream &out) { // Render the component type into a string. std::string componentTypeString; @@ -1995,36 +1995,36 @@ void swift::printFunctionBuilderBuildFunction( bool printedResult = false; switch (function) { - case FunctionBuilderBuildFunction::BuildBlock: + case ResultBuilderBuildFunction::BuildBlock: printer << "buildBlock(_ components: " << componentTypeString << "...)"; break; - case FunctionBuilderBuildFunction::BuildExpression: + case ResultBuilderBuildFunction::BuildExpression: printer << "buildExpression(_ expression: <#Expression#>)"; break; - case FunctionBuilderBuildFunction::BuildOptional: + case ResultBuilderBuildFunction::BuildOptional: printer << "buildOptional(_ component: " << componentTypeString << "?)"; break; - case FunctionBuilderBuildFunction::BuildEitherFirst: + case ResultBuilderBuildFunction::BuildEitherFirst: printer << "buildEither(first component: " << componentTypeString << ")"; break; - case FunctionBuilderBuildFunction::BuildEitherSecond: + case ResultBuilderBuildFunction::BuildEitherSecond: printer << "buildEither(second component: " << componentTypeString << ")"; break; - case FunctionBuilderBuildFunction::BuildArray: + case ResultBuilderBuildFunction::BuildArray: printer << "buildArray(_ components: [" << componentTypeString << "])"; break; - case FunctionBuilderBuildFunction::BuildLimitedAvailability: + case ResultBuilderBuildFunction::BuildLimitedAvailability: printer << "buildLimitedAvailability(_ component: " << componentTypeString << ")"; break; - case FunctionBuilderBuildFunction::BuildFinalResult: + case ResultBuilderBuildFunction::BuildFinalResult: printer << "buildFinalResult(_ component: " << componentTypeString << ") -> <#Result#>"; printedResult = true; diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 0c107fed76ef5..7a84967671844 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -1421,7 +1421,7 @@ TypeVariableBinding::fixForHole(ConstraintSystem &cs) const { // let's not record a fix about result type since there is // just not enough context to infer it without a body. if (cs.hasFixFor(cs.getConstraintLocator(closure->getBody()), - FixKind::IgnoreInvalidFunctionBuilderBody)) + FixKind::IgnoreInvalidResultBuilderBody)) return None; ConstraintFix *fix = SpecifyClosureReturnType::create(cs, dstLocator); diff --git a/lib/Sema/CSClosure.cpp b/lib/Sema/CSClosure.cpp index 0ee0674cd7524..5788096930edc 100644 --- a/lib/Sema/CSClosure.cpp +++ b/lib/Sema/CSClosure.cpp @@ -317,7 +317,7 @@ SolutionApplicationToFunctionResult ConstraintSystem::applySolution( if (auto transform = solution.getAppliedBuilderTransform(fn)) { // Apply the result builder to the closure. We want to be in the // context of the closure for subsequent transforms. - auto newBody = applyFunctionBuilderTransform( + auto newBody = applyResultBuilderTransform( solution, *transform, fn.getBody(), fn.getAsDeclContext(), [&](SolutionApplicationTarget target) { auto resultTarget = rewriteTarget(target); diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index b347887e4c022..52067028ea847 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -244,7 +244,7 @@ ValueDecl *RequirementFailure::getDeclRef() const { // If the locator is for a result builder body result type, the requirement // came from the function's return type. - if (getLocator()->isForFunctionBuilderBodyResult()) { + if (getLocator()->isForResultBuilderBodyResult()) { auto *func = getAsDecl(getAnchor()); return getAffectedDeclFromType(func->getResultInterfaceType()); } @@ -2256,7 +2256,7 @@ bool ContextualFailure::diagnoseAsError() { return true; } - case ConstraintLocator::FunctionBuilderBodyResult: { + case ConstraintLocator::ResultBuilderBodyResult: { diagnostic = *getDiagnosticFor(CTP_Initialization, toType); break; } @@ -5501,7 +5501,7 @@ bool MissingGenericArgumentsFailure::findArgumentLocations( return associator.allParamsAssigned(); } -SourceLoc SkipUnhandledConstructInFunctionBuilderFailure::getLoc() const { +SourceLoc SkipUnhandledConstructInResultBuilderFailure::getLoc() const { if (auto stmt = unhandled.dyn_cast()) return stmt->getStartLoc(); @@ -5519,7 +5519,7 @@ static bool hasMissingElseInChain(IfStmt *ifStmt) { return false; } -void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary( +void SkipUnhandledConstructInResultBuilderFailure::diagnosePrimary( bool asNote) { if (auto stmt = unhandled.dyn_cast()) { emitDiagnostic(asNote ? diag::note_result_builder_control_flow @@ -5532,7 +5532,7 @@ void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary( std::string stubIndent; Type componentType; std::tie(buildInsertionLoc, stubIndent, componentType) = - determineFunctionBuilderBuildFixItInfo(builder); + determineResultBuilderBuildFixItInfo(builder); if (buildInsertionLoc.isInvalid()) { // Do nothing. @@ -5544,8 +5544,8 @@ void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary( std::string fixItString; { llvm::raw_string_ostream out(fixItString); - printFunctionBuilderBuildFunction( - builder, componentType, FunctionBuilderBuildFunction::BuildOptional, + printResultBuilderBuildFunction( + builder, componentType, ResultBuilderBuildFunction::BuildOptional, stubIndent, out); } @@ -5558,14 +5558,14 @@ void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary( std::string fixItString; { llvm::raw_string_ostream out(fixItString); - printFunctionBuilderBuildFunction( + printResultBuilderBuildFunction( builder, componentType, - FunctionBuilderBuildFunction::BuildEitherFirst, + ResultBuilderBuildFunction::BuildEitherFirst, stubIndent, out); out << '\n'; - printFunctionBuilderBuildFunction( + printResultBuilderBuildFunction( builder, componentType, - FunctionBuilderBuildFunction::BuildEitherSecond, + ResultBuilderBuildFunction::BuildEitherSecond, stubIndent, out); } @@ -5578,8 +5578,8 @@ void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary( std::string fixItString; { llvm::raw_string_ostream out(fixItString); - printFunctionBuilderBuildFunction( - builder, componentType, FunctionBuilderBuildFunction::BuildArray, + printResultBuilderBuildFunction( + builder, componentType, ResultBuilderBuildFunction::BuildArray, stubIndent, out); } @@ -5592,14 +5592,14 @@ void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary( } } -bool SkipUnhandledConstructInFunctionBuilderFailure::diagnoseAsError() { +bool SkipUnhandledConstructInResultBuilderFailure::diagnoseAsError() { diagnosePrimary(/*asNote=*/false); emitDiagnosticAt(builder, diag::kind_declname_declared_here, builder->getDescriptiveKind(), builder->getName()); return true; } -bool SkipUnhandledConstructInFunctionBuilderFailure::diagnoseAsNote() { +bool SkipUnhandledConstructInResultBuilderFailure::diagnoseAsNote() { diagnosePrimary(/*asNote=*/true); return true; } diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index c57da9befe1d4..06339142bc818 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1733,7 +1733,7 @@ class MissingGenericArgumentsFailure final : public FailureDiagnostic { llvm::function_ref callback); }; -class SkipUnhandledConstructInFunctionBuilderFailure final +class SkipUnhandledConstructInResultBuilderFailure final : public FailureDiagnostic { public: using UnhandledNode = llvm::PointerUnion; @@ -1744,7 +1744,7 @@ class SkipUnhandledConstructInFunctionBuilderFailure final void diagnosePrimary(bool asNote); public: - SkipUnhandledConstructInFunctionBuilderFailure(const Solution &solution, + SkipUnhandledConstructInResultBuilderFailure(const Solution &solution, UnhandledNode unhandled, NominalTypeDecl *builder, ConstraintLocator *locator) diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 2d50c051c7d01..9f31bb5e96973 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -1003,18 +1003,18 @@ DefaultGenericArgument::create(ConstraintSystem &cs, GenericTypeParamType *param return new (cs.getAllocator()) DefaultGenericArgument(cs, param, locator); } -SkipUnhandledConstructInFunctionBuilder * -SkipUnhandledConstructInFunctionBuilder::create(ConstraintSystem &cs, +SkipUnhandledConstructInResultBuilder * +SkipUnhandledConstructInResultBuilder::create(ConstraintSystem &cs, UnhandledNode unhandled, NominalTypeDecl *builder, ConstraintLocator *locator) { return new (cs.getAllocator()) - SkipUnhandledConstructInFunctionBuilder(cs, unhandled, builder, locator); + SkipUnhandledConstructInResultBuilder(cs, unhandled, builder, locator); } -bool SkipUnhandledConstructInFunctionBuilder::diagnose(const Solution &solution, +bool SkipUnhandledConstructInResultBuilder::diagnose(const Solution &solution, bool asNote) const { - SkipUnhandledConstructInFunctionBuilderFailure failure(solution, unhandled, + SkipUnhandledConstructInResultBuilderFailure failure(solution, unhandled, builder, getLocator()); return failure.diagnose(asNote); } @@ -1552,14 +1552,14 @@ AllowKeyPathWithoutComponents::create(ConstraintSystem &cs, return new (cs.getAllocator()) AllowKeyPathWithoutComponents(cs, locator); } -bool IgnoreInvalidFunctionBuilderBody::diagnose(const Solution &solution, +bool IgnoreInvalidResultBuilderBody::diagnose(const Solution &solution, bool asNote) const { switch (Phase) { // Handled below case ErrorInPhase::PreCheck: break; case ErrorInPhase::ConstraintGeneration: - return true; // Already diagnosed by `matchFunctionBuilder`. + return true; // Already diagnosed by `matchResultBuilder`. } auto *S = getAnchor().get(); @@ -1598,10 +1598,10 @@ bool IgnoreInvalidFunctionBuilderBody::diagnose(const Solution &solution, return walker.diagnosed(); } -IgnoreInvalidFunctionBuilderBody *IgnoreInvalidFunctionBuilderBody::create( +IgnoreInvalidResultBuilderBody *IgnoreInvalidResultBuilderBody::create( ConstraintSystem &cs, ErrorInPhase phase, ConstraintLocator *locator) { return new (cs.getAllocator()) - IgnoreInvalidFunctionBuilderBody(cs, phase, locator); + IgnoreInvalidResultBuilderBody(cs, phase, locator); } bool SpecifyContextualTypeForNil::diagnose(const Solution &solution, diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 76e8578ce4514..3acc4aaeff9c4 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -4494,7 +4494,7 @@ bool ConstraintSystem::repairFailures( getConstraintLocator(anchor, path)); } - case ConstraintLocator::FunctionBuilderBodyResult: { + case ConstraintLocator::ResultBuilderBodyResult: { // If result type of the body couldn't be determined // there is going to be other fix available to diagnose // the underlying issue. @@ -7594,7 +7594,7 @@ ConstraintSystem::simplifyOneWayConstraint( secondSimplified, first, ConstraintKind::BindParam, flags, locator); } -static Type getOpenedFunctionBuilderTypeFor(ConstraintSystem &cs, +static Type getOpenedResultBuilderTypeFor(ConstraintSystem &cs, ConstraintLocatorBuilder locator) { auto lastElt = locator.last(); if (!lastElt) @@ -7620,7 +7620,7 @@ static Type getOpenedFunctionBuilderTypeFor(ConstraintSystem &cs, return Type(); auto *PD = getParameterAt(choice, argToParamElt->getParamIdx()); - auto builderType = PD->getFunctionBuilderType(); + auto builderType = PD->getResultBuilderType(); if (!builderType) return Type(); @@ -7658,14 +7658,14 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, auto *inferredClosureType = getClosureType(closure); // Determine whether a result builder will be applied. - auto functionBuilderType = getOpenedFunctionBuilderTypeFor(*this, locator); + auto resultBuilderType = getOpenedResultBuilderTypeFor(*this, locator); // Determine whether to introduce one-way constraints between the parameter's // type as seen in the body of the closure and the external parameter // type. bool oneWayConstraints = getASTContext().TypeCheckerOpts.EnableOneWayClosureParameters || - functionBuilderType; + resultBuilderType; auto *paramList = closure->getParameters(); SmallVector parameters; @@ -7731,9 +7731,9 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, assignFixedType(typeVar, closureType, closureLocator); // If there is a result builder to apply, do so now. - if (functionBuilderType) { - if (auto result = matchFunctionBuilder( - closure, functionBuilderType, closureType->getResult(), + if (resultBuilderType) { + if (auto result = matchResultBuilder( + closure, resultBuilderType, closureType->getResult(), ConstraintKind::Conversion, locator)) { return result->isSuccess(); } @@ -10131,7 +10131,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::RemoveAddressOf: case FixKind::AddMissingArguments: case FixKind::MoveOutOfOrderArgument: - case FixKind::SkipUnhandledConstructInFunctionBuilder: + case FixKind::SkipUnhandledConstructInResultBuilder: case FixKind::UsePropertyWrapper: case FixKind::UseWrappedValue: case FixKind::ExpandArrayIntoVarargs: @@ -10146,7 +10146,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::SpecifyKeyPathRootType: case FixKind::SpecifyLabelToAssociateTrailingClosure: case FixKind::AllowKeyPathWithoutComponents: - case FixKind::IgnoreInvalidFunctionBuilderBody: + case FixKind::IgnoreInvalidResultBuilderBody: case FixKind::SpecifyContextualTypeForNil: { return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } @@ -10376,7 +10376,7 @@ ConstraintSystem::addArgumentConversionConstraintImpl( if (argTypeVar->getImpl().isClosureType()) { // Extract any type variables present in the parameter's result builder. SmallVector typeVars; - if (auto builderTy = getOpenedFunctionBuilderTypeFor(*this, locator)) + if (auto builderTy = getOpenedResultBuilderTypeFor(*this, locator)) builderTy->getTypeVariables(typeVars); auto *loc = getConstraintLocator(locator); diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 726f0b3dd5344..eb75234cc4925 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -187,8 +187,8 @@ Solution ConstraintSystem::finalize() { for (auto &e : CheckedConformances) solution.Conformances.push_back({e.first, e.second}); - for (const auto &transformed : functionBuilderTransformed) { - solution.functionBuilderTransformed.insert(transformed); + for (const auto &transformed : resultBuilderTransformed) { + solution.resultBuilderTransformed.insert(transformed); } return solution; @@ -276,8 +276,8 @@ void ConstraintSystem::applySolution(const Solution &solution) { for (auto &conformance : solution.Conformances) CheckedConformances.push_back(conformance); - for (const auto &transformed : solution.functionBuilderTransformed) { - functionBuilderTransformed.push_back(transformed); + for (const auto &transformed : solution.resultBuilderTransformed) { + resultBuilderTransformed.push_back(transformed); } // Register any fixes produced along this path. @@ -479,7 +479,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs) numCheckedConformances = cs.CheckedConformances.size(); numDisabledConstraints = cs.solverState->getNumDisabledConstraints(); numFavoredConstraints = cs.solverState->getNumFavoredConstraints(); - numFunctionBuilderTransformed = cs.functionBuilderTransformed.size(); + numResultBuilderTransformed = cs.resultBuilderTransformed.size(); numResolvedOverloads = cs.ResolvedOverloads.size(); numInferredClosureTypes = cs.ClosureTypes.size(); numContextualTypes = cs.contextualTypes.size(); @@ -558,7 +558,7 @@ ConstraintSystem::SolverScope::~SolverScope() { truncate(cs.CheckedConformances, numCheckedConformances); /// Remove any builder transformed closures. - truncate(cs.functionBuilderTransformed, numFunctionBuilderTransformed); + truncate(cs.resultBuilderTransformed, numResultBuilderTransformed); // Remove any inferred closure types (e.g. used in result builder body). truncate(cs.ClosureTypes, numInferredClosureTypes); diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 2008591cc249d..d04ed0895a198 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -261,8 +261,8 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, } } - Type functionBuilderType= var->getFunctionBuilderType(); - if (functionBuilderType) { + Type resultBuilderType= var->getResultBuilderType(); + if (resultBuilderType) { // If the variable's type is structurally a function type, use that // type. Otherwise, form a non-escaping function type for the function // parameter. @@ -288,8 +288,8 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, arg->setNonEphemeralIfPossible(); // Attach a result builder attribute if needed. - if (functionBuilderType) { - auto typeExpr = TypeExpr::createImplicit(functionBuilderType, ctx); + if (resultBuilderType) { + auto typeExpr = TypeExpr::createImplicit(resultBuilderType, ctx); auto attr = CustomAttr::create( ctx, SourceLoc(), typeExpr, /*implicit=*/true); arg->getAttrs().add(attr); diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index e26103fc8c2c6..71e2a143cb7c0 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -44,7 +44,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::ClosureResult: case ConstraintLocator::ClosureBody: case ConstraintLocator::ConstructorMember: - case ConstraintLocator::FunctionBuilderBodyResult: + case ConstraintLocator::ResultBuilderBodyResult: case ConstraintLocator::InstanceType: case ConstraintLocator::AutoclosureResult: case ConstraintLocator::OptionalPayload: @@ -198,8 +198,8 @@ bool ConstraintLocator::isForOptionalTry() const { return directlyAt(); } -bool ConstraintLocator::isForFunctionBuilderBodyResult() const { - return isFirstElement(); +bool ConstraintLocator::isForResultBuilderBodyResult() const { + return isFirstElement(); } GenericTypeParamType *ConstraintLocator::getGenericParameter() const { @@ -300,7 +300,7 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { out << "function result"; break; - case FunctionBuilderBodyResult: + case ResultBuilderBodyResult: out << "result builder body result"; break; diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index fc9b1fd3e165d..a34bfedaf0ec6 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4061,7 +4061,7 @@ void constraints::simplifyLocator(ASTNode &anchor, break; } - case ConstraintLocator::FunctionBuilderBodyResult: { + case ConstraintLocator::ResultBuilderBodyResult: { path = path.slice(1); break; } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 7569036e0c9b2..7f3cfa4041008 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -3098,8 +3098,8 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { .highlight(attr->getArg()->getSourceRange()); } - // Complain if this isn't the primary function-builder attribute. - auto attached = decl->getAttachedFunctionBuilder(); + // Complain if this isn't the primary result-builder attribute. + auto attached = decl->getAttachedResultBuilder(); if (attached != attr) { diagnose(attr->getLocation(), diag::result_builder_multiple, isa(decl)); @@ -3107,9 +3107,9 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { attr->setInvalid(); return; } else { - // Force any diagnostics associated with computing the function-builder + // Force any diagnostics associated with computing the result-builder // type. - (void) decl->getFunctionBuilderType(); + (void) decl->getResultBuilderType(); } return; @@ -3157,14 +3157,14 @@ void AttributeChecker::visitResultBuilderAttr(ResultBuilderAttr *attr) { std::string stubIndent; Type componentType; std::tie(buildInsertionLoc, stubIndent, componentType) = - determineFunctionBuilderBuildFixItInfo(nominal); + determineResultBuilderBuildFixItInfo(nominal); if (buildInsertionLoc.isValid() && potentialMatches.empty()) { std::string fixItString; { llvm::raw_string_ostream out(fixItString); - printFunctionBuilderBuildFunction( + printResultBuilderBuildFunction( nominal, componentType, - FunctionBuilderBuildFunction::BuildBlock, + ResultBuilderBuildFunction::BuildBlock, stubIndent, out); } diff --git a/lib/Sema/TypeCheckRequestFunctions.cpp b/lib/Sema/TypeCheckRequestFunctions.cpp index 8df0bf41c62c6..06c4e510dd7cf 100644 --- a/lib/Sema/TypeCheckRequestFunctions.cpp +++ b/lib/Sema/TypeCheckRequestFunctions.cpp @@ -161,7 +161,7 @@ Type EnumRawTypeRequest::evaluate(Evaluator &evaluator, } CustomAttr * -AttachedFunctionBuilderRequest::evaluate(Evaluator &evaluator, +AttachedResultBuilderRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { ASTContext &ctx = decl->getASTContext(); auto dc = decl->getDeclContext(); @@ -185,7 +185,7 @@ AttachedFunctionBuilderRequest::evaluate(Evaluator &evaluator, } /// Attempt to infer the result builder type for a declaration. -static Type inferFunctionBuilderType(ValueDecl *decl) { +static Type inferResultBuilderType(ValueDecl *decl) { auto dc = decl->getDeclContext(); if (!dc->isTypeContext() || isa(dc)) return Type(); @@ -227,26 +227,26 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { ValueDecl *dynamicReplacement; }; - Type functionBuilderType; + Type resultBuilderType; static Match forConformance( ProtocolConformance *conformance, ValueDecl *requirement, - Type functionBuilderType) { + Type resultBuilderType) { Match match; match.kind = Conformance; match.conformanceMatch.conformance = conformance; match.conformanceMatch.requirement = requirement; - match.functionBuilderType = functionBuilderType; + match.resultBuilderType = resultBuilderType; return match; } static Match forDynamicReplacement( - ValueDecl *dynamicReplacement, Type functionBuilderType) { + ValueDecl *dynamicReplacement, Type resultBuilderType) { Match match; match.kind = DynamicReplacement; match.dynamicReplacement = dynamicReplacement; - match.functionBuilderType = functionBuilderType; + match.resultBuilderType = resultBuilderType; return match; } @@ -286,8 +286,8 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { if (!requirement) continue; - Type functionBuilderType = requirement->getFunctionBuilderType(); - if (!functionBuilderType) + Type resultBuilderType = requirement->getResultBuilderType(); + if (!resultBuilderType) continue; auto witness = conformance->getWitnessDecl(requirement); @@ -297,11 +297,11 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { // Substitute into the result builder type. auto subs = conformance->getSubstitutions(lookupDecl->getModuleContext()); - Type subFunctionBuilderType = functionBuilderType.subst(subs); + Type subResultBuilderType = resultBuilderType.subst(subs); matches.push_back( Match::forConformance( - conformance, requirement, subFunctionBuilderType)); + conformance, requirement, subResultBuilderType)); } } }; @@ -310,9 +310,9 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { // Look for result builder types inferred through dynamic replacements. if (auto replaced = lookupDecl->getDynamicallyReplacedDecl()) { - if (auto functionBuilderType = replaced->getFunctionBuilderType()) { + if (auto resultBuilderType = replaced->getResultBuilderType()) { matches.push_back( - Match::forDynamicReplacement(replaced, functionBuilderType)); + Match::forDynamicReplacement(replaced, resultBuilderType)); } else { addConformanceMatches(replaced); } @@ -322,43 +322,43 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { return Type(); // Determine whether there is more than one actual result builder type. - Type functionBuilderType = matches[0].functionBuilderType; + Type resultBuilderType = matches[0].resultBuilderType; for (const auto &match : matches) { // If the types were the same anyway, there's nothing to do. - Type otherFunctionBuilderType = match.functionBuilderType; - if (functionBuilderType->isEqual(otherFunctionBuilderType)) + Type otherResultBuilderType = match.resultBuilderType; + if (resultBuilderType->isEqual(otherResultBuilderType)) continue; // We have at least two different result builder types. // Diagnose the ambiguity and provide potential solutions. decl->diagnose( diag::result_builder_infer_ambig, lookupDecl->getName(), - functionBuilderType, otherFunctionBuilderType); + resultBuilderType, otherResultBuilderType); decl->diagnose(diag::result_builder_infer_add_return) .fixItInsert(funcDecl->getBodySourceRange().End, "return <#expr#>\n"); for (const auto &match : matches) { decl->diagnose( diag::result_builder_infer_pick_specific, - match.functionBuilderType, + match.resultBuilderType, static_cast(match.kind), match.getSourceName()) .fixItInsert( lookupDecl->getAttributeInsertionLoc(false), - "@" + match.functionBuilderType.getString() + " "); + "@" + match.resultBuilderType.getString() + " "); } return Type(); } - return functionBuilderType; + return resultBuilderType; } -Type FunctionBuilderTypeRequest::evaluate(Evaluator &evaluator, +Type ResultBuilderTypeRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { - // Look for a function-builder custom attribute. - auto attr = decl->getAttachedFunctionBuilder(); + // Look for a result-builder custom attribute. + auto attr = decl->getAttachedResultBuilder(); if (!attr) - return inferFunctionBuilderType(decl); + return inferResultBuilderType(decl); // Resolve a type for the attribute. auto mutableAttr = const_cast(attr); diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index c6ffc17b2b1f5..3bca9efb2fc6c 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1607,14 +1607,14 @@ void TypeChecker::typeCheckASTNode(ASTNode &node, DeclContext *DC, stmtChecker.typeCheckASTNode(node); } -static Type getFunctionBuilderType(FuncDecl *FD) { - Type builderType = FD->getFunctionBuilderType(); +static Type getResultBuilderType(FuncDecl *FD) { + Type builderType = FD->getResultBuilderType(); // For getters, fall back on looking on the attribute on the storage. if (!builderType) { auto accessor = dyn_cast(FD); if (accessor && accessor->getAccessorKind() == AccessorKind::Get) { - builderType = accessor->getStorage()->getFunctionBuilderType(); + builderType = accessor->getStorage()->getResultBuilderType(); } } @@ -1917,9 +1917,9 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(Evaluator &evaluator, // Function builder function doesn't support partial type checking. if (auto *func = dyn_cast(DC)) { - if (Type builderType = getFunctionBuilderType(func)) { + if (Type builderType = getResultBuilderType(func)) { auto optBody = - TypeChecker::applyFunctionBuilderBodyTransform(func, builderType); + TypeChecker::applyResultBuilderBodyTransform(func, builderType); if (!optBody || !*optBody) return true; // Wire up the function body now. @@ -1978,9 +1978,9 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator, bool alreadyTypeChecked = false; if (auto *func = dyn_cast(AFD)) { - if (Type builderType = getFunctionBuilderType(func)) { + if (Type builderType = getResultBuilderType(func)) { if (auto optBody = - TypeChecker::applyFunctionBuilderBodyTransform( + TypeChecker::applyResultBuilderBodyTransform( func, builderType)) { if (!*optBody) return errorBody(); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 742d8c6e636d0..59d93b35f0aa0 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -429,7 +429,7 @@ void typeCheckASTNode(ASTNode &node, DeclContext *DC, /// e.g., because of a \c return statement. Otherwise, returns either the /// fully type-checked body of the function (on success) or a \c nullptr /// value if an error occurred while type checking the transformed body. -Optional applyFunctionBuilderBodyTransform(FuncDecl *func, +Optional applyResultBuilderBodyTransform(FuncDecl *func, Type builderType); /// Find the return statements within the body of the given function. diff --git a/test/Constraints/function_builder.swift b/test/Constraints/result_builder.swift similarity index 99% rename from test/Constraints/function_builder.swift rename to test/Constraints/result_builder.swift index 54abc88e774b9..e1c5a4371a133 100644 --- a/test/Constraints/function_builder.swift +++ b/test/Constraints/result_builder.swift @@ -375,13 +375,13 @@ acceptComponentBuilder { // rdar://53325810 // Test that we don't have problems with expression pre-checking when -// type-checking an overloaded function-builder call. In particular, +// type-checking an overloaded result-builder call. In particular, // we need to make sure that expressions in the closure are pre-checked // before we build constraints for them. Note that top-level expressions // that need to be rewritten by expression prechecking (such as the operator // sequences in the boolean conditions and statements below) won't be // rewritten in the original closure body if we just precheck the -// expressions produced by the function-builder transformation. +// expressions produced by the result-builder transformation. struct ForEach1 { var data: Data var content: (Data.Element) -> Content diff --git a/test/Constraints/function_builder_availability.swift b/test/Constraints/result_builder_availability.swift similarity index 100% rename from test/Constraints/function_builder_availability.swift rename to test/Constraints/result_builder_availability.swift diff --git a/test/Constraints/function_builder_diags.swift b/test/Constraints/result_builder_diags.swift similarity index 100% rename from test/Constraints/function_builder_diags.swift rename to test/Constraints/result_builder_diags.swift diff --git a/test/Constraints/function_builder_infer.swift b/test/Constraints/result_builder_infer.swift similarity index 100% rename from test/Constraints/function_builder_infer.swift rename to test/Constraints/result_builder_infer.swift diff --git a/test/Constraints/function_builder_one_way.swift b/test/Constraints/result_builder_one_way.swift similarity index 100% rename from test/Constraints/function_builder_one_way.swift rename to test/Constraints/result_builder_one_way.swift diff --git a/test/Constraints/function_builder_opaque_result.swift b/test/Constraints/result_builder_opaque_result.swift similarity index 100% rename from test/Constraints/function_builder_opaque_result.swift rename to test/Constraints/result_builder_opaque_result.swift diff --git a/test/IDE/complete_function_builder.swift b/test/IDE/complete_result_builder.swift similarity index 100% rename from test/IDE/complete_function_builder.swift rename to test/IDE/complete_result_builder.swift diff --git a/test/Index/function_builders.swift b/test/Index/result_builders.swift similarity index 100% rename from test/Index/function_builders.swift rename to test/Index/result_builders.swift diff --git a/test/ModuleInterface/Inputs/function_builders_client.swift b/test/ModuleInterface/Inputs/result_builders_client.swift similarity index 100% rename from test/ModuleInterface/Inputs/function_builders_client.swift rename to test/ModuleInterface/Inputs/result_builders_client.swift diff --git a/test/ModuleInterface/function_builders.swift b/test/ModuleInterface/result_builders.swift similarity index 100% rename from test/ModuleInterface/function_builders.swift rename to test/ModuleInterface/result_builders.swift diff --git a/test/Profiler/coverage_function_builder.swift b/test/Profiler/converage_result_builder.swift similarity index 100% rename from test/Profiler/coverage_function_builder.swift rename to test/Profiler/converage_result_builder.swift diff --git a/test/Sema/type_eraser.swift b/test/Sema/type_eraser.swift index 70d1a8ad009fe..b337cf74d490e 100644 --- a/test/Sema/type_eraser.swift +++ b/test/Sema/type_eraser.swift @@ -57,8 +57,8 @@ struct Builder { } } -// CHECK-LABEL: TestFunctionBuilder -class TestFunctionBuilder { +// CHECK-LABEL: TestResultBuilder +class TestResultBuilder { // CHECK-LABEL: testTransformFnBody @Builder dynamic var testTransformFnBody: some P { // CHECK: return_stmt diff --git a/test/SourceKit/CursorInfo/function_builder.swift b/test/SourceKit/CursorInfo/result_builder.swift similarity index 100% rename from test/SourceKit/CursorInfo/function_builder.swift rename to test/SourceKit/CursorInfo/result_builder.swift diff --git a/test/decl/function_builder_fixits.swift b/test/decl/result_builder_fixits.swift similarity index 100% rename from test/decl/function_builder_fixits.swift rename to test/decl/result_builder_fixits.swift diff --git a/test/decl/var/function_builders.swift b/test/decl/var/result_builders.swift similarity index 100% rename from test/decl/var/function_builders.swift rename to test/decl/var/result_builders.swift diff --git a/test/decl/var/function_builders_availability.swift b/test/decl/var/result_builders_availability.swift similarity index 100% rename from test/decl/var/function_builders_availability.swift rename to test/decl/var/result_builders_availability.swift diff --git a/tools/swift-ast-script/ASTScriptEvaluator.cpp b/tools/swift-ast-script/ASTScriptEvaluator.cpp index a283da486ed61..3df61aabd07e9 100644 --- a/tools/swift-ast-script/ASTScriptEvaluator.cpp +++ b/tools/swift-ast-script/ASTScriptEvaluator.cpp @@ -63,7 +63,7 @@ class ASTScriptWalker : public ASTWalker { if (!sig->requiresProtocol(paramResultType, ViewProtocol)) continue; // The parameter must not be a @ViewBuilder parameter. - if (param->getFunctionBuilderType()) continue; + if (param->getResultBuilderType()) continue; // Print the function. printDecl(fn); diff --git a/userdocs/diagnostics/function-builder-methods.md b/userdocs/diagnostics/result-builder-methods.md similarity index 97% rename from userdocs/diagnostics/function-builder-methods.md rename to userdocs/diagnostics/result-builder-methods.md index 86f4664b7fc4c..896f415d3062f 100644 --- a/userdocs/diagnostics/function-builder-methods.md +++ b/userdocs/diagnostics/result-builder-methods.md @@ -1,4 +1,4 @@ -# Function Builder Methods +# Result Builder Methods To be useful as a result builder, a result builder type must provide a sufficient subset of function-building methods that enable the transformation of @@ -8,7 +8,7 @@ can define: ```swift @resultBuilder -struct ExampleFunctionBuilder { +struct ExampleResultBuilder { /// The type of individual statement expressions in the transformed function, /// which defaults to Component if buildExpression() is not provided. typealias Expression = ... From b6c0145932ad121635f8413b16bef68ddf01cc38 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 20 Oct 2020 22:24:41 -0700 Subject: [PATCH 605/745] [SE-0289] Continue printing @_functionBuilder in .swiftinterfaces. Maintain the ability for older Swift compilers to read .swiftinterfaces that make use of result builders by always emitting @_functionBuilder rather than the newer @resultBuilder. --- lib/AST/Attr.cpp | 4 ++++ .../Inputs/result_builders_client.swift | 2 +- test/ModuleInterface/result_builders.swift | 16 +++++++++------- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 8539adeecbc91..3ccb494b2bd14 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -771,6 +771,10 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, case DAK_Optimize: if (DeclAttribute::isDeclModifier(getKind())) { Printer.printKeyword(getAttrName(), Options); + } else if (Options.IsForSwiftInterface && getKind() == DAK_ResultBuilder) { + // Use @_functionBuilder in Swift interfaces to maintain backward + // compatibility. + Printer.printSimpleAttr("_functionBuilder", /*needAt=*/true); } else { Printer.printSimpleAttr(getAttrName(), /*needAt=*/true); } diff --git a/test/ModuleInterface/Inputs/result_builders_client.swift b/test/ModuleInterface/Inputs/result_builders_client.swift index 8ce9da388385b..1d403a80247ff 100644 --- a/test/ModuleInterface/Inputs/result_builders_client.swift +++ b/test/ModuleInterface/Inputs/result_builders_client.swift @@ -1,4 +1,4 @@ -import FunctionBuilders +import ResultBuilders let name = "dsl" tuplify(true) { diff --git a/test/ModuleInterface/result_builders.swift b/test/ModuleInterface/result_builders.swift index 79a89dc5932bd..031551c1497b9 100644 --- a/test/ModuleInterface/result_builders.swift +++ b/test/ModuleInterface/result_builders.swift @@ -1,9 +1,11 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -typecheck -module-name FunctionBuilders -emit-module-interface-path %t/FunctionBuilders.swiftinterface %s -// RUN: %FileCheck %s < %t/FunctionBuilders.swiftinterface +// RUN: %target-swift-frontend -typecheck -module-name ResultBuilders -emit-module-interface-path %t/ResultBuilders.swiftinterface %s +// RUN: %FileCheck %s < %t/ResultBuilders.swiftinterface // RUN: %target-swift-frontend -I %t -typecheck -verify %S/Inputs/result_builders_client.swift -// RUN: %target-swift-frontend -compile-module-from-interface %t/FunctionBuilders.swiftinterface -o %t/FunctionBuilders.swiftmodule +// RUN: %target-swift-frontend -compile-module-from-interface %t/ResultBuilders.swiftinterface -o %t/ResultBuilders.swiftmodule +// RUN: %FileCheck %s < %t/ResultBuilders.swiftinterface +// CHECK: @_functionBuilder public struct TupleBuilder @resultBuilder public struct TupleBuilder { public static func buildBlock(_ t1: T1, _ t2: T2) -> (T1, T2) { @@ -38,7 +40,7 @@ public struct TupleBuilder { public static func buildIf(_ value: T?) -> T? { return value } } -// CHECK-LABEL: public func tuplify(_ cond: Swift.Bool, @FunctionBuilders.TupleBuilder body: (Swift.Bool) -> T) +// CHECK-LABEL: public func tuplify(_ cond: Swift.Bool, @ResultBuilders.TupleBuilder body: (Swift.Bool) -> T) public func tuplify(_ cond: Bool, @TupleBuilder body: (Bool) -> T) { print(body(cond)) } @@ -52,13 +54,13 @@ public struct UsesBuilderProperty { "goodbye" } - // CHECK: public func myFunc(@FunctionBuilders.TupleBuilder fn: () -> ()) + // CHECK: public func myFunc(@ResultBuilders.TupleBuilder fn: () -> ()) public func myFunc(@TupleBuilder fn: () -> ()) {} } public protocol ProtocolWithBuilderProperty { associatedtype Assoc - - // CHECK: @FunctionBuilders.TupleBuilder var myVar: Self.Assoc { get } + + // CHECK: @ResultBuilders.TupleBuilder var myVar: Self.Assoc { get } @TupleBuilder var myVar: Assoc { get } } From c6be347d3ae3b56a2dc19ac04b14d2182d805284 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 21 Oct 2020 10:10:46 +0200 Subject: [PATCH 606/745] SILCombine: fix metatype simplification Don't reuse argument metadata if it's an indirect argument. Fixes a verifier crash. https://bugs.swift.org/browse/SR-13731 rdar://problem/70338666 --- lib/SILOptimizer/Analysis/SimplifyInstruction.cpp | 3 ++- test/SILOptimizer/sil_simplify_instrs.sil | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/SILOptimizer/Analysis/SimplifyInstruction.cpp b/lib/SILOptimizer/Analysis/SimplifyInstruction.cpp index a16af7c7975fc..0a1d5083ce5e6 100644 --- a/lib/SILOptimizer/Analysis/SimplifyInstruction.cpp +++ b/lib/SILOptimizer/Analysis/SimplifyInstruction.cpp @@ -488,7 +488,8 @@ SILValue InstSimplifier::visitMetatypeInst(MetatypeInst *MI) { || instanceType.getStructOrBoundGenericStruct() || instanceType.getEnumOrBoundGenericEnum()) { for (SILArgument *argument : MI->getFunction()->getArguments()) { - if (argument->getType().getASTType() == metaType) + if (argument->getType().getASTType() == metaType && + argument->getType().isObject()) return argument; } } diff --git a/test/SILOptimizer/sil_simplify_instrs.sil b/test/SILOptimizer/sil_simplify_instrs.sil index 8f5b0b361888f..8e7b8c73e1730 100644 --- a/test/SILOptimizer/sil_simplify_instrs.sil +++ b/test/SILOptimizer/sil_simplify_instrs.sil @@ -371,3 +371,16 @@ bb0(%0 : $@thick SpecialEnum.Type): %5 = struct $Bool (%4 : $Builtin.Int1) return %5 : $Bool } + +// CHECK-LABEL: sil @dontSimplifyIndirectMetatype : $@convention(thin) () -> @out @thick Int.Type { +// CHECK: [[MT:%[0-9]+]] = metatype $@thick Int.Type +// CHECK: store [[MT]] to %0 +// CHECK-LABEL: } // end sil function 'dontSimplifyIndirectMetatype' +sil @dontSimplifyIndirectMetatype : $@convention(thin) () -> @out @thick Int.Type { +bb0(%0 : $*@thick Int.Type): + %1 = metatype $@thick Int.Type + store %1 to %0 : $*@thick Int.Type + %3 = tuple () + return %3 : $() +} + From 4a4c6c0f6315434e9ce7660007f4a6075028203c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=85=B7=E9=85=B7=E7=9A=84=E5=93=80=E6=AE=BF?= Date: Wed, 21 Oct 2020 22:50:12 +0800 Subject: [PATCH 607/745] Update ObjectIdentifier.swift --- stdlib/public/core/ObjectIdentifier.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/stdlib/public/core/ObjectIdentifier.swift b/stdlib/public/core/ObjectIdentifier.swift index 36c800c672c48..39b73cbd027ff 100644 --- a/stdlib/public/core/ObjectIdentifier.swift +++ b/stdlib/public/core/ObjectIdentifier.swift @@ -19,9 +19,6 @@ /// lifetime of an object. When the instance gets deallocated, its object /// identifier may be reused for a different object. (Internally, objects are /// identified by their memory location.) -/// -/// If you need an object identifier over the lifetime of an object, it may -/// be appropriate to provide a custom implementation of `Identifiable`. @frozen // trivial-implementation public struct ObjectIdentifier { @usableFromInline // trivial-implementation From 74144e4788135649280b134cc260959228e2b6eb Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 21 Oct 2020 10:54:29 -0700 Subject: [PATCH 608/745] Platform: extract `WLANAPI` module on Windows The WLAN APIs are used for the native WiFi implementation and is not as generally useful. Extract it into a submodule. --- stdlib/public/Platform/winsdk.modulemap | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/stdlib/public/Platform/winsdk.modulemap b/stdlib/public/Platform/winsdk.modulemap index 2a09765f012ee..c447a5741a355 100644 --- a/stdlib/public/Platform/winsdk.modulemap +++ b/stdlib/public/Platform/winsdk.modulemap @@ -377,5 +377,15 @@ module WinSDK [system] { link "AdvAPI32.Lib" } + + // TODO(compnerd) does it make sense to implicitly export this API surface? + // It seems to be meant for device drivers. + module WLANAPI { + header "wlanapi.h" + header "wlanihv.h" + header "wlclient.h" + + link "wlanapi.lib" + } } From 0044e7dcac6ff176be0945ba0f468967979779d3 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Wed, 21 Oct 2020 12:04:10 -0700 Subject: [PATCH 609/745] [ownership] Move OME past SILMem2Reg --- lib/SILOptimizer/PassManager/PassPipeline.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 9ebe5b37eadf9..bea2c93ab93b8 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -296,13 +296,13 @@ void addFunctionPasses(SILPassPipelinePlan &P, P.addSROA(); } + // Promote stack allocations to values. + P.addMem2Reg(); + // We earlier eliminated ownership if we are not compiling the stdlib. Now // handle the stdlib functions. P.addNonTransparentFunctionOwnershipModelEliminator(); - // Promote stack allocations to values. - P.addMem2Reg(); - // Run the existential specializer Pass. P.addExistentialSpecializer(); From e1852956a065bdeca503c28fec37b3de139f8010 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 21 Oct 2020 12:33:49 -0700 Subject: [PATCH 610/745] [Concurrency] Drop "get" prefix when importing Objective-C methods as async. Implements rdar://70506634. --- lib/ClangImporter/ImportName.cpp | 23 +++++++++++++++---- lib/ClangImporter/ImportName.h | 1 + test/ClangImporter/objc_async.swift | 2 ++ .../usr/include/ObjCConcurrency.h | 1 + 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index cae15627ba7ef..b3eb09cc964de 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -1189,6 +1189,7 @@ Optional NameImporter::considerAsyncImport( const clang::ObjCMethodDecl *clangDecl, StringRef &baseName, + SmallVectorImpl &baseNameScratch, SmallVectorImpl ¶mNames, ArrayRef params, bool isInitializer, bool hasCustomName, @@ -1224,6 +1225,17 @@ NameImporter::considerAsyncImport( return None; } + // If there's no custom name, and the base name starts with "get", drop the + // get. + if (!hasCustomName) { + StringRef currentBaseName = newBaseName ? *newBaseName : baseName; + if (currentBaseName.size() > 3 && + camel_case::getFirstWord(currentBaseName) == "get") { + newBaseName = camel_case::toLowercaseInitialisms( + currentBaseName.substr(3), baseNameScratch); + } + } + // Used for returns once we've determined that the method cannot be // imported as async, even though it has what looks like a completion handler // parameter. @@ -1461,6 +1473,7 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, } // If we have a swift_name attribute, use that. + SmallString<32> asyncBaseNameScratch; if (auto *nameAttr = findSwiftNameAttr(D, version)) { bool skipCustomName = false; @@ -1539,9 +1552,9 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, if (version.supportsConcurrency()) { if (auto asyncInfo = considerAsyncImport( - method, parsedName.BaseName, parsedName.ArgumentLabels, - params, isInitializer, /*hasCustomName=*/true, - result.getErrorInfo())) { + method, parsedName.BaseName, asyncBaseNameScratch, + parsedName.ArgumentLabels, params, isInitializer, + /*hasCustomName=*/true, result.getErrorInfo())) { result.info.hasAsyncInfo = true; result.info.asyncInfo = *asyncInfo; @@ -1816,8 +1829,8 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, if (version.supportsConcurrency() && result.info.accessorKind == ImportedAccessorKind::None) { if (auto asyncInfo = considerAsyncImport( - objcMethod, baseName, argumentNames, params, isInitializer, - /*hasCustomName=*/false, + objcMethod, baseName, asyncBaseNameScratch, + argumentNames, params, isInitializer, /*hasCustomName=*/false, result.getErrorInfo())) { result.info.hasAsyncInfo = true; result.info.asyncInfo = *asyncInfo; diff --git a/lib/ClangImporter/ImportName.h b/lib/ClangImporter/ImportName.h index 5f5d9a027343e..12fc9b2bca82d 100644 --- a/lib/ClangImporter/ImportName.h +++ b/lib/ClangImporter/ImportName.h @@ -456,6 +456,7 @@ class NameImporter { Optional considerAsyncImport(const clang::ObjCMethodDecl *clangDecl, StringRef &baseName, + SmallVectorImpl &baseNameScratch, SmallVectorImpl ¶mNames, ArrayRef params, bool isInitializer, bool hasCustomName, diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift index c3cb8a68ac511..018c3a64eee1e 100644 --- a/test/ClangImporter/objc_async.swift +++ b/test/ClangImporter/objc_async.swift @@ -18,6 +18,8 @@ func testSlowServer(slowServer: SlowServer) async throws { // still async version... let _: Int = slowServer.doSomethingConflicted("thinking") // expected-error@-1{{call is 'async' but is not marked with 'await'}} + + let _: String? = await try slowServer.fortune() } func testSlowServerSynchronous(slowServer: SlowServer) { diff --git a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h index 39892d4ea9ac2..5a4da7ccf0d45 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h @@ -10,6 +10,7 @@ -(void)findAnswerAsynchronously:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswer(completionHandler:)"))); -(BOOL)findAnswerFailinglyWithError:(NSError * _Nullable * _Nullable)error completion:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswerFailingly(completionHandler:)"))); -(void)doSomethingFun:(NSString *)operation then:(void (^)(void))completionHandler; +-(void)getFortuneWithCompletionHandler:(void (^)(NSString *_Nullable, NSError * _Nullable))handler; @property(readwrite) void (^completionHandler)(NSInteger); -(void)doSomethingConflicted:(NSString *)operation completionHandler:(void (^)(NSInteger))handler; From b0bda135430ea5bccd471946f850a114aaf8521d Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Sat, 3 Oct 2020 23:45:52 -0700 Subject: [PATCH 611/745] LoadBorrowInvalidation: fix mysteriously inverted boolean returns. --- .../LoadBorrowInvalidationChecker.cpp | 25 ++++++++----------- lib/SIL/Verifier/SILVerifier.cpp | 2 +- lib/SIL/Verifier/VerifierPrivate.h | 2 +- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp b/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp index 149c0e5d2aaa3..ea7e6dbf01d9f 100644 --- a/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp +++ b/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp @@ -322,7 +322,7 @@ bool LoadBorrowNeverInvalidatedAnalysis:: // Treat None as a write. if (!writes) { llvm::errs() << "Failed to find cached writes for: " << *address; - return false; + return true; } auto lbiProjPath = @@ -351,13 +351,13 @@ bool LoadBorrowNeverInvalidatedAnalysis:: // our address from lbiProjPath. If not, we have to a if (!lbiProjPath) { llvm::errs() << "Couldn't find path root for load_borrow: " << *lbi; - return false; + return true; } auto writePath = ProjectionPath::getProjectionPath(address, op->get()); if (!writePath) { llvm::errs() << "Couldn't find path root for write: " << *op->getUser(); - return false; + return true; } // The symmetric difference of two projection paths consists of the parts of @@ -368,11 +368,11 @@ bool LoadBorrowNeverInvalidatedAnalysis:: } llvm::errs() << "Write: " << *op->getUser(); - return false; + return true; } // Ok, we are good. - return true; + return false; } //===----------------------------------------------------------------------===// @@ -450,8 +450,7 @@ bool LoadBorrowNeverInvalidatedAnalysis:: return false; } - -bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated( +bool LoadBorrowNeverInvalidatedAnalysis::isInvalidated( LoadBorrowInst *lbi) { // FIXME: To be reenabled separately in a follow-on commit. @@ -470,7 +469,7 @@ bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated( // If we have a let address, then we are already done. if (storage.isLetAccess()) { - return true; + return false; } // At this point, we know that we /may/ have writes. Now we go through various // cases to try and exhaustively identify if those writes overlap with our @@ -484,7 +483,7 @@ bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated( if (auto *bai = dyn_cast(address)) { // We do not have a modify, assume we are correct. if (bai->getAccessKind() != SILAccessKind::Modify) { - return true; + return false; } // Otherwise, validate that any writes to our begin_access is not when the @@ -533,12 +532,8 @@ bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated( } case AccessedStorage::Argument: { auto *arg = cast(storage.getArgument()); - // We return false if visit a non-address here. Object args are things like - // pointer_to_address that we handle earlier. - if (!arg->getType().isAddress()) - return false; if (arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed)) - return true; + return false; return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, arg); } @@ -547,7 +542,7 @@ bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated( // // FIXME: The yielded address could overlap with another address in this // function. - return true; + return false; } case AccessedStorage::Box: { return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 52f47d996d3e4..0ceacbc39e97c 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -1881,7 +1881,7 @@ class SILVerifier : public SILVerifierBase { requireSameType(LBI->getOperand()->getType().getObjectType(), LBI->getType(), "Load operand type and result type mismatch"); - require(loadBorrowNeverInvalidatedAnalysis.isNeverInvalidated(LBI), + require(!loadBorrowNeverInvalidatedAnalysis.isInvalidated(LBI), "Found load borrow that is invalidated by a local write?!"); } diff --git a/lib/SIL/Verifier/VerifierPrivate.h b/lib/SIL/Verifier/VerifierPrivate.h index 8d3d912acccb6..17bf68b6e0a78 100644 --- a/lib/SIL/Verifier/VerifierPrivate.h +++ b/lib/SIL/Verifier/VerifierPrivate.h @@ -34,7 +34,7 @@ class LoadBorrowNeverInvalidatedAnalysis { /// Returns true if exhaustively lbi is guaranteed to never be invalidated by /// local writes. - bool isNeverInvalidated(LoadBorrowInst *lbi); + bool isInvalidated(LoadBorrowInst *lbi); private: bool doesAddressHaveWriteThatInvalidatesLoadBorrow( From 4f05d8a857f08b42c0def949fe37c99d4131be49 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 8 Oct 2020 05:20:33 -0700 Subject: [PATCH 612/745] LoadBorrowImmutabilityChecker renaming. Limit names to a straightforward and unambiguous statement of purpose. They should not pose additional questions which can only be answered by reading the code. Nuanced meaning belongs in descriptions and code comments. These are all examples that legitimately made reading the code very difficult for me: - LoadBorrowInvalidationChecker: what does "invalidation" mean in this context? How does that extend the meaning of "checker"? How can something ever pass a checker and not be invalid? - constructValuesForKey outside of an ADT does not state purpose at all. - wellBehavedWriteAccumulator: Raises questions about what writes are included and the broader semantics of the parent function. It turns out that well-behavedness is handled by the function's return value and has nothing to do with the accumulator. --- lib/SIL/Verifier/CMakeLists.txt | 2 +- ....cpp => LoadBorrowImmutabilityChecker.cpp} | 75 ++++++++++--------- lib/SIL/Verifier/SILVerifier.cpp | 2 +- lib/SIL/Verifier/VerifierPrivate.h | 4 +- 4 files changed, 42 insertions(+), 41 deletions(-) rename lib/SIL/Verifier/{LoadBorrowInvalidationChecker.cpp => LoadBorrowImmutabilityChecker.cpp} (90%) diff --git a/lib/SIL/Verifier/CMakeLists.txt b/lib/SIL/Verifier/CMakeLists.txt index b9e7fb1fb5910..244a34304a862 100644 --- a/lib/SIL/Verifier/CMakeLists.txt +++ b/lib/SIL/Verifier/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources(swiftSIL PRIVATE - LoadBorrowInvalidationChecker.cpp + LoadBorrowImmutabilityChecker.cpp LinearLifetimeChecker.cpp MemoryLifetime.cpp SILOwnershipVerifier.cpp diff --git a/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp b/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp similarity index 90% rename from lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp rename to lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp index ea7e6dbf01d9f..360b7a9782795 100644 --- a/lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp +++ b/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp @@ -1,4 +1,4 @@ -//===--- LoadBorrowInvalidationChecker.cpp --------------------------------===// +//===--- LoadBorrowImmutabilityChecker.cpp --------------------------------===// // // This source file is part of the Swift.org open source project // @@ -13,12 +13,12 @@ /// \file /// /// This file defines a verifier that exhaustively validates that there aren't -/// any load_borrows in a SIL module that are invalidated by a write to their +/// any load_borrows in a SIL module that have in-scope writes to their /// underlying storage. /// //===----------------------------------------------------------------------===// -#define DEBUG_TYPE "sil-load-borrow-invalidation-checker" +#define DEBUG_TYPE "sil-load-borrow-immutability-checker" #include "VerifierPrivate.h" #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" @@ -39,9 +39,9 @@ using namespace swift::silverifier; // Write Gatherer //===----------------------------------------------------------------------===// -static bool constructValuesForBuiltinKey( - Operand *op, BuiltinInst *bi, - SmallVectorImpl &wellBehavedWriteAccumulator) { +// Helper for gatherAddressWrites. +static bool gatherBuiltinWrites(Operand *op, BuiltinInst *bi, + SmallVectorImpl &writeAccumulator) { // If we definitely do not write to memory, just return true early. if (!bi->mayWriteToMemory()) { return true; @@ -49,16 +49,16 @@ static bool constructValuesForBuiltinKey( // TODO: Should we make this an exhaustive list so that when new builtins are // added, they need to actually update this code? - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); return true; } /// Returns true if we were able to ascertain that either the initialValue has /// no write uses or all of the write uses were writes that we could understand. static bool -constructValuesForKey(SILValue initialValue, - SmallVectorImpl &wellBehavedWriteAccumulator) { - SmallVector worklist(initialValue->getUses()); +gatherAddressWrites(SILValue address, + SmallVectorImpl &writeAccumulator) { + SmallVector worklist(address->getUses()); while (!worklist.empty()) { auto *op = worklist.pop_back_val(); @@ -81,7 +81,7 @@ constructValuesForKey(SILValue initialValue, if (auto *oeai = dyn_cast(user)) { // Mutable access! if (oeai->getAccessKind() != OpenedExistentialAccess::Immutable) { - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); } // Otherwise, look through it and continue. @@ -90,8 +90,8 @@ constructValuesForKey(SILValue initialValue, } // Add any destroy_addrs to the resultAccumulator. - if (isa(user)) { - wellBehavedWriteAccumulator.push_back(op); + if (isa(user) || isa(user)) { + writeAccumulator.push_back(op); continue; } @@ -103,13 +103,13 @@ constructValuesForKey(SILValue initialValue, if (auto *mdi = dyn_cast(user)) { if (mdi->getValue() == op->get()) { - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); } continue; } if (isa(user)) { - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); continue; } @@ -121,7 +121,7 @@ constructValuesForKey(SILValue initialValue, if (auto *ccbi = dyn_cast(user)) { if (ccbi->getConsumptionKind() == CastConsumptionKind::TakeAlways || ccbi->getConsumptionKind() == CastConsumptionKind::TakeOnSuccess) { - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); continue; } } @@ -139,7 +139,7 @@ constructValuesForKey(SILValue initialValue, if (auto *bai = dyn_cast(user)) { // If we do not have a read, mark this as a write. if (bai->getAccessKind() != SILAccessKind::Read) { - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); } // Otherwise, add the users to the worklist and continue. @@ -150,7 +150,7 @@ constructValuesForKey(SILValue initialValue, // If we have a load, we just need to mark the load [take] as a write. if (auto *li = dyn_cast(user)) { if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) { - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); } continue; } @@ -158,12 +158,12 @@ constructValuesForKey(SILValue initialValue, #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \ if (auto *li = dyn_cast(user)) { \ if (li->isTake() == IsTake) { \ - wellBehavedWriteAccumulator.push_back(op); \ + writeAccumulator.push_back(op); \ } \ continue; \ } \ if (isa(user)) { \ - wellBehavedWriteAccumulator.push_back(op); \ + writeAccumulator.push_back(op); \ continue; \ } #include "swift/AST/ReferenceStorage.def" @@ -173,7 +173,7 @@ constructValuesForKey(SILValue initialValue, // interprocedural analysis that we do not perform here. if (auto fas = FullApplySite::isa(user)) { if (fas.isIndirectResultOperand(*op)) { - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); continue; } @@ -191,12 +191,12 @@ constructValuesForKey(SILValue initialValue, } if (argConv.isInoutConvention()) { - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); continue; } if (argConv.isOwnedConvention()) { - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); continue; } @@ -207,7 +207,7 @@ constructValuesForKey(SILValue initialValue, } if (auto as = ApplySite::isa(user)) { - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); continue; } @@ -215,14 +215,14 @@ constructValuesForKey(SILValue initialValue, if (auto *cai = dyn_cast(user)) { // If our value is the destination, this is a write. if (cai->getDest() == op->get()) { - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); continue; } // Ok, so we are Src by process of elimination. Make sure we are not being // taken. if (cai->isTakeOfSrc()) { - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); continue; } @@ -231,7 +231,7 @@ constructValuesForKey(SILValue initialValue, } if (isa(user) || isa(user)) { - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); continue; } @@ -261,7 +261,7 @@ constructValuesForKey(SILValue initialValue, } if (info.isIndirectMutating() || info.isConsumed()) { - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); continue; } } @@ -273,19 +273,19 @@ constructValuesForKey(SILValue initialValue, // unconditional_checked_cast_addr does a take on its input memory. if (isa(user)) { - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); continue; } if (auto *ccabi = dyn_cast(user)) { if (ccabi->getConsumptionKind() != CastConsumptionKind::CopyOnSuccess) { - wellBehavedWriteAccumulator.push_back(op); + writeAccumulator.push_back(op); } continue; } if (auto *bi = dyn_cast(user)) { - if (constructValuesForBuiltinKey(op, bi, wellBehavedWriteAccumulator)) { + if (gatherBuiltinWrites(op, bi, writeAccumulator)) { continue; } } @@ -304,14 +304,15 @@ constructValuesForKey(SILValue initialValue, } //===----------------------------------------------------------------------===// -// Load Borrow Never Invalidated Analysis +// Load Borrow Immutability Analysis //===----------------------------------------------------------------------===// -LoadBorrowNeverInvalidatedAnalysis::LoadBorrowNeverInvalidatedAnalysis( +LoadBorrowImmutabilityAnalysis::LoadBorrowImmutabilityAnalysis( DeadEndBlocks &deadEndBlocks) - : cache(constructValuesForKey), deadEndBlocks(deadEndBlocks) {} + : cache(gatherAddressWrites), deadEndBlocks(deadEndBlocks) {} -bool LoadBorrowNeverInvalidatedAnalysis:: +// \p address may be an address, pointer, or box type. +bool LoadBorrowImmutabilityAnalysis:: doesAddressHaveWriteThatInvalidatesLoadBorrow( LoadBorrowInst *lbi, ArrayRef endBorrowUses, SILValue address) { @@ -379,7 +380,7 @@ bool LoadBorrowNeverInvalidatedAnalysis:: // Top Level Entrypoint //===----------------------------------------------------------------------===// -bool LoadBorrowNeverInvalidatedAnalysis:: +bool LoadBorrowImmutabilityAnalysis:: doesBoxHaveWritesThatInvalidateLoadBorrow(LoadBorrowInst *lbi, ArrayRef endBorrowUses, SILValue originalBox) { @@ -450,7 +451,7 @@ bool LoadBorrowNeverInvalidatedAnalysis:: return false; } -bool LoadBorrowNeverInvalidatedAnalysis::isInvalidated( +bool LoadBorrowImmutabilityAnalysis::isInvalidated( LoadBorrowInst *lbi) { // FIXME: To be reenabled separately in a follow-on commit. diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 0ceacbc39e97c..ae46b679e77db 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -664,7 +664,7 @@ class SILVerifier : public SILVerifierBase { llvm::DenseMap InstNumbers; DeadEndBlocks DEBlocks; - LoadBorrowNeverInvalidatedAnalysis loadBorrowNeverInvalidatedAnalysis; + LoadBorrowImmutabilityAnalysis loadBorrowNeverInvalidatedAnalysis; bool SingleFunction = true; SILVerifier(const SILVerifier&) = delete; diff --git a/lib/SIL/Verifier/VerifierPrivate.h b/lib/SIL/Verifier/VerifierPrivate.h index 17bf68b6e0a78..119735c998def 100644 --- a/lib/SIL/Verifier/VerifierPrivate.h +++ b/lib/SIL/Verifier/VerifierPrivate.h @@ -25,12 +25,12 @@ class Operand; namespace silverifier { -class LoadBorrowNeverInvalidatedAnalysis { +class LoadBorrowImmutabilityAnalysis { SmallMultiMapCache cache; DeadEndBlocks &deadEndBlocks; public: - LoadBorrowNeverInvalidatedAnalysis(DeadEndBlocks &deadEndBlocks); + LoadBorrowImmutabilityAnalysis(DeadEndBlocks &deadEndBlocks); /// Returns true if exhaustively lbi is guaranteed to never be invalidated by /// local writes. From 8e3fb44f2d7981fa443c45386342eeb8cdefb440 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Tue, 1 Sep 2020 13:03:37 -0700 Subject: [PATCH 613/745] Rewrite LoadBorrowImmutabilityChecker using AccessPath. The verification will now be as complete as it can be within the capability of our SIL utilities. It is much more aggressive with respect to boxes, references, and pointers. It's more efficient in that it only considers "overlapping" uses. It is also now wholly consistent with the utilities that it uses, so can be reenabled. We could probably go even further and remove the switch statement entirely, relying on AccessPath to recognize any operations that propagate addresses, boxes, or pointers. But I didn't want to potentially weaken enforcement without more careful consideration. --- .../LoadBorrowImmutabilityChecker.cpp | 660 ++++++------------ lib/SIL/Verifier/SILVerifier.cpp | 6 +- lib/SIL/Verifier/VerifierPrivate.h | 15 +- test/SILOptimizer/load_borrow_verify.sil | 60 ++ 4 files changed, 296 insertions(+), 445 deletions(-) create mode 100644 test/SILOptimizer/load_borrow_verify.sil diff --git a/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp b/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp index 360b7a9782795..4862dc62b2ed3 100644 --- a/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp +++ b/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp @@ -39,268 +39,230 @@ using namespace swift::silverifier; // Write Gatherer //===----------------------------------------------------------------------===// -// Helper for gatherAddressWrites. -static bool gatherBuiltinWrites(Operand *op, BuiltinInst *bi, - SmallVectorImpl &writeAccumulator) { - // If we definitely do not write to memory, just return true early. - if (!bi->mayWriteToMemory()) { - return true; +namespace { + +// Visitor for visitAccessPathUses(). +class GatherWritesVisitor : public AccessUseVisitor { + // Result: writes to the AccessPath being visited. + SmallVectorImpl &writeAccumulator; + +public: + GatherWritesVisitor(SmallVectorImpl &writes) + : AccessUseVisitor(AccessUseType::Overlapping, + NestedAccessType::StopAtAccessBegin), + writeAccumulator(writes) {} + + bool visitUse(Operand *op, AccessUseType useTy); +}; + +// Functor for MultiMapCache construction. +struct GatherWrites { + const SILFunction *function; + GatherWrites(const SILFunction *function) : function(function) {} + + bool operator()(const AccessPath &accessPath, + SmallVectorImpl &writeAccumulator) { + GatherWritesVisitor visitor(writeAccumulator); + return visitAccessPathUses(visitor, accessPath, + const_cast(function)); } +}; - // TODO: Should we make this an exhaustive list so that when new builtins are - // added, they need to actually update this code? - writeAccumulator.push_back(op); - return true; -} +} // end anonymous namespace -/// Returns true if we were able to ascertain that either the initialValue has -/// no write uses or all of the write uses were writes that we could understand. -static bool -gatherAddressWrites(SILValue address, - SmallVectorImpl &writeAccumulator) { - SmallVector worklist(address->getUses()); +// Filter out recognized uses that do not write to memory. +// +// TODO: Ensure that all of the conditional-write logic below is encapsulated in +// mayWriteToMemory and just call that instead. Possibly add additional +// verification that visitAccessPathUses recognizes all instructions that may +// propagate pointers (even though they don't write). +bool GatherWritesVisitor::visitUse(Operand *op, AccessUseType useTy) { + // If this operand is for a dependent type, then it does not actually access + // the operand's address value. It only uses the metatype defined by the + // operation (e.g. open_existential). + if (op->isTypeDependent()) { + return true; + } + SILInstruction *user = op->getUser(); + if (isIncidentalUse(user)) { + return true; + } + switch (user->getKind()) { + + // Known reads... + case SILInstructionKind::LoadBorrowInst: + case SILInstructionKind::SelectEnumAddrInst: + case SILInstructionKind::SwitchEnumAddrInst: + case SILInstructionKind::DeallocStackInst: + case SILInstructionKind::DeallocBoxInst: + case SILInstructionKind::WitnessMethodInst: + case SILInstructionKind::ExistentialMetatypeInst: + return true; - while (!worklist.empty()) { - auto *op = worklist.pop_back_val(); + // Known writes... + case SILInstructionKind::DestroyAddrInst: + case SILInstructionKind::DestroyValueInst: + case SILInstructionKind::InjectEnumAddrInst: + case SILInstructionKind::StoreInst: + case SILInstructionKind::AssignInst: + case SILInstructionKind::UncheckedTakeEnumDataAddrInst: + case SILInstructionKind::MarkFunctionEscapeInst: + writeAccumulator.push_back(op); + return true; - // Skip type dependent operands. - if (op->isTypeDependent()) { - continue; - } + // Load/Store variations... +#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \ + case SILInstructionKind::Load##Name##Inst: \ + if (cast(user)->isTake() == IsTake) { \ + writeAccumulator.push_back(op); \ + } \ + return true; \ + \ + case SILInstructionKind::Store##Name##Inst: \ + writeAccumulator.push_back(op); \ + return true; +#include "swift/AST/ReferenceStorage.def" - SILInstruction *user = op->getUser(); + // Ignored pointer uses... - if (Projection::isAddressProjection(user) || - isa(user)) { - for (SILValue r : user->getResults()) { - llvm::copy(r->getUses(), std::back_inserter(worklist)); - } - continue; - } + // Allow store_borrow within the load_borrow scope. + // FIXME: explain why. + case SILInstructionKind::StoreBorrowInst: + // Returns are never in scope. + case SILInstructionKind::ReturnInst: + return true; - if (auto *oeai = dyn_cast(user)) { - // Mutable access! - if (oeai->getAccessKind() != OpenedExistentialAccess::Immutable) { - writeAccumulator.push_back(op); - } + // Reads that may perform a "take"... - // Otherwise, look through it and continue. - llvm::copy(oeai->getUses(), std::back_inserter(worklist)); - continue; + case SILInstructionKind::LoadInst: + if (cast(user)->getOwnershipQualifier() + == LoadOwnershipQualifier::Take) { + writeAccumulator.push_back(op); } + return true; - // Add any destroy_addrs to the resultAccumulator. - if (isa(user) || isa(user)) { + case SILInstructionKind::UnconditionalCheckedCastAddrInst: + return true; + + case SILInstructionKind::CheckedCastAddrBranchInst: { + auto *ccbi = cast(user); + if (ccbi->getConsumptionKind() != CastConsumptionKind::CopyOnSuccess) { writeAccumulator.push_back(op); - continue; } + return true; + } - // load_borrow, load_weak, load_unowned and incidental uses are fine as - // well. - if (isa(user) || isIncidentalUse(user)) { - continue; - } + // Conditional writes... - if (auto *mdi = dyn_cast(user)) { - if (mdi->getValue() == op->get()) { - writeAccumulator.push_back(op); - } - continue; + case SILInstructionKind::CopyAddrInst: + if (cast(user)->getDest() == op->get()) { + writeAccumulator.push_back(op); + return true; } - - if (isa(user)) { + // This operand is the copy source. Check if it is taken. + if (cast(user)->isTakeOfSrc()) { writeAccumulator.push_back(op); - continue; } + return true; - // switch_enum_addr never writes to memory. - if (isa(user)) { - continue; + // If this value is dependent on another, conservatively consider it a write. + // + // FIXME: explain why a mark_dependence effectively writes to storage. + case SILInstructionKind::MarkDependenceInst: + if (cast(user)->getValue() == op->get()) { + writeAccumulator.push_back(op); } + return true; - if (auto *ccbi = dyn_cast(user)) { - if (ccbi->getConsumptionKind() == CastConsumptionKind::TakeAlways || - ccbi->getConsumptionKind() == CastConsumptionKind::TakeOnSuccess) { - writeAccumulator.push_back(op); - continue; - } + // Check for mutable existentials. + case SILInstructionKind::OpenExistentialAddrInst: + if (cast(user)->getAccessKind() + != OpenedExistentialAccess::Immutable) { + writeAccumulator.push_back(op); } + return true; - if (isa(user)) { - continue; + case SILInstructionKind::BeginAccessInst: + if (cast(user)->getAccessKind() != SILAccessKind::Read) { + writeAccumulator.push_back(op); } + return true; - // Skip store_borrow. - if (isa(user)) { - continue; + case SILInstructionKind::BuiltinInst: + if (!cast(user)->mayWriteToMemory()) { + return true; } + writeAccumulator.push_back(op); + return true; - // Look through immutable begin_access. - if (auto *bai = dyn_cast(user)) { - // If we do not have a read, mark this as a write. - if (bai->getAccessKind() != SILAccessKind::Read) { - writeAccumulator.push_back(op); - } - - // Otherwise, add the users to the worklist and continue. - llvm::copy(bai->getUses(), std::back_inserter(worklist)); - continue; + case SILInstructionKind::YieldInst: { + SILYieldInfo info = cast(user)->getYieldInfoForOperand(*op); + if (info.isIndirectInGuaranteed()) { + return true; } - - // If we have a load, we just need to mark the load [take] as a write. - if (auto *li = dyn_cast(user)) { - if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) { - writeAccumulator.push_back(op); - } - continue; + if (info.isIndirectMutating() || info.isConsumed()) { + writeAccumulator.push_back(op); + return true; } - -#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \ - if (auto *li = dyn_cast(user)) { \ - if (li->isTake() == IsTake) { \ - writeAccumulator.push_back(op); \ - } \ - continue; \ - } \ - if (isa(user)) { \ - writeAccumulator.push_back(op); \ - continue; \ + break; // unknown yield convention } -#include "swift/AST/ReferenceStorage.def" - // If we have a FullApplySite, see if we use the value as an - // indirect_guaranteed parameter. If we use it as inout, we need - // interprocedural analysis that we do not perform here. - if (auto fas = FullApplySite::isa(user)) { - if (fas.isIndirectResultOperand(*op)) { - writeAccumulator.push_back(op); - continue; - } - - auto argConv = fas.getArgumentConvention(*op); - - // We should have an indirect convention here. - if (!argConv.isIndirectConvention()) { - llvm::errs() << "Full apply site taking non-indirect operand: " - << *user; - return false; - } - - if (argConv == SILArgumentConvention::Indirect_In_Guaranteed) { - continue; - } - - if (argConv.isInoutConvention()) { - writeAccumulator.push_back(op); - continue; - } - - if (argConv.isOwnedConvention()) { - writeAccumulator.push_back(op); - continue; - } - - // Otherwise, be conservative and return that we had a write that we did - // not understand. - llvm::errs() << "Full apply site not understood: " << *user; - return false; - } + default: + break; + } // end switch(user->getKind()) - if (auto as = ApplySite::isa(user)) { + // If we have a FullApplySite, see if we use the value as an + // indirect_guaranteed parameter. If we use it as inout, we need + // interprocedural analysis that we do not perform here. + if (auto fas = FullApplySite::isa(user)) { + if (fas.isIndirectResultOperand(*op)) { writeAccumulator.push_back(op); - continue; - } - - // Copy addr that read are just loads. - if (auto *cai = dyn_cast(user)) { - // If our value is the destination, this is a write. - if (cai->getDest() == op->get()) { - writeAccumulator.push_back(op); - continue; - } - - // Ok, so we are Src by process of elimination. Make sure we are not being - // taken. - if (cai->isTakeOfSrc()) { - writeAccumulator.push_back(op); - continue; - } - - // Otherwise, we are safe and can continue. - continue; + return true; } + auto argConv = fas.getArgumentConvention(*op); - if (isa(user) || isa(user)) { + // A box or pointer value may be passed directly. Consider that a write. + if (!argConv.isIndirectConvention()) { writeAccumulator.push_back(op); - continue; - } - - if (isa(user)) { - continue; - } - - if (isa(user)) { - continue; - } - - if (isa(user)) { - continue; - } - - // We consider address_to_pointer to be an escape from our system. The - // frontend must protect the uses of the load_borrow as appropriate in other - // ways (for instance by using a mark_dependence). - if (isa(user)) { - continue; - } - - if (auto *yi = dyn_cast(user)) { - auto info = yi->getYieldInfoForOperand(*op); - if (info.isIndirectInGuaranteed()) { - continue; - } - - if (info.isIndirectMutating() || info.isConsumed()) { - writeAccumulator.push_back(op); - continue; - } + return true; } - - // Existential metatype doesnt write to memory. - if (isa(user)) { - continue; + if (argConv == SILArgumentConvention::Indirect_In_Guaranteed) { + return true; } - - // unconditional_checked_cast_addr does a take on its input memory. - if (isa(user)) { + if (argConv.isInoutConvention()) { writeAccumulator.push_back(op); - continue; - } - - if (auto *ccabi = dyn_cast(user)) { - if (ccabi->getConsumptionKind() != CastConsumptionKind::CopyOnSuccess) { - writeAccumulator.push_back(op); - } - continue; + return true; } - - if (auto *bi = dyn_cast(user)) { - if (gatherBuiltinWrites(op, bi, writeAccumulator)) { - continue; - } + if (argConv.isOwnedConvention()) { + writeAccumulator.push_back(op); + return true; } - - // If we did not recognize the user, just return conservatively that it was - // written to in a way we did not understand. - llvm::errs() << "Function: " << user->getFunction()->getName() << "\n"; - llvm::errs() << "Value: " << op->get(); - llvm::errs() << "Unknown instruction!: " << *user; - // llvm::report_fatal_error("Unable to handle instruction?!"); + // Otherwise, be conservative and return that we had a write that we did + // not understand. + llvm::errs() << "Full apply site not understood: " << *user; return false; } - // Ok, we finished our worklist and this address is not being written to. - return true; + // Handle a capture-by-address like a write. + if (auto as = ApplySite::isa(user)) { + writeAccumulator.push_back(op); + return true; + } + // We don't have an inclusive list of all use patterns for non-address + // values. References and pointers can be passed to almost anything that takes + // a value. We assume that visitAccessPathUses has already looked past + // operations that can propagate a reference or pointer, and simply check that + // the leaf use that it returned cannot itself write to memory. + if (!op->get()->getType().isAddress() && !user->mayWriteToMemory()) { + return true; + } + // If we did not recognize the user, just return conservatively that it was + // written to in a way we did not understand. + llvm::errs() << "Function: " << user->getFunction()->getName() << "\n"; + llvm::errs() << "Value: " << op->get(); + llvm::errs() << "Unknown instruction: " << *user; + llvm::report_fatal_error("Unexpected instruction using borrowed address?!"); + return false; } //===----------------------------------------------------------------------===// @@ -308,27 +270,24 @@ gatherAddressWrites(SILValue address, //===----------------------------------------------------------------------===// LoadBorrowImmutabilityAnalysis::LoadBorrowImmutabilityAnalysis( - DeadEndBlocks &deadEndBlocks) - : cache(gatherAddressWrites), deadEndBlocks(deadEndBlocks) {} + DeadEndBlocks &deadEndBlocks, const SILFunction *f) + : cache(GatherWrites(f)), deadEndBlocks(deadEndBlocks) {} // \p address may be an address, pointer, or box type. -bool LoadBorrowImmutabilityAnalysis:: - doesAddressHaveWriteThatInvalidatesLoadBorrow( - LoadBorrowInst *lbi, ArrayRef endBorrowUses, - SILValue address) { +bool LoadBorrowImmutabilityAnalysis::isImmutableInScope( + LoadBorrowInst *lbi, ArrayRef endBorrowUses, + AccessPath accessPath) { + SmallPtrSet visitedBlocks; LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); - auto writes = cache.get(address); + auto writes = cache.get(accessPath); // Treat None as a write. if (!writes) { - llvm::errs() << "Failed to find cached writes for: " << *address; - return true; + llvm::errs() << "Failed to find cached writes for: "; + accessPath.getStorage().print(llvm::errs()); + return false; } - - auto lbiProjPath = - ProjectionPath::getProjectionPath(address, lbi->getOperand()); - // Then for each write... for (auto *op : *writes) { visitedBlocks.clear(); @@ -337,140 +296,38 @@ bool LoadBorrowImmutabilityAnalysis:: if (deadEndBlocks.isDeadEnd(op->getUser()->getParent())) { continue; } - // See if the write is within the load borrow's lifetime. If it isn't, we // don't have to worry about it. if (!checker.validateLifetime(lbi, endBorrowUses, op)) { continue; } - - // Ok, we found a write that overlaps with our load_borrow. We now need to - // prove that the write is to an address that can not trivially alias our - // load_borrow. - // - // First we check if we were actually able to compute a projection path to - // our address from lbiProjPath. If not, we have to a - if (!lbiProjPath) { - llvm::errs() << "Couldn't find path root for load_borrow: " << *lbi; - return true; - } - - auto writePath = ProjectionPath::getProjectionPath(address, op->get()); - if (!writePath) { - llvm::errs() << "Couldn't find path root for write: " << *op->getUser(); - return true; - } - - // The symmetric difference of two projection paths consists of the parts of - // two projection paths that are unique to each of the two. Naturally, if - // such a thing exists we must have that the two values can not alias. - if (writePath->hasNonEmptySymmetricDifference(*lbiProjPath)) { - continue; - } - llvm::errs() << "Write: " << *op->getUser(); - return true; + return false; } - // Ok, we are good. - return false; + return true; } //===----------------------------------------------------------------------===// // Top Level Entrypoint //===----------------------------------------------------------------------===// -bool LoadBorrowImmutabilityAnalysis:: - doesBoxHaveWritesThatInvalidateLoadBorrow(LoadBorrowInst *lbi, - ArrayRef endBorrowUses, - SILValue originalBox) { - SILValue box = originalBox; - SmallVector otherProjBoxInsts; - SmallVector worklist; - - // First walk up use->def from our project_box's operand to the actual box. As - // we do the walk, we gather up any project_box that we see (for later write - // checking) and then if we are either a copy_value or a begin_borrow strip - // the value and continue. - // - // So by the end of this loop, the worklist will contain not the copy_value, - // begin_borrow that we stripped, but rather any uses of those copy_value, - // begin_borrow that we stripped. This is to make sure we find project_box - // from webs of copy_value, begin_borrow. - do { - for (auto *use : box->getUses()) { - auto *user = use->getUser(); - if (auto *pbi = dyn_cast(user)) { - otherProjBoxInsts.push_back(pbi); - continue; - } - - if (isa(user) || isa(user)) { - worklist.push_back(user); - } - } - - if (isa(box) || isa(box)) { - box = cast(box)->getOperand(0); - continue; - } - } while (false); - - // Now that we finished our walk and gathered up copy_value, begin_borrow, - // visit each of those instructions recursively def->use, looking through - // further copy_value, begin_borrow and stashing any project_box we see for - // later write checking. - while (!worklist.empty()) { - auto *inst = worklist.pop_back_val(); - for (SILValue result : inst->getResults()) { - for (auto *use : result->getUses()) { - auto *user = use->getUser(); - if (auto *pbi = dyn_cast(user)) { - otherProjBoxInsts.push_back(pbi); - continue; - } - - if (isa(user) || isa(user)) { - worklist.push_back(user); - } - } - } - } - - // Ok! We now know that we have all project_box from the "local phi" web of - // the alloc_box our project_box is from. Now check that none of those have - // simple aliasing writes when our load_borrow is live. - while (!otherProjBoxInsts.empty()) { - auto *otherProjBox = otherProjBoxInsts.pop_back_val(); - - if (doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, - otherProjBox)) { - return true; - } - } - - return false; -} -bool LoadBorrowImmutabilityAnalysis::isInvalidated( - LoadBorrowInst *lbi) { +bool LoadBorrowImmutabilityAnalysis::isImmutable(LoadBorrowInst *lbi) { + // Find either the enclosing access scope or a single base address. // FIXME: To be reenabled separately in a follow-on commit. return true; - SILValue address = getAccessScope(lbi->getOperand()); - if (!address) - return false; - - auto storage = AccessedStorage::compute(address); - // If we couldn't find an access storage, return that we are assumed to write. - if (!storage) { - llvm::errs() << "Couldn't compute access storage?!\n"; - return false; + AccessPath accessPath = AccessPath::computeInScope(lbi->getOperand()); + // Bail on an invalid AccessPath. AccessPath completeness is verified + // independently--it may be invalid in extraordinary situations. When + // AccessPath is valid, we know all its uses are recognizable. + if (!accessPath.isValid()) { + return true; } - // If we have a let address, then we are already done. - if (storage.isLetAccess()) { - return false; + if (accessPath.getStorage().isLetAccess()) { + return true; } // At this point, we know that we /may/ have writes. Now we go through various // cases to try and exhaustively identify if those writes overlap with our @@ -480,105 +337,38 @@ bool LoadBorrowImmutabilityAnalysis::isInvalidated( std::back_inserter(endBorrowUses), [](EndBorrowInst *ebi) { return &ebi->getAllOperands()[0]; }); - // If we have a begin_access and... - if (auto *bai = dyn_cast(address)) { + switch (accessPath.getStorage().getKind()) { + case AccessedStorage::Nested: { + // If we have a begin_access and... + auto *bai = cast(accessPath.getStorage().getValue()); // We do not have a modify, assume we are correct. if (bai->getAccessKind() != SILAccessKind::Modify) { - return false; + return true; } - // Otherwise, validate that any writes to our begin_access is not when the // load_borrow's result is live. // - // FIXME: do we verify that the load_borrow scope is always nested within - // the begin_access scope (to ensure no aliasing access)? - return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, - bai); - } - - // FIXME: the subsequent checks appear to assume that 'address' is not aliased - // within the scope of the load_borrow. This can only be assumed when either - // the load_borrow is nested within an access scope or when the - // storage.isUniquelyIdentified() and all uses of storage.getRoot() have been - // analyzed. The later can be done with AccessPath::collectUses(). - - // Check if our unidentified access storage is a project_box. In such a case, - // validate that all uses of the project_box are not writes that overlap with - // our load_borrow's result. These are things that may not be a formal access - // base. - // - // FIXME: Remove this PointerToAddress check. It appears to be incorrect. we - // don't verify anywhere that a pointer_to_address cannot itself be derived - // from another address that is accessible in the same function, either - // because it was returned from a call or directly address_to_pointer'd. - if (isa(address)) { - return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, - address); - } - - // FIXME: This ProjectBoxInst - // If we have a project_box, we need to see if our base, modulo begin_borrow, - // copy_value have any other project_box that we need to analyze. - if (auto *pbi = dyn_cast(address)) { - return doesBoxHaveWritesThatInvalidateLoadBorrow(lbi, endBorrowUses, - pbi->getOperand()); - } - - - switch (storage.getKind()) { - case AccessedStorage::Stack: { - // For now assume that stack is safe. - return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, - address); + // TODO: As a separate analysis, verify that the load_borrow scope is always + // nested within the begin_access scope (to ensure no aliasing access). + return isImmutableInScope(lbi, endBorrowUses, accessPath); } case AccessedStorage::Argument: { - auto *arg = cast(storage.getArgument()); - if (arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed)) - return false; - return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, - arg); - } - case AccessedStorage::Yield: { - // For now, do this. Otherwise, check for in_guaranteed, etc. - // - // FIXME: The yielded address could overlap with another address in this - // function. - return false; - } - case AccessedStorage::Box: { - return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, - address); - } - case AccessedStorage::Class: { - // Check that the write to the class's memory doesn't overlap with our - // load_borrow. - // - // FIXME: how do we know that other projections of the same object don't - // occur within the same function? - return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, - address); - } - case AccessedStorage::Tail: { - // This should be as strong as the Class address case, but to handle it we - // need to find all aliases of the object and all tail projections within - // that object. - return false; - } - case AccessedStorage::Global: { - // Check that the write to the class's memory doesn't overlap with our - // load_borrow. - return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses, - address); - } - case AccessedStorage::Unidentified: { - // Otherwise, we didn't understand this, so bail. - llvm::errs() << "Unidentified access storage: "; - storage.dump(); - return false; - } - case AccessedStorage::Nested: { - llvm_unreachable("Should have been handled above"); + auto *arg = + cast(accessPath.getStorage().getArgument()); + if (arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed)) { + return true; + } + return isImmutableInScope(lbi, endBorrowUses, accessPath); } + // FIXME: A yielded address could overlap with another in this function. + case AccessedStorage::Yield: + case AccessedStorage::Stack: + case AccessedStorage::Box: + case AccessedStorage::Class: + case AccessedStorage::Tail: + case AccessedStorage::Global: + case AccessedStorage::Unidentified: + return isImmutableInScope(lbi, endBorrowUses, accessPath); } llvm_unreachable("Covered switch isn't covered?!"); } diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index ae46b679e77db..08f70846d53fe 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -664,7 +664,7 @@ class SILVerifier : public SILVerifierBase { llvm::DenseMap InstNumbers; DeadEndBlocks DEBlocks; - LoadBorrowImmutabilityAnalysis loadBorrowNeverInvalidatedAnalysis; + LoadBorrowImmutabilityAnalysis loadBorrowImmutabilityAnalysis; bool SingleFunction = true; SILVerifier(const SILVerifier&) = delete; @@ -863,7 +863,7 @@ class SILVerifier : public SILVerifierBase { fnConv(F.getConventionsInContext()), TC(F.getModule().Types), OpenedArchetypes(&F), Dominance(nullptr), InstNumbers(numInstsInFunction(F)), DEBlocks(&F), - loadBorrowNeverInvalidatedAnalysis(DEBlocks), + loadBorrowImmutabilityAnalysis(DEBlocks, &F), SingleFunction(SingleFunction) { if (F.isExternalDeclaration()) return; @@ -1881,7 +1881,7 @@ class SILVerifier : public SILVerifierBase { requireSameType(LBI->getOperand()->getType().getObjectType(), LBI->getType(), "Load operand type and result type mismatch"); - require(!loadBorrowNeverInvalidatedAnalysis.isInvalidated(LBI), + require(loadBorrowImmutabilityAnalysis.isImmutable(LBI), "Found load borrow that is invalidated by a local write?!"); } diff --git a/lib/SIL/Verifier/VerifierPrivate.h b/lib/SIL/Verifier/VerifierPrivate.h index 119735c998def..29fdccc569f63 100644 --- a/lib/SIL/Verifier/VerifierPrivate.h +++ b/lib/SIL/Verifier/VerifierPrivate.h @@ -14,6 +14,7 @@ #define SWIFT_SIL_VERIFIER_VERIFIERPRIVATE_H #include "swift/Basic/MultiMapCache.h" +#include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/SILValue.h" namespace swift { @@ -26,21 +27,21 @@ class Operand; namespace silverifier { class LoadBorrowImmutabilityAnalysis { - SmallMultiMapCache cache; + SmallMultiMapCache cache; DeadEndBlocks &deadEndBlocks; public: - LoadBorrowImmutabilityAnalysis(DeadEndBlocks &deadEndBlocks); + LoadBorrowImmutabilityAnalysis(DeadEndBlocks &deadEndBlocks, + const SILFunction *f); /// Returns true if exhaustively lbi is guaranteed to never be invalidated by /// local writes. - bool isInvalidated(LoadBorrowInst *lbi); + bool isImmutable(LoadBorrowInst *lbi); private: - bool doesAddressHaveWriteThatInvalidatesLoadBorrow( - LoadBorrowInst *lbi, ArrayRef endBorrowUses, SILValue address); - bool doesBoxHaveWritesThatInvalidateLoadBorrow( - LoadBorrowInst *lbi, ArrayRef endBorrowUses, SILValue box); + bool isImmutableInScope(LoadBorrowInst *lbi, + ArrayRef endBorrowUses, + AccessPath accessPath); }; } // namespace silverifier diff --git a/test/SILOptimizer/load_borrow_verify.sil b/test/SILOptimizer/load_borrow_verify.sil new file mode 100644 index 0000000000000..72620cd797833 --- /dev/null +++ b/test/SILOptimizer/load_borrow_verify.sil @@ -0,0 +1,60 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s | %FileCheck %s + +// Test that the LoadBorrowImmutabilityChecker accepts these cases. +// The verification should not bail-out on these, but there's no way +// to check that. + +// RUN: %target-sil-opt -enable-sil-verify-all %s -global-opt | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift + +private var testGlobal: Int64 + +sil_global private @globalinit_33_00F4D2139E6BDDFEC71E5005B67B5674_token0 : $Builtin.Word + +sil_global private @$s4test10testGlobalSivp : $Int64 + +sil private @globalinit_33_00F4D2139E6BDDFEC71E5005B67B5674_func0 : $@convention(c) () -> () { +bb0: + alloc_global @$s4test10testGlobalSivp + %1 = global_addr @$s4test10testGlobalSivp : $*Int64 + %2 = integer_literal $Builtin.Int64, 27 + %3 = struct $Int64 (%2 : $Builtin.Int64) + store %3 to %1 : $*Int64 + %5 = tuple () + return %5 : $() +} + +sil hidden [global_init] @$s4test10testGlobalSivau : $@convention(thin) () -> Builtin.RawPointer { +bb0: + %0 = global_addr @globalinit_33_00F4D2139E6BDDFEC71E5005B67B5674_token0 : $*Builtin.Word + %1 = address_to_pointer %0 : $*Builtin.Word to $Builtin.RawPointer + %2 = function_ref @globalinit_33_00F4D2139E6BDDFEC71E5005B67B5674_func0 : $@convention(c) () -> () + %3 = builtin "once"(%1 : $Builtin.RawPointer, %2 : $@convention(c) () -> ()) : $() + %4 = global_addr @$s4test10testGlobalSivp : $*Int64 + %5 = address_to_pointer %4 : $*Int64 to $Builtin.RawPointer + return %5 : $Builtin.RawPointer +} + +// CHECK-LABEL: sil @dont_propagate_global_with_multiple_writes +// CHECK: [[V:%[0-9]+]] = load +// CHECK: return [[V]] +// CHECK: } // end sil function 'dont_propagate_global_with_multiple_writes' +sil @dont_propagate_global_with_multiple_writes : $@convention(thin) (Int64) -> Int64 { +bb0(%0 : $Int64): + %2 = function_ref @$s4test10testGlobalSivau : $@convention(thin) () -> Builtin.RawPointer + %3 = apply %2() : $@convention(thin) () -> Builtin.RawPointer + %4 = pointer_to_address %3 : $Builtin.RawPointer to [strict] $*Int64 + %5 = integer_literal $Builtin.Int64, 42 + %6 = struct $Int64 (%5 : $Builtin.Int64) + %7 = begin_access [modify] [dynamic] [no_nested_conflict] %4 : $*Int64 + store %6 to %7 : $*Int64 + end_access %7 : $*Int64 + %33 = begin_access [read] [dynamic] [no_nested_conflict] %4 : $*Int64 + %35 = load %33 : $*Int64 + end_access %33 : $*Int64 + return %35 : $Int64 +} From 0f1beedfb7cb62a8e9c8969152615593a382877e Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Wed, 14 Oct 2020 00:40:09 -0700 Subject: [PATCH 614/745] Reenable load-borrow checker. --- lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp b/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp index 4862dc62b2ed3..f9d78fc8012bb 100644 --- a/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp +++ b/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp @@ -313,11 +313,6 @@ bool LoadBorrowImmutabilityAnalysis::isImmutableInScope( //===----------------------------------------------------------------------===// bool LoadBorrowImmutabilityAnalysis::isImmutable(LoadBorrowInst *lbi) { - // Find either the enclosing access scope or a single base address. - - // FIXME: To be reenabled separately in a follow-on commit. - return true; - AccessPath accessPath = AccessPath::computeInScope(lbi->getOperand()); // Bail on an invalid AccessPath. AccessPath completeness is verified // independently--it may be invalid in extraordinary situations. When From 987d055b8c69347a4cd8533d7b59c7ceaccee0fe Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Thu, 15 Oct 2020 14:06:30 -0700 Subject: [PATCH 615/745] [Mangler] Handle mangling for Clang types not derivable from Swift types. --- docs/ABI/Mangling.rst | 9 ++- include/swift/AST/ASTMangler.h | 3 + include/swift/Demangling/DemangleNodes.def | 3 + include/swift/Demangling/Demangler.h | 3 +- include/swift/Demangling/TypeDecoder.h | 32 +++++++--- lib/AST/ASTMangler.cpp | 57 ++++++++++++++++- lib/Demangling/Demangler.cpp | 63 +++++++++++++++---- lib/Demangling/NodePrinter.cpp | 52 +++++++++++++-- lib/Demangling/OldDemangler.cpp | 18 ++++-- lib/Demangling/OldRemangler.cpp | 35 +++++++---- lib/Demangling/Remangler.cpp | 59 +++++++++++++++-- .../Inputs/manglings-with-clang-types.txt | 2 + test/Demangle/clang-function-types.swift | 39 ++++++++++++ .../clang-importer-sdk/usr/include/ctypes.h | 4 ++ .../usr/include/objc/NSObject.h | 1 + test/Serialization/clang-function-types.swift | 3 +- 16 files changed, 328 insertions(+), 55 deletions(-) create mode 100644 test/Demangle/Inputs/manglings-with-clang-types.txt create mode 100644 test/Demangle/clang-function-types.swift diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index bc0b78d5c1edf..094745b7df02a 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -523,8 +523,10 @@ Types FUNCTION-KIND ::= 'U' // uncurried function type (currently not used) FUNCTION-KIND ::= 'K' // @auto_closure function type (noescape) FUNCTION-KIND ::= 'B' // objc block function type - FUNCTION-KIND ::= 'L' // objc block function type (escaping) (DWARF only; otherwise use 'B') + FUNCTION-KIND ::= 'zB' C-TYPE // objc block type with non-canonical C type + FUNCTION-KIND ::= 'L' // objc block function type with canonical C type (escaping) (DWARF only; otherwise use 'B' or 'zB' C-TYPE) FUNCTION-KIND ::= 'C' // C function pointer type + FUNCTION-KIND ::= 'zC' C-TYPE // C function pointer type with with non-canonical C type FUNCTION-KIND ::= 'A' // @auto_closure function type (escaping) FUNCTION-KIND ::= 'E' // function type (noescape) FUNCTION-KIND ::= 'F' // @differentiable function type @@ -532,6 +534,9 @@ Types FUNCTION-KIND ::= 'H' // @differentiable(linear) function type FUNCTION-KIND ::= 'I' // @differentiable(linear) function type (escaping) + C-TYPE is mangled according to the Itanium ABI, and prefixed with the length. + Non-ASCII identifiers are preserved as-is; we do not use Punycode. + function-signature ::= params-type params-type async? throws? // results and parameters params-type ::= type 'z'? 'h'? // tuple in case of multiple parameters or a single parameter with a single tuple type @@ -618,7 +623,9 @@ mangled in to disambiguate. CALLEE-CONVENTION ::= 't' // thin FUNC-REPRESENTATION ::= 'B' // C block invocation function + FUNC-REPRESENTATION ::= 'zB' C-TYPE // C block invocation function with non-canonical C type FUNC-REPRESENTATION ::= 'C' // C global function + FUNC-REPRESENTATION ::= 'zC' C-TYPE // C global function with non-canonical C type FUNC-REPRESENTATION ::= 'M' // Swift method FUNC-REPRESENTATION ::= 'J' // ObjC method FUNC-REPRESENTATION ::= 'K' // closure diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index b244e766ffc52..af7294875332e 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -318,6 +318,9 @@ class ASTMangler : public Mangler { const ValueDecl *forDecl = nullptr); void appendFunctionType(AnyFunctionType *fn, bool isAutoClosure = false, const ValueDecl *forDecl = nullptr); + void appendClangType(AnyFunctionType *fn); + template + void appendClangType(FnType *fn, llvm::raw_svector_ostream &os); void appendFunctionSignature(AnyFunctionType *fn, const ValueDecl *forDecl = nullptr); diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 1c3c612f9e98a..b74eb68485802 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -44,6 +44,7 @@ NODE(BoundGenericTypeAlias) NODE(BoundGenericFunction) NODE(BuiltinTypeName) NODE(CFunctionPointer) +NODE(ClangType) CONTEXT_NODE(Class) NODE(ClassMetadataBaseOffset) NODE(ConcreteProtocolConformance) @@ -119,6 +120,8 @@ NODE(ImplEscaping) NODE(ImplConvention) NODE(ImplDifferentiability) NODE(ImplFunctionAttribute) +NODE(ImplFunctionConvention) +NODE(ImplFunctionConventionName) NODE(ImplFunctionType) NODE(ImplInvocationSubstitutions) CONTEXT_NODE(ImplicitClosure) diff --git a/include/swift/Demangling/Demangler.h b/include/swift/Demangling/Demangler.h index 3616ff426af86..afe1bce8f0a83 100644 --- a/include/swift/Demangling/Demangler.h +++ b/include/swift/Demangling/Demangler.h @@ -505,7 +505,7 @@ class Demangler : public NodeFactory { NodePointer demangleAnyGenericType(Node::Kind kind); NodePointer demangleExtensionContext(); NodePointer demanglePlainFunction(); - NodePointer popFunctionType(Node::Kind kind); + NodePointer popFunctionType(Node::Kind kind, bool hasClangType = false); NodePointer popFunctionParams(Node::Kind kind); NodePointer popFunctionParamLabels(NodePointer FuncType); NodePointer popTuple(); @@ -522,6 +522,7 @@ class Demangler : public NodeFactory { NodePointer demangleImplResultConvention(Node::Kind ConvKind); NodePointer demangleImplDifferentiability(); NodePointer demangleImplFunctionType(); + NodePointer demangleClangType(); NodePointer demangleMetatype(); NodePointer demanglePrivateContextDescriptor(); NodePointer createArchetypeRef(int depth, int i); diff --git a/include/swift/Demangling/TypeDecoder.h b/include/swift/Demangling/TypeDecoder.h index fb33edf878252..932c42d54ce35 100644 --- a/include/swift/Demangling/TypeDecoder.h +++ b/include/swift/Demangling/TypeDecoder.h @@ -631,6 +631,12 @@ class TypeDecoder { } unsigned firstChildIdx = 0; + if (Node->getChild(firstChildIdx)->getKind() == NodeKind::ClangType) { + // [TODO: synthesize-Clang-type-from-mangled-name] Use the first child + // to create a ClangTypeInfo. + ++firstChildIdx; + } + bool isThrow = false; if (Node->getChild(firstChildIdx)->getKind() == NodeKind::ThrowsAnnotation) { @@ -695,18 +701,28 @@ class TypeDecoder { } else if (child->getText() == "@callee_guaranteed") { calleeConvention = ImplParameterConvention::Direct_Guaranteed; } - } else if (child->getKind() == NodeKind::ImplFunctionAttribute) { - if (!child->hasText()) - return MAKE_NODE_TYPE_ERROR0(child, "expected text"); - - StringRef text = child->getText(); - if (text == "@convention(c)") { + } else if (child->getKind() == NodeKind::ImplFunctionConvention) { + if (child->getNumChildren() == 0) + return MAKE_NODE_TYPE_ERROR0(child, "expected grandchildren"); + if ((child->getFirstChild()->getKind() != + NodeKind::ImplFunctionConventionName) || + !child->getFirstChild()->hasText()) + return MAKE_NODE_TYPE_ERROR0(child, "expected convention name"); + + // [TODO: synthesize-Clang-type-from-mangled-name] If there are two + // grand-children, the second is going to be the mangled Clang type. + StringRef text = child->getFirstChild()->getText(); + if (text == "c") { flags = flags.withRepresentation(ImplFunctionRepresentation::CFunctionPointer); - } else if (text == "@convention(block)") { + } else if (text == "block") { flags = flags.withRepresentation(ImplFunctionRepresentation::Block); - } else if (text == "@async") { + } + } else if (child->getKind() == NodeKind::ImplFunctionAttribute) { + if (!child->hasText()) + return MAKE_NODE_TYPE_ERROR0(child, "expected text"); + if (child->getText() == "@async") { flags = flags.withAsync(); } } else if (child->getKind() == NodeKind::ImplDifferentiable) { diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 48fb9337f6459..490385afbe1a1 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -30,24 +30,27 @@ #include "swift/AST/ProtocolConformanceRef.h" #include "swift/AST/SILLayout.h" #include "swift/Basic/Defer.h" +#include "swift/ClangImporter/ClangImporter.h" +#include "swift/Demangling/Demangler.h" #include "swift/Demangling/ManglingMacros.h" #include "swift/Demangling/ManglingUtils.h" -#include "swift/Demangling/Demangler.h" #include "swift/Strings.h" #include "clang/AST/ASTContext.h" -#include "clang/Basic/CharInfo.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Mangle.h" +#include "clang/Basic/CharInfo.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Support/CommandLine.h" + +#include using namespace swift; using namespace swift::Mangle; @@ -1653,15 +1656,35 @@ void ASTMangler::appendImplFunctionType(SILFunctionType *fn) { OpArgs.push_back('t'); } + bool mangleClangType = fn->getASTContext().LangOpts.UseClangFunctionTypes && + fn->hasNonDerivableClangType(); + + auto appendClangTypeToVec = [this, fn](auto &Vec) { + llvm::raw_svector_ostream OpArgsOS(Vec); + appendClangType(fn, OpArgsOS); + }; + switch (fn->getRepresentation()) { case SILFunctionTypeRepresentation::Thick: case SILFunctionTypeRepresentation::Thin: break; case SILFunctionTypeRepresentation::Block: + if (!mangleClangType) { + OpArgs.push_back('B'); + break; + } + OpArgs.push_back('z'); OpArgs.push_back('B'); + appendClangTypeToVec(OpArgs); break; case SILFunctionTypeRepresentation::CFunctionPointer: + if (!mangleClangType) { + OpArgs.push_back('C'); + break; + } + OpArgs.push_back('z'); OpArgs.push_back('C'); + appendClangTypeToVec(OpArgs); break; case SILFunctionTypeRepresentation::ObjCMethod: OpArgs.push_back('O'); @@ -2244,6 +2267,9 @@ void ASTMangler::appendFunctionType(AnyFunctionType *fn, bool isAutoClosure, appendFunctionSignature(fn, forDecl); + bool mangleClangType = fn->getASTContext().LangOpts.UseClangFunctionTypes && + fn->hasNonDerivableClangType(); + // Note that we do not currently use thin representations in the AST // for the types of function decls. This may need to change at some // point, in which case the uncurry logic can probably migrate to that @@ -2256,6 +2282,10 @@ void ASTMangler::appendFunctionType(AnyFunctionType *fn, bool isAutoClosure, // changes to better support thin functions. switch (fn->getRepresentation()) { case AnyFunctionType::Representation::Block: + if (mangleClangType) { + appendOperator("XzB"); + return appendClangType(fn); + } // We distinguish escaping and non-escaping blocks, but only in the DWARF // mangling, because the ABI is already set. if (!fn->isNoEscape() && DWARFMangling) @@ -2287,10 +2317,31 @@ void ASTMangler::appendFunctionType(AnyFunctionType *fn, bool isAutoClosure, return appendOperator("c"); case AnyFunctionType::Representation::CFunctionPointer: + if (mangleClangType) { + appendOperator("XzC"); + return appendClangType(fn); + } return appendOperator("XC"); } } +template +void ASTMangler::appendClangType(FnType *fn, llvm::raw_svector_ostream &out) { + auto clangType = fn->getClangTypeInfo().getType(); + SmallString<64> scratch; + llvm::raw_svector_ostream scratchOS(scratch); + clang::ASTContext &clangCtx = + fn->getASTContext().getClangModuleLoader()->getClangASTContext(); + std::unique_ptr mangler{ + clang::ItaniumMangleContext::create(clangCtx, clangCtx.getDiagnostics())}; + mangler->mangleTypeName(clang::QualType(clangType, 0), scratchOS); + out << scratchOS.str().size() << scratchOS.str(); +} + +void ASTMangler::appendClangType(AnyFunctionType *fn) { + appendClangType(fn, Buffer); +} + void ASTMangler::appendFunctionSignature(AnyFunctionType *fn, const ValueDecl *forDecl) { appendFunctionResultType(fn->getResult(), forDecl); diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index d6a06b003f6da..83519d1152c8d 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -1228,8 +1228,13 @@ NodePointer Demangler::demanglePlainFunction() { return createWithChildren(Node::Kind::Function, Ctx, Name, Type); } -NodePointer Demangler::popFunctionType(Node::Kind kind) { +NodePointer Demangler::popFunctionType(Node::Kind kind, bool hasClangType) { NodePointer FuncType = createNode(kind); + NodePointer ClangType = nullptr; + if (hasClangType) { + ClangType = demangleClangType(); + } + addChild(FuncType, ClangType); addChild(FuncType, popNode(Node::Kind::ThrowsAnnotation)); addChild(FuncType, popNode(Node::Kind::AsyncAnnotation)); @@ -1748,6 +1753,16 @@ NodePointer Demangler::demangleImplDifferentiability() { return createNode(Node::Kind::ImplDifferentiability, attr); } +NodePointer Demangler::demangleClangType() { + int numChars = demangleNatural(); + if (numChars <= 0 || Pos + numChars > Text.size()) + return nullptr; + CharVector mangledClangType; + mangledClangType.append(StringRef(Text.data() + Pos, numChars), *this); + Pos = Pos + numChars; + return createNode(Node::Kind::ClangType, mangledClangType); +} + NodePointer Demangler::demangleImplFunctionType() { NodePointer type = createNode(Node::Kind::ImplFunctionType); @@ -1806,20 +1821,33 @@ NodePointer Demangler::demangleImplFunctionType() { } type->addChild(createNode(Node::Kind::ImplConvention, CAttr), *this); - const char *FAttr = nullptr; + const char *FConv = nullptr; + bool hasClangType = false; switch (nextChar()) { - case 'B': FAttr = "@convention(block)"; break; - case 'C': FAttr = "@convention(c)"; break; - case 'M': FAttr = "@convention(method)"; break; - case 'O': FAttr = "@convention(objc_method)"; break; - case 'K': FAttr = "@convention(closure)"; break; - case 'W': FAttr = "@convention(witness_method)"; break; - default: - pushBack(); - break; + case 'B': FConv = "block"; break; + case 'C': FConv = "c"; break; + case 'z': { + switch (nextChar()) { + case 'B': hasClangType = true; FConv = "block"; break; + case 'C': hasClangType = true; FConv = "c"; break; + default: pushBack(); pushBack(); break; + } + break; + } + case 'M': FConv = "method"; break; + case 'O': FConv = "objc_method"; break; + case 'K': FConv = "closure"; break; + case 'W': FConv = "witness_method"; break; + default: pushBack(); break; + } + if (FConv) { + auto FAttrNode = createNode(Node::Kind::ImplFunctionConvention); + FAttrNode->addChild( + createNode(Node::Kind::ImplFunctionConventionName, FConv), *this); + if (hasClangType) + FAttrNode->addChild(demangleClangType(), *this); + type->addChild(FAttrNode, *this); } - if (FAttr) - type->addChild(createNode(Node::Kind::ImplFunctionAttribute, FAttr), *this); const char *CoroAttr = nullptr; if (nextIf('A')) @@ -2863,6 +2891,15 @@ NodePointer Demangler::demangleSpecialType() { return popFunctionType(Node::Kind::ObjCBlock); case 'C': return popFunctionType(Node::Kind::CFunctionPointer); + case 'z': + switch (auto cchar = nextChar()) { + case 'B': + return popFunctionType(Node::Kind::ObjCBlock, true); + case 'C': + return popFunctionType(Node::Kind::CFunctionPointer, true); + default: + return nullptr; + } case 'F': return popFunctionType(Node::Kind::DifferentiableFunctionType); case 'G': diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 5287239caffc5..03023b0a9c990 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -338,6 +338,7 @@ class NodePrinter { case Node::Kind::AutoClosureType: case Node::Kind::BaseConformanceDescriptor: case Node::Kind::BaseWitnessTableAccessor: + case Node::Kind::ClangType: case Node::Kind::ClassMetadataBaseOffset: case Node::Kind::CFunctionPointer: case Node::Kind::Constructor: @@ -403,6 +404,8 @@ class NodePrinter { case Node::Kind::ImplConvention: case Node::Kind::ImplDifferentiability: case Node::Kind::ImplFunctionAttribute: + case Node::Kind::ImplFunctionConvention: + case Node::Kind::ImplFunctionConventionName: case Node::Kind::ImplFunctionType: case Node::Kind::ImplInvocationSubstitutions: case Node::Kind::ImplPatternSubstitutions: @@ -750,11 +753,22 @@ class NodePrinter { } void printFunctionType(NodePointer LabelList, NodePointer node) { - if (node->getNumChildren() < 2 || node->getNumChildren() > 4) { + if (node->getNumChildren() < 2 || node->getNumChildren() > 5) { setInvalid(); return; } + auto printConventionWithMangledCType = [this, + node](const char *convention) { + Printer << "@convention(" << convention; + if (node->getFirstChild()->getKind() == Node::Kind::ClangType) { + Printer << ", mangledCType: \""; + print(node->getFirstChild()); + Printer << '"'; + } + Printer << ") "; + }; + switch (node->getKind()) { case Node::Kind::FunctionType: case Node::Kind::UncurriedFunctionType: @@ -766,11 +780,14 @@ class NodePrinter { case Node::Kind::ThinFunctionType: Printer << "@convention(thin) "; break; case Node::Kind::CFunctionPointer: - Printer << "@convention(c) "; break; - case Node::Kind::ObjCBlock: - Printer << "@convention(block) "; break; + printConventionWithMangledCType("c"); + break; case Node::Kind::EscapingObjCBlock: - Printer << "@escaping @convention(block) "; break; + Printer << "@escaping "; + LLVM_FALLTHROUGH; + case Node::Kind::ObjCBlock: + printConventionWithMangledCType("block"); + break; case Node::Kind::DifferentiableFunctionType: Printer << "@differentiable "; break; case Node::Kind::EscapingDifferentiableFunctionType: @@ -785,6 +802,10 @@ class NodePrinter { unsigned startIndex = 0; bool isAsync = false, isThrows = false; + if (node->getChild(startIndex)->getKind() == Node::Kind::ClangType) { + // handled earlier + ++startIndex; + } if (node->getChild(startIndex)->getKind() == Node::Kind::ThrowsAnnotation) { ++startIndex; isThrows = true; @@ -1301,6 +1322,9 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::EscapingLinearFunctionType: printFunctionType(nullptr, Node); return nullptr; + case Node::Kind::ClangType: + Printer << Node->getText(); + return nullptr; case Node::Kind::ArgumentTuple: printFunctionParameters(nullptr, Node, Options.ShowFunctionArgumentTypes); return nullptr; @@ -2096,6 +2120,24 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::ImplFunctionAttribute: Printer << Node->getText(); return nullptr; + case Node::Kind::ImplFunctionConvention: + Printer << "@convention("; + switch (Node->getNumChildren()) { + case 1: + Printer << Node->getChild(0)->getText(); + break; + case 2: + Printer << Node->getChild(0)->getText() << ", mangledCType: \""; + print(Node->getChild(1)); + Printer << '"'; + break; + default: + assert(false && "Unexpected numChildren for ImplFunctionConvention"); + } + Printer << ')'; + return nullptr; + case Node::Kind::ImplFunctionConventionName: + assert(false && "Already handled in ImplFunctionConvention"); case Node::Kind::ImplErrorResult: Printer << "@error "; printChildren(Node, " "); diff --git a/lib/Demangling/OldDemangler.cpp b/lib/Demangling/OldDemangler.cpp index 575750012f1c7..f7ed9400adf42 100644 --- a/lib/Demangling/OldDemangler.cpp +++ b/lib/Demangling/OldDemangler.cpp @@ -2135,15 +2135,15 @@ class OldDemangler { if (Mangled.nextIf('C')) { if (Mangled.nextIf('b')) - addImplFunctionAttribute(type, "@convention(block)"); + addImplFunctionConvention(type, "block"); else if (Mangled.nextIf('c')) - addImplFunctionAttribute(type, "@convention(c)"); + addImplFunctionConvention(type, "c"); else if (Mangled.nextIf('m')) - addImplFunctionAttribute(type, "@convention(method)"); + addImplFunctionConvention(type, "method"); else if (Mangled.nextIf('O')) - addImplFunctionAttribute(type, "@convention(objc_method)"); + addImplFunctionConvention(type, "objc_method"); else if (Mangled.nextIf('w')) - addImplFunctionAttribute(type, "@convention(witness_method)"); + addImplFunctionConvention(type, "witness_method"); else return nullptr; } @@ -2234,6 +2234,14 @@ class OldDemangler { parent->addChild(Factory.createNode(kind, attr), Factory); } + void addImplFunctionConvention(NodePointer parent, StringRef attr) { + auto attrNode = Factory.createNode(Node::Kind::ImplFunctionConvention); + attrNode->addChild( + Factory.createNode(Node::Kind::ImplFunctionConventionName, attr), + Factory); + parent->addChild(attrNode, Factory); + } + // impl-parameter ::= impl-convention type bool demangleImplParameters(NodePointer parent) { while (!Mangled.nextIf('_')) { diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index d402cc7bcbb0f..a9176efa0a57b 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -1241,17 +1241,7 @@ void Remangler::mangleImplFunctionType(Node *node) { void Remangler::mangleImplFunctionAttribute(Node *node) { StringRef text = node->getText(); - if (text == "@convention(block)") { - Buffer << "Cb"; - } else if (text == "@convention(c)") { - Buffer << "Cc"; - } else if (text == "@convention(method)") { - Buffer << "Cm"; - } else if (text == "@convention(objc_method)") { - Buffer << "CO"; - } else if (text == "@convention(witness_method)") { - Buffer << "Cw"; - } else if (text == "@yield_once") { + if (text == "@yield_once") { Buffer << "A"; } else if (text == "@yield_many") { Buffer << "G"; @@ -1262,6 +1252,29 @@ void Remangler::mangleImplFunctionAttribute(Node *node) { } } +void Remangler::mangleImplFunctionConvention(Node *node) { + mangle(node->getChild(0)); +} + +void Remangler::mangleImplFunctionConventionName(Node *node) { + StringRef text = node->getText(); + if (text == "block") { + Buffer << "Cb"; + } else if (text == "c") { + Buffer << "Cc"; + } else if (text == "method") { + Buffer << "Cm"; + } else if (text == "objc_method") { + Buffer << "CO"; + } else if (text == "witness_method") { + Buffer << "Cw"; + } else { + unreachable("bad impl-function-convention-name"); + } +} + +void Remangler::mangleClangType(Node *node) { unreachable("unsupported"); } + void Remangler::mangleImplParameter(Node *node) { assert(node->getNumChildren() == 2); mangleChildNodes(node); // impl convention, type diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 0b0fdec171717..1692558a5e2cf 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -774,6 +774,15 @@ void Remangler::mangleBuiltinTypeName(Node *node) { } void Remangler::mangleCFunctionPointer(Node *node) { + if (node->getNumChildren() > 0 && + node->getFirstChild()->getKind() == Node::Kind::ClangType) { + for (size_t Idx = node->getNumChildren() - 1; Idx >= 1; --Idx) { + mangleChildNode(node, Idx); + } + Buffer << "XzC"; + mangleClangType(node->getFirstChild()); + return; + } mangleChildNodesReversed(node); // argument tuple, result type Buffer << "XC"; } @@ -1436,6 +1445,37 @@ void Remangler::mangleImplFunctionAttribute(Node *node) { unreachable("handled inline"); } +void Remangler::mangleImplFunctionConvention(Node *node) { + StringRef text = + (node->getNumChildren() > 0 && node->getFirstChild()->hasText()) + ? node->getFirstChild()->getText() + : ""; + char FuncAttr = llvm::StringSwitch(text) + .Case("block", 'B') + .Case("c", 'C') + .Case("method", 'M') + .Case("objc_method", 'O') + .Case("closure", 'K') + .Case("witness_method", 'W') + .Default(0); + assert(FuncAttr && "invalid impl function convention"); + if ((FuncAttr == 'B' || FuncAttr == 'C') && node->getNumChildren() > 1 && + node->getChild(1)->getKind() == Node::Kind::ClangType) { + Buffer << 'z' << FuncAttr; + mangleClangType(node->getChild(1)); + return; + } + Buffer << FuncAttr; +} + +void Remangler::mangleImplFunctionConventionName(Node *node) { + unreachable("handled inline"); +} + +void Remangler::mangleClangType(Node *node) { + Buffer << node->getText().size() << node->getText(); +} + void Remangler::mangleImplInvocationSubstitutions(Node *node) { unreachable("handled inline"); } @@ -1527,14 +1567,12 @@ void Remangler::mangleImplFunctionType(Node *node) { Buffer << ConvCh; break; } + case Node::Kind::ImplFunctionConvention: { + mangleImplFunctionConvention(Child); + break; + } case Node::Kind::ImplFunctionAttribute: { char FuncAttr = llvm::StringSwitch(Child->getText()) - .Case("@convention(block)", 'B') - .Case("@convention(c)", 'C') - .Case("@convention(method)", 'M') - .Case("@convention(objc_method)", 'O') - .Case("@convention(closure)", 'K') - .Case("@convention(witness_method)", 'W') .Case("@yield_once", 'A') .Case("@yield_many", 'G') .Case("@async", 'H') @@ -1783,6 +1821,15 @@ void Remangler::mangleObjCAttribute(Node *node) { } void Remangler::mangleObjCBlock(Node *node) { + if (node->getNumChildren() > 0 && + node->getFirstChild()->getKind() == Node::Kind::ClangType) { + for (size_t Idx = node->getNumChildren() - 1; Idx >= 1; --Idx) { + mangleChildNode(node, Idx); + } + Buffer << "XzB"; + mangleClangType(node->getFirstChild()); + return; + } mangleChildNodesReversed(node); Buffer << "XB"; } diff --git a/test/Demangle/Inputs/manglings-with-clang-types.txt b/test/Demangle/Inputs/manglings-with-clang-types.txt new file mode 100644 index 0000000000000..2dca7def1cc42 --- /dev/null +++ b/test/Demangle/Inputs/manglings-with-clang-types.txt @@ -0,0 +1,2 @@ +$s3tmp1fyySiyXzC9_ZTSPFmvEF ---> tmp.f(@convention(c, mangledCType: "_ZTSPFmvE") () -> Swift.Int) -> () +$s3tmp1hyyySbXzB24_ZTSU13block_pointerFvaEF ---> tmp.h(@convention(block, mangledCType: "_ZTSU13block_pointerFvaE") (Swift.Bool) -> ()) -> () diff --git a/test/Demangle/clang-function-types.swift b/test/Demangle/clang-function-types.swift new file mode 100644 index 0000000000000..df7a4993febcc --- /dev/null +++ b/test/Demangle/clang-function-types.swift @@ -0,0 +1,39 @@ +// NOTE: manglings.txt should be kept in sync with the manglings in this file. + +// Make sure we are testing the right manglings. +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -emit-sil -o - -I %S/../Inputs/custom-modules -use-clang-function-types -module-name tmp -enable-objc-interop | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-os-%target-cpu + +// Check that demangling works. + +// %t.input: "A ---> B" ==> "A" +// RUN: sed -ne '/--->/s/ *--->.*$//p' < %S/Inputs/manglings-with-clang-types.txt > %t.input +// %t.check: "A ---> B" ==> "B" +// RUN: sed -ne '/--->/s/^.*---> *//p' < %S/Inputs/manglings-with-clang-types.txt > %t.check +// RUN: swift-demangle -classify < %t.input > %t.output +// RUN: diff %t.check %t.output + +// Other tests already check mangling for Windows, so we don't need to +// check that here again. + +// UNSUPPORTED: OS=windows-msvc + +import ctypes + +#if os(macOS) && arch(x86_64) +import ObjectiveC + +// BOOL == signed char on x86_64 macOS +public func h(_ k: @convention(block, cType: "void (^)(BOOL)") (Bool) -> ()) { + let _ = k(true) +} +h(A.setGlobal) // OK: check that importing preserves Clang types + +// CHECK-macosx-x86_64: sil @$s3tmp1hyyySbXzB24_ZTSU13block_pointerFvaEF +#endif + +public func f(_ k: @convention(c, cType: "size_t (*)(void)") () -> Int) { + let _ = k() +} +f(ctypes.returns_size_t) // OK: check that importing preserves Clang type + +// CHECK: sil @$s3tmp1fyySiyXzC9_ZTSPFmvEF diff --git a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h index 5898fd4bbb9c4..b0a9e03a7f136 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h @@ -232,6 +232,10 @@ typedef OpaqueTypedefForFP (*FunctionPointerReturningOpaqueTypedef)(void); typedef struct ForwardInTypedefForFP2 *OpaqueTypedefForFP2; typedef OpaqueTypedefForFP2 (*FunctionPointerReturningOpaqueTypedef2)(void); +// Functions that get Swift types which cannot be used to re-derive the +// Clang type. +size_t returns_size_t(); + // This will probably never be serializable. typedef struct { int x; int y; } *(*UnserializableFunctionPointer)(void); diff --git a/test/Inputs/clang-importer-sdk/usr/include/objc/NSObject.h b/test/Inputs/clang-importer-sdk/usr/include/objc/NSObject.h index a86f7b8851669..e80887d8165f0 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/objc/NSObject.h +++ b/test/Inputs/clang-importer-sdk/usr/include/objc/NSObject.h @@ -136,6 +136,7 @@ @interface A(BoolStuff) - setEnabled:(BOOL)enabled; ++ (void)setGlobal:(BOOL)global; @end @interface AlmostSubscriptable diff --git a/test/Serialization/clang-function-types.swift b/test/Serialization/clang-function-types.swift index 195343c08b1a8..61d0ff0a628be 100644 --- a/test/Serialization/clang-function-types.swift +++ b/test/Serialization/clang-function-types.swift @@ -10,8 +10,7 @@ import def_clang_function_types // CHECK-LABEL: sil hidden @$s4main5test1yyF func test1() { - // FIXME: this mangling will have to change - // CHECK: global_addr @$s24def_clang_function_types11has_fp_types13OpaquePointerVSgyXCSgvp : $*Optional<@convention(c, cType: "struct ForwardInTypedefForFP *(*)(void)") () -> Optional> + // CHECK: global_addr @$s24def_clang_function_types11has_fp_types13OpaquePointerVSgyXzC32_ZTSPFP21ForwardInTypedefForFPvESgvp : $*Optional<@convention(c, cType: "struct ForwardInTypedefForFP *(*)(void)") () -> Optional> let fp = has_fp_type _ = fp?() } From 6cb71c6b457d17b16046d56777fe378b22c8d8bb Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Tue, 6 Oct 2020 15:33:23 -0700 Subject: [PATCH 616/745] [ASTPrinter] Print Clang type only if not derivable from Swift type. --- lib/AST/ASTPrinter.cpp | 32 ++++++------ test/ClangImporter/clang-function-types.swift | 6 +-- test/ClangImporter/ctypes_parse.swift | 2 +- .../clang-importer-sdk/usr/include/ctypes.h | 6 +-- test/ModuleInterface/full-convention.swift | 50 +++++++++++++------ test/SIL/clang-function-type-windows.swift | 11 ++++ .../SIL/clang-function-types-nonwindows.swift | 11 ++++ test/SIL/clang-function-types.swift | 8 --- 8 files changed, 82 insertions(+), 44 deletions(-) create mode 100644 test/SIL/clang-function-type-windows.swift create mode 100644 test/SIL/clang-function-types-nonwindows.swift delete mode 100644 test/SIL/clang-function-types.swift diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index e07febf037f29..2425dc6eaf324 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -4037,7 +4037,9 @@ class TypePrinter : public TypeVisitor { visit(staticSelfT); } - void printFunctionExtInfo(ASTContext &Ctx, AnyFunctionType::ExtInfo info) { + void printFunctionExtInfo(AnyFunctionType *fnType) { + auto &ctx = fnType->getASTContext(); + auto info = fnType->getExtInfo(); if (Options.SkipAttributes) return; @@ -4074,13 +4076,13 @@ class TypePrinter : public TypeVisitor { break; case SILFunctionType::Representation::Block: Printer << "block"; - if (printClangType && !info.getClangTypeInfo().empty()) - printCType(Ctx, Printer, info); + if (printClangType && fnType->hasNonDerivableClangType()) + printCType(ctx, Printer, info); break; case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; - if (printClangType && !info.getClangTypeInfo().empty()) - printCType(Ctx, Printer, info); + if (printClangType && fnType->hasNonDerivableClangType()) + printCType(ctx, Printer, info); break; case SILFunctionType::Representation::Method: Printer << "method"; @@ -4101,9 +4103,12 @@ class TypePrinter : public TypeVisitor { } } - void printFunctionExtInfo(ASTContext &Ctx, - SILFunctionType::ExtInfo info, - ProtocolConformanceRef witnessMethodConformance) { + void printFunctionExtInfo(SILFunctionType *fnType) { + auto &Ctx = fnType->getASTContext(); + auto info = fnType->getExtInfo(); + auto witnessMethodConformance = + fnType->getWitnessMethodConformanceOrInvalid(); + if (Options.SkipAttributes) return; @@ -4140,12 +4145,12 @@ class TypePrinter : public TypeVisitor { break; case SILFunctionType::Representation::Block: Printer << "block"; - if (printClangType) + if (printClangType && fnType->hasNonDerivableClangType()) printCType(Ctx, Printer, info); break; case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; - if (printClangType) + if (printClangType && fnType->hasNonDerivableClangType()) printCType(Ctx, Printer, info); break; case SILFunctionType::Representation::Method: @@ -4220,7 +4225,7 @@ class TypePrinter : public TypeVisitor { Printer.printStructurePost(PrintStructureKind::FunctionType); }; - printFunctionExtInfo(T->getASTContext(), T->getExtInfo()); + printFunctionExtInfo(T); // If we're stripping argument labels from types, do it when printing. visitAnyFunctionTypeParams(T->getParams(), /*printLabels*/false); @@ -4260,7 +4265,7 @@ class TypePrinter : public TypeVisitor { Printer.printStructurePost(PrintStructureKind::FunctionType); }; - printFunctionExtInfo(T->getASTContext(), T->getExtInfo()); + printFunctionExtInfo(T); printGenericSignature(T->getGenericSignature(), PrintAST::PrintParams | PrintAST::PrintRequirements); @@ -4322,8 +4327,7 @@ class TypePrinter : public TypeVisitor { void visitSILFunctionType(SILFunctionType *T) { printSILCoroutineKind(T->getCoroutineKind()); - printFunctionExtInfo(T->getASTContext(), T->getExtInfo(), - T->getWitnessMethodConformanceOrInvalid()); + printFunctionExtInfo(T); printCalleeConvention(T->getCalleeConvention()); if (auto sig = T->getInvocationGenericSignature()) { diff --git a/test/ClangImporter/clang-function-types.swift b/test/ClangImporter/clang-function-types.swift index 52d7dae1dd75a..0afd8221d5709 100644 --- a/test/ClangImporter/clang-function-types.swift +++ b/test/ClangImporter/clang-function-types.swift @@ -2,11 +2,11 @@ import ctypes -// CHECK: f1: (@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)? +// CHECK: f1: (@convention(c, cType: "size_t (*)(size_t)") (Swift.Int) -> Swift.Int)? public let f1 = getFunctionPointer_() -// CHECK: f2: (@convention(c, cType: "int (*(*)(int (*)(int)))(int)") ((@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)?) -> (@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)?)? +// CHECK: f2: (@convention(c) ((@convention(c, cType: "size_t (*)(size_t)") (Swift.Int) -> Swift.Int)?) -> (@convention(c, cType: "size_t (*)(size_t)") (Swift.Int) -> Swift.Int)?)? public let f2 = getHigherOrderFunctionPointer() -// CHECK: f3: () -> (@convention(c, cType: "Dummy *(*)(Dummy *)") (Swift.UnsafeMutablePointer?) -> Swift.UnsafeMutablePointer?)? +// CHECK: f3: () -> (@convention(c) (Swift.UnsafeMutablePointer?) -> Swift.UnsafeMutablePointer?)? public let f3 = getFunctionPointer3 diff --git a/test/ClangImporter/ctypes_parse.swift b/test/ClangImporter/ctypes_parse.swift index 9cf79bf690379..fd6aa6ea11776 100644 --- a/test/ClangImporter/ctypes_parse.swift +++ b/test/ClangImporter/ctypes_parse.swift @@ -205,7 +205,7 @@ func testFunctionPointers() { useFunctionPointer(wrapper.a) _ = wrapper.b as (@convention(c) (CInt) -> CInt) - var anotherFP: @convention(c) (CInt, CLong, UnsafeMutableRawPointer?) -> Void + var anotherFP: @convention(c) (Int, CLong, UnsafeMutableRawPointer?) -> Void = getFunctionPointer2() var sizedFP: (@convention(c) (CInt, CInt, UnsafeMutableRawPointer?) -> Void)? diff --git a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h index b0a9e03a7f136..dfe17a3dfdca8 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h @@ -205,18 +205,18 @@ typedef int (*fptr)(int); fptr getFunctionPointer(void); void useFunctionPointer(fptr); -int (*getFunctionPointer_(void))(int); +size_t (*getFunctionPointer_(void))(size_t); struct FunctionPointerWrapper { fptr a; fptr b; }; -typedef void (*fptr2)(int, long, void *); +typedef void (*fptr2)(size_t, long, void *); fptr2 getFunctionPointer2(void); void useFunctionPointer2(fptr2); -int (*(*getHigherOrderFunctionPointer(void))(int (*)(int)))(int); +size_t (*(*getHigherOrderFunctionPointer(void))(size_t (*)(size_t)))(size_t); typedef struct Dummy { int x; diff --git a/test/ModuleInterface/full-convention.swift b/test/ModuleInterface/full-convention.swift index 950ad95402ba6..d4d51e2a07eb1 100644 --- a/test/ModuleInterface/full-convention.swift +++ b/test/ModuleInterface/full-convention.swift @@ -1,32 +1,52 @@ -// RUN: %target-swift-frontend -typecheck -swift-version 5 -emit-module-interface-path - -enable-library-evolution %s -experimental-print-full-convention | %FileCheck %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -swift-version 5 -emit-module-interface-path - -enable-library-evolution %s -experimental-print-full-convention | %FileCheck %s + +import ctypes public func f( - // CHECK: g: @convention(c, cType: "void (*)(void)") + // CHECK: g: @convention(c) g: @convention(c) () -> (), - // CHECK: h0: @convention(c, cType: "int (*)(long long)") + // CHECK: h0: @convention(c) h0: @convention(c) (Int64) -> Int32, - // CHECK: h1: @convention(c, cType: "int (*)(long long)") + // CHECK: h1: @convention(c) h1: @convention(c, cType: "int (*)(long long)") (Int64) -> Int32, - // CHECK: i0: @convention(c, cType: "int *(*)(long long, int)") + // CHECK: h1c: @convention(c, cType: "intptr_t (*)(size_t)") + h1c: @convention(c, cType: "intptr_t (*)(size_t)") (Int) -> Int, + + // CHECK: i0: @convention(c) i0: @convention(c) (Int64, Int32) -> Optional>, - // CHECK: i1: @convention(c, cType: "int *(*)(long long, int)") + // CHECK: i1: @convention(c) i1: @convention(c, cType: "int *(*)(long long, int)") (Int64, Int32) -> Optional>, - // CHECK: p0: @convention(c, cType: "void (*)(void (*)(int))") - // CHECK: @convention(c, cType: "void (*)(int)") + // CHECK: i1c: @convention(c, cType: "size_t *(*)(intptr_t, ptrdiff_t)") + i1c: @convention(c, cType: "size_t *(*)(intptr_t, ptrdiff_t)") (Int, Int) -> Optional>, + + // CHECK: p0: @convention(c) + // CHECK: @convention(c) p0: @convention(c) (@convention(c) (Int32) -> Void) -> Void, - // CHECK: p1: @convention(c, cType: "void (*)(void (*)(int))") - // CHECK: @convention(c, cType: "void (*)(int)") + // CHECK: p1: @convention(c) + // CHECK: @convention(c) p1: @convention(c, cType: "void (*)(void (*)(int))") (@convention(c) (Int32) -> Void) -> Void, - // CHECK: p2: @convention(c, cType: "void (*)(void (*)(int))") - // CHECK: @convention(c, cType: "void (*)(int)") + // CHECK: p1c: @convention(c, cType: "void (*)(void (*)(size_t))") + // CHECK: @convention(c) + p1c: @convention(c, cType: "void (*)(void (*)(size_t))") (@convention(c) (Int) -> Void) -> Void, + + // CHECK: p2: @convention(c) + // CHECK: @convention(c) p2: @convention(c) (@convention(c, cType: "void (*)(int)") (Int32) -> Void) -> Void, - // CHECK: p3: @convention(c, cType: "void (*)(void (*)(int))") - // CHECK: @convention(c, cType: "void (*)(int)") - p3: @convention(c, cType: "void (*)(void (*)(int))") (@convention(c, cType: "void (*)(int)") (Int32) -> Void) -> Void + // CHECK: p2c: @convention(c) + // CHECK: @convention(c, cType: "void (*)(size_t)") + p2c: @convention(c) (@convention(c, cType: "void (*)(size_t)") (Int) -> Void) -> Void, + + // CHECK: p3: @convention(c) + // CHECK: @convention(c) + p3: @convention(c, cType: "void (*)(void (*)(int))") (@convention(c, cType: "void (*)(int)") (Int32) -> Void) -> Void, + + // CHECK: p3c: @convention(c) + // CHECK: @convention(c, cType: "void (*)(size_t)") + p3c: @convention(c, cType: "void (*)(void (*)(size_t))") (@convention(c, cType: "void (*)(size_t)") (Int) -> Void) -> Void ) {} diff --git a/test/SIL/clang-function-type-windows.swift b/test/SIL/clang-function-type-windows.swift new file mode 100644 index 0000000000000..6fcf84b196a44 --- /dev/null +++ b/test/SIL/clang-function-type-windows.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -emit-sil -swift-version 5 -use-clang-function-types -experimental-print-full-convention -o - | %FileCheck %s + +// REQUIRES: OS=windows-msvc + +import ctypes + +public func f(g: @convention(c, cType: "void (*)(size_t)") (Int) -> ()) { g(0) } + +// CHECK: sil @$s4main1f1gyySiXzC9_ZTSPFvyE_tF : $@convention(thin) (@convention(c, cType: "void (*)(unsigned long long)") @noescape (Int) -> ()) -> () { +// CHECK: bb0(%0 : $@convention(c, cType: "void (*)(unsigned long long)") @noescape (Int) -> ()): +// CHECK: debug_value %0 : $@convention(c, cType: "void (*)(unsigned long long)") @noescape (Int) -> (), let, name "g", argno 1 // id: %1 diff --git a/test/SIL/clang-function-types-nonwindows.swift b/test/SIL/clang-function-types-nonwindows.swift new file mode 100644 index 0000000000000..013e0f4c86d05 --- /dev/null +++ b/test/SIL/clang-function-types-nonwindows.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -emit-sil -swift-version 5 -use-clang-function-types -experimental-print-full-convention -o - | %FileCheck %s + +// UNSUPPORTED: OS=windows-msvc + +import ctypes + +public func f(g: @convention(c, cType: "void (*)(size_t)") (Int) -> ()) { g(0) } + +// CHECK: sil @$s4main1f1gyySiXzC9_ZTSPFvmE_tF : $@convention(thin) (@convention(c, cType: "void (*)(unsigned long)") @noescape (Int) -> ()) -> () { +// CHECK: bb0(%0 : $@convention(c, cType: "void (*)(unsigned long)") @noescape (Int) -> ()): +// CHECK: debug_value %0 : $@convention(c, cType: "void (*)(unsigned long)") @noescape (Int) -> (), let, name "g", argno 1 // id: %1 diff --git a/test/SIL/clang-function-types.swift b/test/SIL/clang-function-types.swift deleted file mode 100644 index 6ed5268a9268a..0000000000000 --- a/test/SIL/clang-function-types.swift +++ /dev/null @@ -1,8 +0,0 @@ -// RUN: %target-swift-frontend %s -emit-sil -swift-version 5 -use-clang-function-types -experimental-print-full-convention -o - | %FileCheck %s - -public func f(g: @convention(c) () -> ()) { g() } - -// CHECK: sil @$s4main1f1gyyyXC_tF : $@convention(thin) (@convention(c, cType: "void (*)(void)") @noescape () -> ()) -> () { -// CHECK: bb0(%0 : $@convention(c, cType: "void (*)(void)") @noescape () -> ()): -// CHECK: debug_value %0 : $@convention(c, cType: "void (*)(void)") @noescape () -> (), let, name "g", argno 1 // id: %1 -// CHECK: %2 = apply %0() : $@convention(c, cType: "void (*)(void)") @noescape () -> () From 97202faee50f20d1e86be9205b87ca8b801cd342 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 14 Oct 2020 13:58:03 -0700 Subject: [PATCH 617/745] [Async CC] Put direct returns after indirect returns. For callers who do not know the actual type of the called function, e.g. when the called function is the result of a partial apply, the offset to the direct returns would otherwise not be known. --- lib/IRGen/GenCall.cpp | 22 +++++++++++----------- lib/IRGen/GenCall.h | 17 +++++++++++------ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index fdfdd3d945b6f..d2aca23a162de 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -121,6 +121,17 @@ AsyncContextLayout irgen::getAsyncContextLayout( indirectReturnInfos.push_back(indirectResult); } + // ResultTypes directResults...; + auto directResults = fnConv.getDirectSILResults(); + for (auto result : directResults) { + auto ty = + fnConv.getSILType(result, IGF.IGM.getMaximalTypeExpansionContext()); + auto &ti = IGF.getTypeInfoForLowered(ty.getASTType()); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + directReturnInfos.push_back(result); + } + // SelfType self?; bool hasLocalContextParameter = hasSelfContextParameter(substitutedType); bool canHaveValidError = substitutedType->hasErrorResult(); @@ -203,17 +214,6 @@ AsyncContextLayout irgen::getAsyncContextLayout( trailingWitnessInfo = AsyncContextLayout::TrailingWitnessInfo(); } - // ResultTypes directResults...; - auto directResults = fnConv.getDirectSILResults(); - for (auto result : directResults) { - auto ty = - fnConv.getSILType(result, IGF.IGM.getMaximalTypeExpansionContext()); - auto &ti = IGF.getTypeInfoForLowered(ty.getASTType()); - valTypes.push_back(ty); - typeInfos.push_back(&ti); - directReturnInfos.push_back(result); - } - return AsyncContextLayout( IGF.IGM, LayoutStrategy::Optimal, valTypes, typeInfos, IGF, originalType, substitutedType, substitutionMap, std::move(bindings), diff --git a/lib/IRGen/GenCall.h b/lib/IRGen/GenCall.h index 893e5f5eeaebe..3e847c4122d8c 100644 --- a/lib/IRGen/GenCall.h +++ b/lib/IRGen/GenCall.h @@ -115,13 +115,21 @@ namespace irgen { unsigned getFirstIndirectReturnIndex() { return getErrorIndex() + getErrorCount(); } + unsigned getIndexAfterIndirectReturns() { + return getFirstIndirectReturnIndex() + getIndirectReturnCount(); + } + unsigned getFirstDirectReturnIndex() { + return getIndexAfterIndirectReturns(); + } + unsigned getIndexAfterDirectReturns() { + return getFirstDirectReturnIndex() + getDirectReturnCount(); + } unsigned getLocalContextIndex() { assert(hasLocalContext()); - return getFirstIndirectReturnIndex() + getIndirectReturnCount(); + return getIndexAfterDirectReturns(); } unsigned getIndexAfterLocalContext() { - return getFirstIndirectReturnIndex() + getIndirectReturnCount() + - (hasLocalContext() ? 1 : 0); + return getIndexAfterDirectReturns() + (hasLocalContext() ? 1 : 0); } unsigned getBindingsIndex() { assert(hasBindings()); @@ -145,9 +153,6 @@ namespace irgen { unsigned getIndexAfterTrailingWitnesses() { return getIndexAfterArguments() + (hasTrailingWitnesses() ? 2 : 0); } - unsigned getFirstDirectReturnIndex() { - return getIndexAfterTrailingWitnesses(); - } public: bool canHaveError() { return canHaveValidError; } From a9aee1b9ddbba56a27de3a604cecefb17ed042a5 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 15 Oct 2020 17:58:43 -0700 Subject: [PATCH 618/745] [Async CC] Put bindings after formal arguments. Bindings will always be supplied by the first partial apply, so they will only be added to the async context when its full layout is known. If they are earlier in the layout, subsequent partial applies will put their arguments into the wrong position because they will not be privy to the space requirements of the bindings. --- lib/IRGen/GenCall.cpp | 18 +++++++++--------- lib/IRGen/GenCall.h | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index d2aca23a162de..7b4bacf67fa70 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -168,15 +168,6 @@ AsyncContextLayout irgen::getAsyncContextLayout( } // ArgTypes formalArguments...; - auto bindings = NecessaryBindings::forAsyncFunctionInvocation( - IGF.IGM, originalType, substitutionMap); - if (!bindings.empty()) { - auto bindingsSize = bindings.getBufferSize(IGF.IGM); - auto &bindingsTI = IGF.IGM.getOpaqueStorageTypeInfo( - bindingsSize, IGF.IGM.getPointerAlignment()); - valTypes.push_back(SILType()); - typeInfos.push_back(&bindingsTI); - } for (auto parameter : parameters) { SILType ty = IGF.IGM.silConv.getSILType( parameter, substitutedType, IGF.IGM.getMaximalTypeExpansionContext()); @@ -191,6 +182,15 @@ AsyncContextLayout irgen::getAsyncContextLayout( typeInfos.push_back(&ti); paramInfos.push_back({ty, parameter.getConvention()}); } + auto bindings = NecessaryBindings::forAsyncFunctionInvocation( + IGF.IGM, originalType, substitutionMap); + if (!bindings.empty()) { + auto bindingsSize = bindings.getBufferSize(IGF.IGM); + auto &bindingsTI = IGF.IGM.getOpaqueStorageTypeInfo( + bindingsSize, IGF.IGM.getPointerAlignment()); + valTypes.push_back(SILType()); + typeInfos.push_back(&bindingsTI); + } Optional trailingWitnessInfo; if (originalType->getRepresentation() == diff --git a/lib/IRGen/GenCall.h b/lib/IRGen/GenCall.h index 3e847c4122d8c..c195bb3bbd10b 100644 --- a/lib/IRGen/GenCall.h +++ b/lib/IRGen/GenCall.h @@ -131,27 +131,27 @@ namespace irgen { unsigned getIndexAfterLocalContext() { return getIndexAfterDirectReturns() + (hasLocalContext() ? 1 : 0); } + unsigned getFirstArgumentIndex() { return getIndexAfterLocalContext(); } + unsigned getIndexAfterArguments() { + return getFirstArgumentIndex() + getArgumentCount(); + } unsigned getBindingsIndex() { assert(hasBindings()); - return getIndexAfterLocalContext(); + return getIndexAfterArguments(); } unsigned getIndexAfterBindings() { - return getIndexAfterLocalContext() + (hasBindings() ? 1 : 0); - } - unsigned getFirstArgumentIndex() { return getIndexAfterBindings(); } - unsigned getIndexAfterArguments() { - return getFirstArgumentIndex() + getArgumentCount(); + return getIndexAfterArguments() + (hasBindings() ? 1 : 0); } unsigned getSelfMetadataIndex() { assert(hasTrailingWitnesses()); - return getIndexAfterArguments(); + return getIndexAfterBindings(); } unsigned getSelfWitnessTableIndex() { assert(hasTrailingWitnesses()); - return getIndexAfterArguments() + 1; + return getIndexAfterBindings() + 1; } unsigned getIndexAfterTrailingWitnesses() { - return getIndexAfterArguments() + (hasTrailingWitnesses() ? 2 : 0); + return getIndexAfterBindings() + (hasTrailingWitnesses() ? 2 : 0); } public: From 409f3d253b4f09de15722b4f08d65e7b60f9b461 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Mon, 19 Oct 2020 12:26:59 -0700 Subject: [PATCH 619/745] [NFC] Removed Explosion::peek. The convenience was added to aid with the async calling convention but it has proven to be unnecessary. Here, it is removed. --- lib/IRGen/Explosion.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/IRGen/Explosion.h b/lib/IRGen/Explosion.h index e77bd9749084e..db24fa97a8cb2 100644 --- a/lib/IRGen/Explosion.h +++ b/lib/IRGen/Explosion.h @@ -152,11 +152,6 @@ class Explosion { return Values[NextValue-1]; } - llvm::Value *peek(unsigned n) { - assert(n < Values.size()); - return Values[n]; - } - /// Claim and remove the last item in the array. /// Unlike the normal 'claim' methods, the item is gone forever. llvm::Value *takeLast() { From 0356c9f4c009daea16595a39690be335c70f2367 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Mon, 19 Oct 2020 17:34:44 -0700 Subject: [PATCH 620/745] [NFC] Simplify saving of self to async context. --- lib/IRGen/GenCall.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 7b4bacf67fa70..f342bd4926e94 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -2004,9 +2004,6 @@ class AsyncCallEmission final : public CallEmission { IGF.getSILModule()); // Move all the arguments into the context. - if (selfValue) { - llArgs.add(selfValue); - } auto layout = getAsyncContextLayout(); for (unsigned index = 0, count = layout.getIndirectReturnCount(); index < count; ++index) { @@ -2024,8 +2021,10 @@ class AsyncCallEmission final : public CallEmission { layout.getBindings().save(IGF, bindingsAddr, llArgs); } if (selfValue) { + Explosion selfExplosion; + selfExplosion.add(selfValue); auto fieldLayout = layout.getLocalContextLayout(); - saveValue(fieldLayout, llArgs, isOutlined); + saveValue(fieldLayout, selfExplosion, isOutlined); } } void emitCallToUnmappedExplosion(llvm::CallInst *call, Explosion &out) override { From 63b5ba457e5a36e890fd4846fc9ec0f2b638259c Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 20 Oct 2020 17:19:42 -0700 Subject: [PATCH 621/745] [NFC] Used consistent style for casting. --- lib/IRGen/GenCall.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index f342bd4926e94..793b4ac4d6c42 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -1959,8 +1959,8 @@ class AsyncCallEmission final : public CallEmission { } void loadValue(ElementLayout layout, Explosion &explosion) { Address addr = layout.project(IGF, context, /*offsets*/ llvm::None); - auto &ti = layout.getType(); - cast(ti).loadAsTake(IGF, addr, explosion); + auto &ti = cast(layout.getType()); + ti.loadAsTake(IGF, addr, explosion); } public: From 03f4e40f20c512fad34cae93a89144090bda1ee5 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 20 Oct 2020 10:29:53 -0700 Subject: [PATCH 622/745] [Async CC] Move self after formal arguments. Partial applies of methods supply only self. When applies of the resulting thick function are performed, that self was one of the arguments is not known. As a result, self must appear after the fields that might be supplied at the apply site, which is to say all the arguments. --- lib/IRGen/GenCall.cpp | 48 ++++++++++++++++++++++--------------------- lib/IRGen/GenCall.h | 25 ++++++++++++---------- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 793b4ac4d6c42..66ad478939cef 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -143,29 +143,6 @@ AsyncContextLayout irgen::getAsyncContextLayout( if (hasLocalContextParameter) { parameters = parameters.drop_back(); } - Optional localContextInfo = llvm::None; - if (hasLocalContext) { - if (hasLocalContextParameter) { - SILType ty = - IGF.IGM.silConv.getSILType(localContextParameter, substitutedType, - IGF.IGM.getMaximalTypeExpansionContext()); - auto argumentLoweringType = - getArgumentLoweringType(ty.getASTType(), localContextParameter, - /*isNoEscape*/ true); - - auto &ti = IGF.getTypeInfoForLowered(argumentLoweringType); - valTypes.push_back(ty); - typeInfos.push_back(&ti); - localContextInfo = {ty, localContextParameter.getConvention()}; - } else { - // TODO: DETERMINE: Is there a field in this case to match the sync ABI? - auto &ti = IGF.IGM.getNativeObjectTypeInfo(); - SILType ty = SILType::getNativeObjectType(IGF.IGM.Context); - valTypes.push_back(ty); - typeInfos.push_back(&ti); - localContextInfo = {ty, substitutedType->getCalleeConvention()}; - } - } // ArgTypes formalArguments...; for (auto parameter : parameters) { @@ -192,6 +169,31 @@ AsyncContextLayout irgen::getAsyncContextLayout( typeInfos.push_back(&bindingsTI); } + Optional localContextInfo = llvm::None; + if (hasLocalContext) { + if (hasLocalContextParameter) { + SILType ty = + IGF.IGM.silConv.getSILType(localContextParameter, substitutedType, + IGF.IGM.getMaximalTypeExpansionContext()); + auto argumentLoweringType = + getArgumentLoweringType(ty.getASTType(), localContextParameter, + /*isNoEscape*/ true); + + auto &ti = IGF.getTypeInfoForLowered(argumentLoweringType); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + localContextInfo = {ty, localContextParameter.getConvention()}; + } else { + // TODO: DETERMINE: Is there a field in this case to match the sync ABI? + auto &ti = IGF.IGM.getNativeObjectTypeInfo(); + SILType ty = SILType::getNativeObjectType(IGF.IGM.Context); + valTypes.push_back(ty); + typeInfos.push_back(&ti); + localContextInfo = {ty, substitutedType->getCalleeConvention()}; + } + } + + Optional trailingWitnessInfo; if (originalType->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod) { diff --git a/lib/IRGen/GenCall.h b/lib/IRGen/GenCall.h index c195bb3bbd10b..ce1a692c31285 100644 --- a/lib/IRGen/GenCall.h +++ b/lib/IRGen/GenCall.h @@ -83,6 +83,8 @@ namespace irgen { // }; // ResultTypes directResults...; // }; + // ArgTypes formalArguments...; + // SelfType self?; // }; struct AsyncContextLayout : StructLayout { struct ArgumentInfo { @@ -124,14 +126,7 @@ namespace irgen { unsigned getIndexAfterDirectReturns() { return getFirstDirectReturnIndex() + getDirectReturnCount(); } - unsigned getLocalContextIndex() { - assert(hasLocalContext()); - return getIndexAfterDirectReturns(); - } - unsigned getIndexAfterLocalContext() { - return getIndexAfterDirectReturns() + (hasLocalContext() ? 1 : 0); - } - unsigned getFirstArgumentIndex() { return getIndexAfterLocalContext(); } + unsigned getFirstArgumentIndex() { return getIndexAfterDirectReturns(); } unsigned getIndexAfterArguments() { return getFirstArgumentIndex() + getArgumentCount(); } @@ -142,16 +137,24 @@ namespace irgen { unsigned getIndexAfterBindings() { return getIndexAfterArguments() + (hasBindings() ? 1 : 0); } + unsigned getLocalContextIndex() { + assert(hasLocalContext()); + return getIndexAfterBindings(); + } + unsigned getIndexAfterLocalContext() { + return getIndexAfterBindings() + + (hasLocalContext() ? 1 : 0); + } unsigned getSelfMetadataIndex() { assert(hasTrailingWitnesses()); - return getIndexAfterBindings(); + return getIndexAfterLocalContext(); } unsigned getSelfWitnessTableIndex() { assert(hasTrailingWitnesses()); - return getIndexAfterBindings() + 1; + return getIndexAfterLocalContext() + 1; } unsigned getIndexAfterTrailingWitnesses() { - return getIndexAfterBindings() + (hasTrailingWitnesses() ? 2 : 0); + return getIndexAfterLocalContext() + (hasTrailingWitnesses() ? 2 : 0); } public: From a9f497d20cdd1fee7953f762f4cd2fe032262026 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Wed, 21 Oct 2020 13:52:37 -0700 Subject: [PATCH 623/745] [utils/swift_build_sdk_interfaces.py] Remove passing '-track-system-dependencies' when prebuilding modules from the interfaces Including all the system header dependencies and `stat`ing them all the time introduces significant performance overhead for normal compilation, and other features like code-completion, without being worth it in practice. --- .../track-system-dependencies.swift | 4 ++-- utils/swift_build_sdk_interfaces.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/test/ModuleInterface/swift_build_sdk_interfaces/track-system-dependencies.swift b/test/ModuleInterface/swift_build_sdk_interfaces/track-system-dependencies.swift index 7703e3c677bbc..55f0c2b4b96ed 100644 --- a/test/ModuleInterface/swift_build_sdk_interfaces/track-system-dependencies.swift +++ b/test/ModuleInterface/swift_build_sdk_interfaces/track-system-dependencies.swift @@ -17,12 +17,12 @@ // RUN: %target-typecheck-verify-swift -sdk %t/sdk -I %t/sdk/usr/lib/swift/ -module-cache-path %t/MCP -prebuilt-module-cache-path %t/prebuilt // RUN: %{python} %S/../ModuleCache/Inputs/check-is-forwarding-module.py %t/MCP/Swifty-*.swiftmodule -// Actually change a file in the SDK, to check that we're tracking dependencies +// Actually change a file in the SDK, to check that we're not tracking system dependencies // at all. // RUN: rm -rf %t/MCP // RUN: echo "void unrelated();" >> %t/sdk/usr/include/Platform.h // RUN: %target-typecheck-verify-swift -sdk %t/sdk -I %t/sdk/usr/lib/swift/ -module-cache-path %t/MCP -prebuilt-module-cache-path %t/prebuilt -// RUN: not %{python} %S/../ModuleCache/Inputs/check-is-forwarding-module.py %t/MCP/Swifty-*.swiftmodule +// RUN: %{python} %S/../ModuleCache/Inputs/check-is-forwarding-module.py %t/MCP/Swifty-*.swiftmodule // Without the prebuilt cache everything should still work; it'll just take time // because we have to build the interfaces. diff --git a/utils/swift_build_sdk_interfaces.py b/utils/swift_build_sdk_interfaces.py index 7ffa954246cd2..606fb4313f5c0 100755 --- a/utils/swift_build_sdk_interfaces.py +++ b/utils/swift_build_sdk_interfaces.py @@ -253,7 +253,6 @@ def process_module(module_file): '-build-module-from-parseable-interface', '-sdk', args.sdk, '-prebuilt-module-cache-path', args.output_dir, - '-track-system-dependencies' ] module_cache_path = "" if args.module_cache_path: From af5fa5e31dca51e9b3622c628ad59cace301fd53 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 21 Oct 2020 19:07:04 -0700 Subject: [PATCH 624/745] Remove `isPrivateToEnclosingFile` Accessor I removed the implementation in #34151 --- include/swift/AST/Decl.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index c36f461da6c92..b0068598ea507 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -950,9 +950,6 @@ class alignas(1 << DeclAlignInBits) Decl { /// If this returns true, the decl can be safely casted to ValueDecl. bool isPotentiallyOverridable() const; - /// Returns true if this Decl cannot be seen by any other source file - bool isPrivateToEnclosingFile() const; - /// Retrieve the global actor attribute that applies to this declaration, /// if any. /// From c3881d9651c9383dc1046a4696c5b61f020ddd6f Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 21 Oct 2020 19:08:04 -0700 Subject: [PATCH 625/745] [NFC] Pull the Computation of Fingerprints Into the Common Base Class --- .../AST/AbstractSourceFileDepGraphFactory.h | 16 +++++ lib/AST/FrontendSourceFileDepGraphFactory.cpp | 60 ++++--------------- lib/AST/FrontendSourceFileDepGraphFactory.h | 12 ---- 3 files changed, 27 insertions(+), 61 deletions(-) diff --git a/include/swift/AST/AbstractSourceFileDepGraphFactory.h b/include/swift/AST/AbstractSourceFileDepGraphFactory.h index a0991efd8ad93..10e6a6e9f9aa0 100644 --- a/include/swift/AST/AbstractSourceFileDepGraphFactory.h +++ b/include/swift/AST/AbstractSourceFileDepGraphFactory.h @@ -13,6 +13,7 @@ #ifndef SWIFT_AST_SOURCE_FILE_DEP_GRAPH_CONSTRUCTOR_H #define SWIFT_AST_SOURCE_FILE_DEP_GRAPH_CONSTRUCTOR_H +#include "swift/AST/Decl.h" #include "swift/AST/DeclContext.h" #include "swift/AST/FineGrainedDependencies.h" @@ -72,6 +73,21 @@ class AbstractSourceFileDepGraphFactory { Optional fingerprint); void addAUsedDecl(const DependencyKey &def, const DependencyKey &use); + + static Optional getFingerprintIfAny( + std::pair) { + return None; + } + + static Optional getFingerprintIfAny(const Decl *d) { + if (const auto *idc = dyn_cast(d)) { + auto result = idc->getBodyFingerprint(); + assert((!result || !result->empty()) && + "Fingerprint should never be empty"); + return result; + } + return None; + } }; } // namespace fine_grained_dependencies diff --git a/lib/AST/FrontendSourceFileDepGraphFactory.cpp b/lib/AST/FrontendSourceFileDepGraphFactory.cpp index 5ca65c70fb870..049d6c7b57205 100644 --- a/lib/AST/FrontendSourceFileDepGraphFactory.cpp +++ b/lib/AST/FrontendSourceFileDepGraphFactory.cpp @@ -229,6 +229,13 @@ std::string FrontendSourceFileDepGraphFactory::getFingerprint(SourceFile *SF) { return getInterfaceHash(SF); } +std::string +FrontendSourceFileDepGraphFactory::getInterfaceHash(SourceFile *SF) { + llvm::SmallString<32> interfaceHash; + SF->getInterfaceHash(interfaceHash); + return interfaceHash.str().str(); +} + //============================================================================== // MARK: FrontendSourceFileDepGraphFactory - adding collections of defined Decls //============================================================================== @@ -407,7 +414,8 @@ template void FrontendSourceFileDepGraphFactory::addAllDefinedDeclsOfAGivenType( std::vector &contentsVec) { for (const auto &declOrPair : contentsVec) { - Optional fp = getFingerprintIfAny(declOrPair); + Optional fp = + AbstractSourceFileDepGraphFactory::getFingerprintIfAny(declOrPair); addADefinedDecl( DependencyKey::createForProvidedEntityInterface(declOrPair), fp ? StringRef(fp.getValue()) : Optional()); @@ -519,33 +527,6 @@ void FrontendSourceFileDepGraphFactory::addAllUsedDecls() { .enumerateAllUses(); } -//============================================================================== -// MARK: FrontendSourceFileDepGraphFactory - adding individual defined Decls -//============================================================================== - -std::string -FrontendSourceFileDepGraphFactory::getInterfaceHash(SourceFile *SF) { - llvm::SmallString<32> interfaceHash; - SF->getInterfaceHash(interfaceHash); - return interfaceHash.str().str(); -} - -/// At present, only \c NominalTypeDecls have (body) fingerprints -Optional FrontendSourceFileDepGraphFactory::getFingerprintIfAny( - std::pair) { - return None; -} -Optional -FrontendSourceFileDepGraphFactory::getFingerprintIfAny(const Decl *d) { - if (const auto *idc = dyn_cast(d)) { - auto result = idc->getBodyFingerprint(); - assert((!result || !result->empty()) && - "Fingerprint should never be empty"); - return result; - } - return None; -} - //============================================================================== // MARK: ModuleDepGraphFactory //============================================================================== @@ -591,29 +572,10 @@ template void ModuleDepGraphFactory::addAllDefinedDeclsOfAGivenType( std::vector &contentsVec) { for (const auto &declOrPair : contentsVec) { - Optional fp = getFingerprintIfAny(declOrPair); + Optional fp = + AbstractSourceFileDepGraphFactory::getFingerprintIfAny(declOrPair); addADefinedDecl( DependencyKey::createForProvidedEntityInterface(declOrPair), fp ? StringRef(fp.getValue()) : Optional()); } } - -//============================================================================== -// MARK: ModuleDepGraphFactory - adding individual defined Decls -//============================================================================== - -/// At present, only \c NominalTypeDecls have (body) fingerprints -Optional ModuleDepGraphFactory::getFingerprintIfAny( - std::pair) { - return None; -} -Optional -ModuleDepGraphFactory::getFingerprintIfAny(const Decl *d) { - if (const auto *idc = dyn_cast(d)) { - auto result = idc->getBodyFingerprint(); - assert((!result || !result->empty()) && - "Fingerprint should never be empty"); - return result; - } - return None; -} diff --git a/lib/AST/FrontendSourceFileDepGraphFactory.h b/lib/AST/FrontendSourceFileDepGraphFactory.h index 6b2740f54baf2..d11fc431324f1 100644 --- a/lib/AST/FrontendSourceFileDepGraphFactory.h +++ b/lib/AST/FrontendSourceFileDepGraphFactory.h @@ -44,12 +44,6 @@ class FrontendSourceFileDepGraphFactory /// create node pairs for context and name template void addAllDefinedDeclsOfAGivenType(std::vector &contentsVec); - - /// At present, only nominals, protocols, and extensions have (body) - /// fingerprints - static Optional - getFingerprintIfAny(std::pair); - static Optional getFingerprintIfAny(const Decl *d); }; class ModuleDepGraphFactory : public AbstractSourceFileDepGraphFactory { @@ -68,12 +62,6 @@ class ModuleDepGraphFactory : public AbstractSourceFileDepGraphFactory { /// create node pairs for context and name template void addAllDefinedDeclsOfAGivenType(std::vector &contentsVec); - - /// At present, only nominals, protocols, and extensions have (body) - /// fingerprints - static Optional getFingerprintIfAny( - std::pair); - static Optional getFingerprintIfAny(const Decl *d); }; } // namespace fine_grained_dependencies From e2e19dcf822eac442605651a5b7bb71ad26c5ae3 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 21 Oct 2020 19:35:04 -0700 Subject: [PATCH 626/745] Add Compilation "Wave" Assertion in +Asserts Builds The "wave" of a compilation job describes the number of indirections through other compile jobs the driver required to reach the decision to schedule a job. In incremental mode, it should always be the case that it takes no more than two complete waves to arrive at a fixpoint in the build. This is a natural consequence of the structure of the dependencies emitted by the Swift frontend - namely we rely on transitivity in dependency arcs. A quick proof sketch: Suppose an arbitrary perturbation of the inputs to an incremental compilation session are made. In the first wave, dependency edges from the prior build's state (the "zeroeth wave") are loaded and the files corresponding to invalidated edges are scheduled into the first wave. Supposing the second wave is not the null set - the trivial case - there are additional arcs that were invalidated. Now suppose that there were a third wave. Take an arbitrary arc invalidated by this third wave. It must be the case that the file containing the use is not new - else it would be scheduled. Further it must be the case that its def was not invalidated by the zeroeth or first waves of compilation otherwise we would have scheduled it into the first or second waves. Finally, it must have a use that was discovered in the second wave. But in order for that use to have been included in the second wave, there must have been an invalidated arc created by the first wave. By transitivity of dependency arcs, there must therefore be a dependency arc from a definition invalidated in the first wave to our third wave job, which implies that the file would be scheduled into the second wave! [Insert contradiction pig image here] --- include/swift/Driver/Job.h | 22 ++++++++++++++++++++++ lib/Driver/Compilation.cpp | 23 +++++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/include/swift/Driver/Job.h b/include/swift/Driver/Job.h index 52ba51aafa1fe..c14dcbc2a47bc 100644 --- a/include/swift/Driver/Job.h +++ b/include/swift/Driver/Job.h @@ -308,6 +308,23 @@ class Job { /// The modification time of the main input file, if any. llvm::sys::TimePoint<> InputModTime = llvm::sys::TimePoint<>::max(); +#ifndef NDEBUG + /// The "wave" of incremental jobs that this \c Job was scheduled into. + /// + /// The first "wave" of jobs is computed by the driver from the set of inputs + /// and external files that have been mutated by the user. From there, as + /// jobs from the first wave finish executing, we reload their \c swiftdeps + /// files and re-integrate them into the dependency graph to discover + /// the jobs for the second "wave". + /// + /// In +asserts builds, we ensure that no more than two "waves" occur for + /// any given incremental compilation session. This is a consequence of + /// 1) transitivity in dependency arcs + /// 2) dependency tracing from uses that affect a def's interfaces to that + /// def's uses. + mutable unsigned Wave = 1; +#endif + public: Job(const JobAction &Source, SmallVectorImpl &&Inputs, std::unique_ptr Output, const char *Executable, @@ -399,6 +416,11 @@ class Job { /// Assumes that, if a compile job, has one primary swift input /// May return empty if none. StringRef getFirstSwiftPrimaryInput() const; + +#ifndef NDEBUG + unsigned getWave() const { return Wave; } + void setWave(unsigned WaveNum) const { Wave = WaveNum; } +#endif }; /// A BatchJob comprises a _set_ of jobs, each of which is sufficiently similar diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index 6bede8ee5dec6..10c418565888f 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -323,6 +323,17 @@ namespace driver { return; } +#ifndef NDEBUG + // If we can, assert that no compile jobs are scheduled beyond the second + // wave. If this assertion fails, it indicates one of: + // 1) A failure of the driver's job tracing machinery to follow a + // dependency arc. + // 2) A failure of the frontend to emit a dependency arc. + if (isa(Cmd->getSource()) && Cmd->getWave() > 2) { + llvm_unreachable("Scheduled a command into a third wave!"); + } +#endif + // Adding to scheduled means we've committed to its completion (not // distinguished from skipping). We never remove it once inserted. ScheduledCommands.insert(Cmd); @@ -694,8 +705,16 @@ namespace driver { "because of dependencies discovered later"); scheduleCommandsInSortedOrder(DependentsInEffect); - for (const Job *Cmd : DependentsInEffect) - DeferredCommands.erase(Cmd); + for (const Job *Cmd : DependentsInEffect) { + if (DeferredCommands.erase(Cmd)) { +#ifndef NDEBUG + if (isa(FinishedCmd->getSource())) + Cmd->setWave(FinishedCmd->getWave() + 1); +#else + continue; +#endif + } + } return TaskFinishedResponse::ContinueExecution; } From f0f22467931f7410bff76248806b94a9d5e64537 Mon Sep 17 00:00:00 2001 From: Zoe Carver Date: Wed, 21 Oct 2020 20:42:25 -0700 Subject: [PATCH 627/745] [cxx-interop] Support C++ function templates in Swift. (#33053) This patch adds rudimentary support for C++ template functions in swift. --- include/swift/AST/ASTContext.h | 10 +- include/swift/AST/ClangModuleLoader.h | 14 +++ include/swift/AST/Decl.h | 7 +- include/swift/AST/DiagnosticsSema.def | 5 + include/swift/ClangImporter/ClangImporter.h | 5 + lib/AST/ASTContext.cpp | 23 +++- lib/AST/ClangTypeConverter.cpp | 30 ++++++ lib/AST/ClangTypeConverter.h | 10 ++ lib/AST/Decl.cpp | 13 ++- lib/ClangImporter/ClangImporter.cpp | 34 +++++- lib/ClangImporter/ImportDecl.cpp | 101 ++++++++++++------ lib/ClangImporter/ImportType.cpp | 75 +++++++++---- lib/ClangImporter/ImporterImpl.h | 23 ++-- lib/ClangImporter/SwiftLookupTable.h | 1 + lib/Sema/CSApply.cpp | 71 +++++++++++- .../Cxx/templates/Inputs/function-templates.h | 22 ++++ .../Cxx/templates/Inputs/module.modulemap | 4 + .../templates/function-template-errors.swift | 39 +++++++ .../function-template-irgen-objc.swift | 17 +++ .../templates/function-template-irgen.swift | 39 +++++++ .../function-template-module-interface.swift | 8 ++ .../templates/function-template-silgen.swift | 31 ++++++ .../Cxx/templates/function-template.swift | 37 +++++++ 23 files changed, 531 insertions(+), 88 deletions(-) create mode 100644 test/Interop/Cxx/templates/Inputs/function-templates.h create mode 100644 test/Interop/Cxx/templates/function-template-errors.swift create mode 100644 test/Interop/Cxx/templates/function-template-irgen-objc.swift create mode 100644 test/Interop/Cxx/templates/function-template-irgen.swift create mode 100644 test/Interop/Cxx/templates/function-template-module-interface.swift create mode 100644 test/Interop/Cxx/templates/function-template-silgen.swift create mode 100644 test/Interop/Cxx/templates/function-template.swift diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 4d32b65aadf12..c9f3fbe06e187 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -23,11 +23,12 @@ #include "swift/AST/Import.h" #include "swift/AST/SearchPathOptions.h" #include "swift/AST/Type.h" -#include "swift/AST/Types.h" #include "swift/AST/TypeAlignments.h" +#include "swift/AST/Types.h" #include "swift/Basic/LangOptions.h" #include "swift/Basic/Located.h" #include "swift/Basic/Malloc.h" +#include "clang/AST/DeclTemplate.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" @@ -628,6 +629,13 @@ class ASTContext final { ArrayRef params, Optional result, SILFunctionType::Representation trueRep); + /// Instantiates "Impl.Converter" if needed, then calls + /// ClangTypeConverter::getClangTemplateArguments. + std::unique_ptr getClangTemplateArguments( + const clang::TemplateParameterList *templateParams, + ArrayRef genericArgs, + SmallVectorImpl &templateArgs); + /// Get the Swift declaration that a Clang declaration was exported from, /// if applicable. const Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl); diff --git a/include/swift/AST/ClangModuleLoader.h b/include/swift/AST/ClangModuleLoader.h index 06c41a3d8e61e..b02bac076b25a 100644 --- a/include/swift/AST/ClangModuleLoader.h +++ b/include/swift/AST/ClangModuleLoader.h @@ -14,7 +14,9 @@ #define SWIFT_AST_CLANG_MODULE_LOADER_H #include "swift/AST/ModuleLoader.h" +#include "swift/AST/SubstitutionMap.h" #include "swift/Basic/TaggedUnion.h" +#include "clang/AST/DeclTemplate.h" namespace clang { class ASTContext; @@ -219,6 +221,18 @@ class ClangModuleLoader : public ModuleLoader { /// based on subtleties like the target module interface format. virtual bool isSerializable(const clang::Type *type, bool checkCanonical) const = 0; + + virtual clang::FunctionDecl * + instantiateCXXFunctionTemplate(ASTContext &ctx, + clang::FunctionTemplateDecl *func, + SubstitutionMap subst) = 0; +}; + +/// Used to describe a template instantiation error. +struct TemplateInstantiationError { + /// Generic types that could not be converted to QualTypes using the + /// ClangTypeConverter. + SmallVector failedTypes; }; } // namespace swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index de0cc5c196baa..f53fff7dff473 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5960,9 +5960,10 @@ class FuncDecl : public AbstractFunctionDecl { DeclContext *Parent); static FuncDecl *createImported(ASTContext &Context, SourceLoc FuncLoc, - DeclName Name, SourceLoc NameLoc, - bool Async, bool Throws, - ParameterList *BodyParams, Type FnRetType, + DeclName Name, SourceLoc NameLoc, bool Async, + bool Throws, ParameterList *BodyParams, + Type FnRetType, + GenericParamList *GenericParams, DeclContext *Parent, ClangNode ClangN); bool isStatic() const; diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index b20617674c576..a292f6e771793 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1674,6 +1674,11 @@ ERROR(where_nongeneric_ctx,none, ERROR(where_nongeneric_toplevel,none, "'where' clause cannot be applied to a non-generic top-level " "declaration", ()) +ERROR(unable_to_convert_generic_swift_types,none, + "could not generate C++ types from the generic Swift types provided. " + "The following Swift type(s) provided to '%0' were unable to be " + "converted: %1.", + (StringRef, StringRef)) // Type aliases ERROR(type_alias_underlying_type_access,none, diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index 578287637964b..2a30888e5cba3 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -472,6 +472,11 @@ class ClangImporter final : public ClangModuleLoader { bool isSerializable(const clang::Type *type, bool checkCanonical) const override; + + clang::FunctionDecl * + instantiateCXXFunctionTemplate(ASTContext &ctx, + clang::FunctionTemplateDecl *func, + SubstitutionMap subst) override; }; ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN, diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index facc679307835..7cf42afa2d904 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -18,13 +18,13 @@ #include "ClangTypeConverter.h" #include "ForeignRepresentationInfo.h" #include "SubstitutionMapStorage.h" -#include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ConcreteDeclRef.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/FileUnit.h" +#include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" @@ -41,19 +41,19 @@ #include "swift/AST/PropertyWrappers.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/RawComment.h" +#include "swift/AST/SILLayout.h" #include "swift/AST/SemanticAttrs.h" #include "swift/AST/SourceFile.h" #include "swift/AST/SubstitutionMap.h" -#include "swift/AST/SILLayout.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Compiler.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/StringExtras.h" -#include "swift/Syntax/References.h" -#include "swift/Syntax/SyntaxArena.h" #include "swift/Strings.h" #include "swift/Subsystems.h" +#include "swift/Syntax/References.h" +#include "swift/Syntax/SyntaxArena.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringMap.h" @@ -4563,6 +4563,21 @@ ASTContext::getCanonicalClangFunctionType( return ty ? ty->getCanonicalTypeInternal().getTypePtr() : nullptr; } +std::unique_ptr +ASTContext::getClangTemplateArguments( + const clang::TemplateParameterList *templateParams, + ArrayRef genericArgs, + SmallVectorImpl &templateArgs) { + auto &impl = getImpl(); + if (!impl.Converter) { + auto *cml = getClangModuleLoader(); + impl.Converter.emplace(*this, cml->getClangASTContext(), LangOpts.Target); + } + + return impl.Converter->getClangTemplateArguments(templateParams, genericArgs, + templateArgs); +} + const Decl * ASTContext::getSwiftDeclForExportedClangDecl(const clang::Decl *decl) { auto &impl = getImpl(); diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index 365d9b72e3d03..9da86112ebae6 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -807,3 +807,33 @@ Decl *ClangTypeConverter::getSwiftDeclForExportedClangDecl( auto it = ReversedExportMap.find(decl); return (it != ReversedExportMap.end() ? it->second : nullptr); } + +std::unique_ptr +ClangTypeConverter::getClangTemplateArguments( + const clang::TemplateParameterList *templateParams, + ArrayRef genericArgs, + SmallVectorImpl &templateArgs) { + // Keep track of the types we failed to convert so we can return a useful + // error. + SmallVector failedTypes; + for (clang::NamedDecl *param : *templateParams) { + // Note: all template parameters must be template type parameters. This is + // verified when we import the clang decl. + auto templateParam = cast(param); + auto replacement = genericArgs[templateParam->getIndex()]; + auto qualType = convert(replacement); + if (qualType.isNull()) { + failedTypes.push_back(replacement); + // Find all the types we can't convert. + continue; + } + templateArgs.push_back(clang::TemplateArgument(qualType)); + } + if (failedTypes.empty()) + return nullptr; + auto errorInfo = std::make_unique(); + llvm::for_each(failedTypes, [&errorInfo](auto type) { + errorInfo->failedTypes.push_back(type); + }); + return errorInfo; +} diff --git a/lib/AST/ClangTypeConverter.h b/lib/AST/ClangTypeConverter.h index e41a824163312..b590b3ef0c0ec 100644 --- a/lib/AST/ClangTypeConverter.h +++ b/lib/AST/ClangTypeConverter.h @@ -84,8 +84,18 @@ class ClangTypeConverter : /// Swift declaration. Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl) const; + /// Translate Swift generic arguments to template arguments. + /// + /// \returns nullptr if successful. If an error occors, returns a unique_ptr + /// to a `TemplateInstantiationError` with a list of the failed types. + std::unique_ptr getClangTemplateArguments( + const clang::TemplateParameterList *templateParams, + ArrayRef genericArgs, + SmallVectorImpl &templateArgs); + private: clang::QualType convert(Type type); + clang::QualType convertMemberType(NominalTypeDecl *DC, StringRef memberName); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index e487fb81866cc..f67316d4b4ce7 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -7227,16 +7227,15 @@ FuncDecl *FuncDecl::createImplicit(ASTContext &Context, } FuncDecl *FuncDecl::createImported(ASTContext &Context, SourceLoc FuncLoc, - DeclName Name, SourceLoc NameLoc, - bool Async, bool Throws, - ParameterList *BodyParams, - Type FnRetType, DeclContext *Parent, - ClangNode ClangN) { + DeclName Name, SourceLoc NameLoc, bool Async, + bool Throws, ParameterList *BodyParams, + Type FnRetType, + GenericParamList *GenericParams, + DeclContext *Parent, ClangNode ClangN) { assert(ClangN && FnRetType); auto *const FD = FuncDecl::createImpl( Context, SourceLoc(), StaticSpellingKind::None, FuncLoc, Name, NameLoc, - Async, SourceLoc(), Throws, SourceLoc(), - /*GenericParams=*/nullptr, Parent, ClangN); + Async, SourceLoc(), Throws, SourceLoc(), GenericParams, Parent, ClangN); FD->setParameters(BodyParams); FD->setResultInterfaceType(FnRetType); return FD; diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 35719f241d73b..eaf38cbaf7b0e 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -21,8 +21,9 @@ #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsClangImporter.h" -#include "swift/AST/ImportCache.h" +#include "swift/AST/DiagnosticsSema.h" #include "swift/AST/IRGenOptions.h" +#include "swift/AST/ImportCache.h" #include "swift/AST/LinkLibrary.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" @@ -35,8 +36,6 @@ #include "swift/ClangImporter/ClangModule.h" #include "swift/Config.h" #include "swift/Demangling/Demangle.h" -#include "swift/ClangImporter/ClangModule.h" -#include "swift/Config.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/Parser.h" #include "swift/Strings.h" @@ -4082,3 +4081,32 @@ swift::getModuleCachePathFromClang(const clang::CompilerInstance &Clang) { return llvm::sys::path::parent_path(SpecificModuleCachePath).str(); } +clang::FunctionDecl *ClangImporter::instantiateCXXFunctionTemplate( + ASTContext &ctx, clang::FunctionTemplateDecl *func, SubstitutionMap subst) { + SmallVector templateSubst; + std::unique_ptr error = + ctx.getClangTemplateArguments(func->getTemplateParameters(), + subst.getReplacementTypes(), templateSubst); + if (error) { + std::string failedTypesStr; + llvm::raw_string_ostream failedTypesStrStream(failedTypesStr); + llvm::interleaveComma(error->failedTypes, failedTypesStrStream); + // TODO: Use the location of the apply here. + // TODO: This error message should not reference implementation details. + // See: https://github.com/apple/swift/pull/33053#discussion_r477003350 + ctx.Diags.diagnose(SourceLoc(), + diag::unable_to_convert_generic_swift_types.ID, + {func->getName(), StringRef(failedTypesStr)}); + // Return a valid FunctionDecl but, we'll never use it. + return func->getAsFunction(); + } + + // Instanciate a specialization of this template using the substitution map. + auto *templateArgList = clang::TemplateArgumentList::CreateCopy( + func->getASTContext(), templateSubst); + auto &sema = getClangInstance().getSema(); + auto *spec = sema.InstantiateFunctionDeclaration(func, templateArgList, + clang::SourceLocation()); + sema.InstantiateFunctionDefinition(clang::SourceLocation(), spec); + return spec; +} diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index b2128bea8b732..2a058ac9ed739 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -17,6 +17,7 @@ #include "CFTypeInfo.h" #include "ImporterImpl.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/ASTDemangler.h" #include "swift/AST/ASTMangler.h" #include "swift/AST/Attr.h" #include "swift/AST/Builtins.h" @@ -51,7 +52,6 @@ #include "clang/AST/DeclObjCCommon.h" #include "clang/AST/DeclCXX.h" #include "clang/Basic/CharInfo.h" -#include "swift/Basic/Statistic.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/Preprocessor.h" @@ -161,27 +161,22 @@ createVarWithPattern(ASTContext &ctx, DeclContext *dc, Identifier name, Type ty, static FuncDecl *createFuncOrAccessor(ASTContext &ctx, SourceLoc funcLoc, Optional accessorInfo, DeclName name, SourceLoc nameLoc, - ParameterList *bodyParams, - Type resultTy, - bool async, - bool throws, - DeclContext *dc, + GenericParamList *genericParams, + ParameterList *bodyParams, Type resultTy, + bool async, bool throws, DeclContext *dc, ClangNode clangNode) { if (accessorInfo) { return AccessorDecl::create(ctx, funcLoc, /*accessorKeywordLoc*/ SourceLoc(), - accessorInfo->Kind, - accessorInfo->Storage, - /*StaticLoc*/SourceLoc(), - StaticSpellingKind::None, - throws, - /*ThrowsLoc=*/SourceLoc(), - /*GenericParams=*/nullptr, - bodyParams, - resultTy, dc, clangNode); + accessorInfo->Kind, accessorInfo->Storage, + /*StaticLoc*/ SourceLoc(), + StaticSpellingKind::None, throws, + /*ThrowsLoc=*/SourceLoc(), genericParams, + bodyParams, resultTy, dc, clangNode); } else { return FuncDecl::createImported(ctx, funcLoc, name, nameLoc, async, throws, - bodyParams, resultTy, dc, clangNode); + bodyParams, resultTy, genericParams, dc, + clangNode); } } @@ -3736,9 +3731,9 @@ namespace { continue; nonSelfParams.push_back(decl->getParamDecl(i)); } - return Impl.importFunctionParameterList(dc, decl, nonSelfParams, - decl->isVariadic(), - allowNSUIntegerAsInt, argNames); + return Impl.importFunctionParameterList( + dc, decl, nonSelfParams, decl->isVariadic(), allowNSUIntegerAsInt, + argNames, /*genericParams=*/{}); } Decl *importGlobalAsInitializer(const clang::FunctionDecl *decl, @@ -3783,10 +3778,11 @@ namespace { return importFunctionDecl(decl, importedName, correctSwiftName, None); } - Decl *importFunctionDecl(const clang::FunctionDecl *decl, - ImportedName importedName, - Optional correctSwiftName, - Optional accessorInfo) { + Decl *importFunctionDecl( + const clang::FunctionDecl *decl, ImportedName importedName, + Optional correctSwiftName, + Optional accessorInfo, + const clang::FunctionTemplateDecl *funcTemplate = nullptr) { if (decl->isDeleted()) return nullptr; @@ -3801,6 +3797,22 @@ namespace { ImportedType importedType; bool selfIsInOut = false; ParameterList *bodyParams = nullptr; + GenericParamList *genericParams = nullptr; + SmallVector templateParams; + if (funcTemplate) { + unsigned i = 0; + for (auto param : *funcTemplate->getTemplateParameters()) { + auto *typeParam = Impl.createDeclWithClangNode( + param, AccessLevel::Public, dc, + Impl.SwiftContext.getIdentifier(param->getName()), SourceLoc(), 0, + i); + templateParams.push_back(typeParam); + (void)++i; + } + genericParams = GenericParamList::create(Impl.SwiftContext, SourceLoc(), + templateParams, SourceLoc()); + } + if (!dc->isModuleScopeContext() && !isa(decl)) { // Handle initializers. if (name.getBaseName() == DeclBaseName::createConstructor()) { @@ -3868,7 +3880,8 @@ namespace { // names get into the resulting function type. importedType = Impl.importFunctionParamsAndReturnType( dc, decl, {decl->param_begin(), decl->param_size()}, - decl->isVariadic(), isInSystemModule(dc), name, bodyParams); + decl->isVariadic(), isInSystemModule(dc), name, bodyParams, + templateParams); if (auto *mdecl = dyn_cast(decl)) { if (mdecl->isStatic() || @@ -3901,6 +3914,10 @@ namespace { auto loc = Impl.importSourceLoc(decl->getLocation()); + ClangNode clangNode = decl; + if (funcTemplate) + clangNode = funcTemplate; + // FIXME: Poor location info. auto nameLoc = Impl.importSourceLoc(decl->getLocation()); @@ -3914,17 +3931,17 @@ namespace { DeclName ctorName(Impl.SwiftContext, DeclBaseName::createConstructor(), bodyParams); result = Impl.createDeclWithClangNode( - decl, AccessLevel::Public, ctorName, loc, /*failable=*/false, + clangNode, AccessLevel::Public, ctorName, loc, /*failable=*/false, /*FailabilityLoc=*/SourceLoc(), /*Throws=*/false, - /*ThrowsLoc=*/SourceLoc(), bodyParams, /*GenericParams=*/nullptr, - dc); + /*ThrowsLoc=*/SourceLoc(), bodyParams, genericParams, dc); } else { auto resultTy = importedType.getType(); FuncDecl *func = createFuncOrAccessor(Impl.SwiftContext, loc, accessorInfo, name, - nameLoc, bodyParams, resultTy, - /*async=*/false, /*throws=*/false, dc, decl); + nameLoc, genericParams, bodyParams, resultTy, + /*async=*/false, /*throws=*/false, dc, + clangNode); result = func; if (!dc->isModuleScopeContext()) { @@ -4158,6 +4175,21 @@ namespace { return nullptr; } + Decl *VisitFunctionTemplateDecl(const clang::FunctionTemplateDecl *decl) { + Optional correctSwiftName; + auto importedName = + importFullName(decl->getAsFunction(), correctSwiftName); + if (!importedName) + return nullptr; + // All template parameters must be template type parameters. + if (!llvm::all_of(*decl->getTemplateParameters(), [](auto param) { + return isa(param); + })) + return nullptr; + return importFunctionDecl(decl->getAsFunction(), importedName, + correctSwiftName, None, decl); + } + Decl *VisitUsingDecl(const clang::UsingDecl *decl) { // Using declarations are not imported. return nullptr; @@ -4530,12 +4562,11 @@ namespace { } auto result = createFuncOrAccessor(Impl.SwiftContext, - /*funcLoc*/SourceLoc(), - accessorInfo, + /*funcLoc*/ SourceLoc(), accessorInfo, importedName.getDeclName(), - /*nameLoc*/SourceLoc(), - bodyParams, resultTy, - async, throws, dc, decl); + /*nameLoc*/ SourceLoc(), + /*genericParams=*/nullptr, bodyParams, + resultTy, async, throws, dc, decl); result->setAccess(getOverridableAccessLevel(dc)); @@ -6045,7 +6076,7 @@ Decl *SwiftDeclConverter::importGlobalAsInitializer( } else { parameterList = Impl.importFunctionParameterList( dc, decl, {decl->param_begin(), decl->param_end()}, decl->isVariadic(), - allowNSUIntegerAsInt, argNames); + allowNSUIntegerAsInt, argNames, /*genericParams=*/{}); } if (!parameterList) return nullptr; diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 70bd70dff9ac6..7156b8cde5353 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -24,6 +24,7 @@ #include "swift/AST/DiagnosticsClangImporter.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ParameterList.h" @@ -210,10 +211,10 @@ namespace { ImportResult VisitType(const Type*) = delete; -#define DEPENDENT_TYPE(Class, Base) \ - ImportResult Visit##Class##Type(const clang::Class##Type *) { \ - llvm_unreachable("Dependent types cannot be converted"); \ - } +#define DEPENDENT_TYPE(Class, Base) \ + ImportResult Visit##Class##Type(const clang::Class##Type *) { \ + llvm_unreachable("Dependent types cannot be converted"); \ + } #define TYPE(Class, Base) #include "clang/AST/TypeNodes.inc" @@ -1696,22 +1697,51 @@ ImportedType ClangImporter::Implementation::importFunctionReturnType( OptionalityOfReturn); } +static Type +findGenericTypeInGenericDecls(const clang::TemplateTypeParmType *templateParam, + ArrayRef genericParams) { + StringRef name = templateParam->getIdentifier()->getName(); + auto genericParamIter = + llvm::find_if(genericParams, [name](GenericTypeParamDecl *generic) { + return generic->getName().str() == name; + }); + // TODO: once we support generics in class types, replace this with + // "return nullptr". Once support for template classes, this will need to + // be updated, though. I'm leaving the assert here to make it easier to + // find. + assert(genericParamIter != genericParams.end() && + "Could not find generic param type in generic params."); + auto *genericParamDecl = *genericParamIter; + auto metatype = + cast(genericParamDecl->getInterfaceType().getPointer()); + return metatype->getMetatypeInstanceType(); +} + ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType( DeclContext *dc, const clang::FunctionDecl *clangDecl, ArrayRef params, bool isVariadic, - bool isFromSystemModule, DeclName name, ParameterList *¶meterList) { + bool isFromSystemModule, DeclName name, ParameterList *¶meterList, + ArrayRef genericParams) { bool allowNSUIntegerAsInt = shouldAllowNSUIntegerAsInt(isFromSystemModule, clangDecl); - auto importedType = - importFunctionReturnType(dc, clangDecl, allowNSUIntegerAsInt); - if (!importedType) - return {Type(), false}; + ImportedType importedType; + if (auto templateType = + dyn_cast(clangDecl->getReturnType())) { + importedType = {findGenericTypeInGenericDecls(templateType, genericParams), + false}; + } else { + importedType = + importFunctionReturnType(dc, clangDecl, allowNSUIntegerAsInt); + if (!importedType) + return {Type(), false}; + } ArrayRef argNames = name.getArgumentNames(); parameterList = importFunctionParameterList(dc, clangDecl, params, isVariadic, - allowNSUIntegerAsInt, argNames); + allowNSUIntegerAsInt, argNames, + genericParams); if (!parameterList) return {Type(), false}; @@ -1725,7 +1755,8 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType( ParameterList *ClangImporter::Implementation::importFunctionParameterList( DeclContext *dc, const clang::FunctionDecl *clangDecl, ArrayRef params, bool isVariadic, - bool allowNSUIntegerAsInt, ArrayRef argNames) { + bool allowNSUIntegerAsInt, ArrayRef argNames, + ArrayRef genericParams) { // Import the parameters. SmallVector parameters; unsigned index = 0; @@ -1773,12 +1804,21 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList( importKind = ImportTypeKind::CFUnretainedOutParameter; // Import the parameter type into Swift. - auto importedType = importType(paramTy, importKind, allowNSUIntegerAsInt, - Bridgeability::Full, OptionalityOfParam); - if (!importedType) - return nullptr; + Type swiftParamTy; + bool isParamTypeImplicitlyUnwrapped = false; + if (auto *templateParamType = + dyn_cast(paramTy)) { + swiftParamTy = + findGenericTypeInGenericDecls(templateParamType, genericParams); + } else { + auto importedType = importType(paramTy, importKind, allowNSUIntegerAsInt, + Bridgeability::Full, OptionalityOfParam); + if (!importedType) + return nullptr; - auto swiftParamTy = importedType.getType(); + isParamTypeImplicitlyUnwrapped = importedType.isImplicitlyUnwrapped(); + swiftParamTy = importedType.getType(); + } // Map __attribute__((noescape)) to @noescape. if (param->hasAttr()) { @@ -1806,8 +1846,7 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList( ImportedHeaderUnit); paramInfo->setSpecifier(ParamSpecifier::Default); paramInfo->setInterfaceType(swiftParamTy); - recordImplicitUnwrapForDecl(paramInfo, - importedType.isImplicitlyUnwrapped()); + recordImplicitUnwrapForDecl(paramInfo, isParamTypeImplicitlyUnwrapped); parameters.push_back(paramInfo); ++index; } diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 5b0876cb98e45..45f537f3d33fb 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -1077,13 +1077,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// to system APIs. /// \param name The name of the function. /// \param[out] parameterList The parameters visible inside the function body. - ImportedType - importFunctionParamsAndReturnType(DeclContext *dc, - const clang::FunctionDecl *clangDecl, - ArrayRef params, - bool isVariadic, bool isFromSystemModule, - DeclName name, - ParameterList *¶meterList); + ImportedType importFunctionParamsAndReturnType( + DeclContext *dc, const clang::FunctionDecl *clangDecl, + ArrayRef params, bool isVariadic, + bool isFromSystemModule, DeclName name, ParameterList *¶meterList, + ArrayRef genericParams); /// Import the given function return type. /// @@ -1110,12 +1108,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// \param argNames The argument names /// /// \returns The imported parameter list on success, or null on failure - ParameterList * - importFunctionParameterList(DeclContext *dc, - const clang::FunctionDecl *clangDecl, - ArrayRef params, - bool isVariadic, bool allowNSUIntegerAsInt, - ArrayRef argNames); + ParameterList *importFunctionParameterList( + DeclContext *dc, const clang::FunctionDecl *clangDecl, + ArrayRef params, bool isVariadic, + bool allowNSUIntegerAsInt, ArrayRef argNames, + ArrayRef genericParams); ImportedType importPropertyType(const clang::ObjCPropertyDecl *clangDecl, bool isFromSystemModule); diff --git a/lib/ClangImporter/SwiftLookupTable.h b/lib/ClangImporter/SwiftLookupTable.h index 0a5afe26e0d4d..db60c67a90807 100644 --- a/lib/ClangImporter/SwiftLookupTable.h +++ b/lib/ClangImporter/SwiftLookupTable.h @@ -201,6 +201,7 @@ class EffectiveClangContext { DC = nsDecl->getCanonicalDecl(); } else { assert(isa(dc) || + isa(dc) || isa(dc) && "No other kinds of effective Clang contexts"); DC = dc; diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 571965a2f387d..2b08dda1cd2bb 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -16,17 +16,18 @@ // //===----------------------------------------------------------------------===// -#include "CodeSynthesis.h" #include "CSDiagnostics.h" +#include "CodeSynthesis.h" #include "MiscDiagnostics.h" #include "TypeCheckProtocol.h" #include "TypeCheckType.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" +#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ExistentialLayout.h" -#include "swift/AST/Initializer.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" +#include "swift/AST/Initializer.h" #include "swift/AST/OperatorNameLookup.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" @@ -34,6 +35,12 @@ #include "swift/Basic/StringExtras.h" #include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/SolutionResult.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Mangle.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/Template.h" +#include "clang/Sema/TemplateDeduction.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/SmallString.h" @@ -99,6 +106,45 @@ Solution::computeSubstitutions(GenericSignature sig, lookupConformanceFn); } +static ConcreteDeclRef generateDeclRefForSpecializedCXXFunctionTemplate( + ASTContext &ctx, FuncDecl *oldDecl, SubstitutionMap subst, + clang::FunctionDecl *specialized) { + // Create a new ParameterList with the substituted type. + auto oldFnType = + cast(oldDecl->getInterfaceType().getPointer()); + auto newFnType = oldFnType->substGenericArgs(subst); + SmallVector newParams; + unsigned i = 0; + for (auto paramTy : newFnType->getParams()) { + auto *oldParamDecl = oldDecl->getParameters()->get(i); + auto *newParamDecl = + ParamDecl::cloneWithoutType(oldDecl->getASTContext(), oldParamDecl); + newParamDecl->setInterfaceType(paramTy.getParameterType()); + newParams.push_back(newParamDecl); + (void)++i; + } + auto *newParamList = + ParameterList::create(ctx, SourceLoc(), newParams, SourceLoc()); + + // Generate a name for the specialized function. + std::string newNameStr; + llvm::raw_string_ostream buffer(newNameStr); + clang::MangleContext *mangler = + specialized->getASTContext().createMangleContext(); + mangler->mangleName(specialized, buffer); + buffer.flush(); + // Add all parameters as empty parameters. + auto newName = DeclName( + ctx, DeclName(ctx.getIdentifier(newNameStr)).getBaseName(), newParamList); + + auto newFnDecl = FuncDecl::createImported( + ctx, oldDecl->getLoc(), newName, oldDecl->getNameLoc(), + /*Async*/ false, oldDecl->hasThrows(), newParamList, + newFnType->getResult(), /*GenericParams*/ nullptr, + oldDecl->getDeclContext(), specialized); + return ConcreteDeclRef(newFnDecl); +} + ConcreteDeclRef Solution::resolveConcreteDeclRef(ValueDecl *decl, ConstraintLocator *locator) const { @@ -107,7 +153,25 @@ Solution::resolveConcreteDeclRef(ValueDecl *decl, // Get the generic signatue of the decl and compute the substitutions. auto sig = decl->getInnermostDeclContext()->getGenericSignatureOfContext(); - return ConcreteDeclRef(decl, computeSubstitutions(sig, locator)); + auto subst = computeSubstitutions(sig, locator); + + // If this is a C++ function template, get it's specialization for the given + // substitution map and update the decl accordingly. + if (decl->getClangDecl() && + isa(decl->getClangDecl())) { + auto *newFn = + decl->getASTContext() + .getClangModuleLoader() + ->instantiateCXXFunctionTemplate( + decl->getASTContext(), + const_cast( + cast(decl->getClangDecl())), + subst); + return generateDeclRefForSpecializedCXXFunctionTemplate( + decl->getASTContext(), cast(decl), subst, newFn); + } + + return ConcreteDeclRef(decl, subst); } ConstraintLocator *Solution::getCalleeLocator(ConstraintLocator *locator, @@ -634,7 +698,6 @@ namespace { if (!ref) ref = solution.resolveConcreteDeclRef(decl, loc); - assert(ref.getDecl() == decl); return ref; } diff --git a/test/Interop/Cxx/templates/Inputs/function-templates.h b/test/Interop/Cxx/templates/Inputs/function-templates.h new file mode 100644 index 0000000000000..31a0567e8a4d3 --- /dev/null +++ b/test/Interop/Cxx/templates/Inputs/function-templates.h @@ -0,0 +1,22 @@ +template T add(T a, T b) { return a + b; } + +template A addTwoTemplates(A a, B b) { return a + b; } + +template T passThrough(T value) { return value; } + +template const T passThroughConst(const T value) { return value; } + +void takesString(const char *) {} +template void expectsString(T str) { takesString(str); } + +template void integerTemplate() {} +template void defaultIntegerTemplate() {} + +// We cannot yet use this in swift but, make sure we don't crash when parsing +// it. +template R returns_template(T a, U b) { + return a + b; +} + +// Same here: +template void cannot_infer_template() {} diff --git a/test/Interop/Cxx/templates/Inputs/module.modulemap b/test/Interop/Cxx/templates/Inputs/module.modulemap index ca82db18fc25f..0b52858a95826 100644 --- a/test/Interop/Cxx/templates/Inputs/module.modulemap +++ b/test/Interop/Cxx/templates/Inputs/module.modulemap @@ -22,6 +22,10 @@ module ExplicitClassSpecialization { header "explicit-class-specialization.h" } +module FunctionTemplates { + header "function-templates.h" +} + module UsingDirective { header "using-directive.h" } diff --git a/test/Interop/Cxx/templates/function-template-errors.swift b/test/Interop/Cxx/templates/function-template-errors.swift new file mode 100644 index 0000000000000..28d163218d19e --- /dev/null +++ b/test/Interop/Cxx/templates/function-template-errors.swift @@ -0,0 +1,39 @@ +// RUN: not %target-swift-emit-sil %s -I %S/Inputs -enable-cxx-interop 2>&1 | %FileCheck %s + +// README: If you just added support for protocol composition to the +// ClangTypeConverter, please update this test to use a different type that we +// don't support so the error messages here are still tested. + + +import FunctionTemplates + +// Use protocol composition to create a type that we cannot (yet) turn into a clang::QualType. +protocol A { } +protocol B { } +protocol C { } + +// CHECK: error: could not generate C++ types from the generic Swift types provided. The following Swift type(s) provided to 'passThrough' were unable to be converted: A & B. +public func caller1(x: A & B) -> A & B { + return passThrough(x) +} + +// CHECK: error: could not generate C++ types from the generic Swift types provided. The following Swift type(s) provided to 'addTwoTemplates' were unable to be converted: A & B, A & C. +public func caller2(x: A & B, y: A & C) -> A & B { + return addTwoTemplates(x, y) +} + +// Make sure we emit an error and don't crash when failing to instantiate a function. +// CHECK: error: no matching function for call to 'takesString' +// CHECK: note: in instantiation of function template specialization 'expectsString' requested here +// CHECK: note: candidate function not viable: no known conversion from 'int' to 'const char *' for 1st argument +public func callExpectsString() { + expectsString(0 as Int32) +} + +// Make sure we don't import non-type template parameters. +// CHECK: error: cannot find 'integerTemplate' in scope +// CHECK: error: cannot find 'defaultIntegerTemplate' in scope +public func callIntegerTemplates() { + integerTemplate() + defaultIntegerTemplate() +} diff --git a/test/Interop/Cxx/templates/function-template-irgen-objc.swift b/test/Interop/Cxx/templates/function-template-irgen-objc.swift new file mode 100644 index 0000000000000..8999172836226 --- /dev/null +++ b/test/Interop/Cxx/templates/function-template-irgen-objc.swift @@ -0,0 +1,17 @@ +// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s + +// TODO: This needs to be fixed in the SwiftTypeConverter. +// Currently "Any" is imported as an Objective-C "id". +// That doesn't work unless we have Objective-C interop. +// Once that's done, this test can be merged with "template-irgen". +// REQUIRES: objc_interop + +import FunctionTemplates + +// CHECK-LABEL: define {{.*}}void @"$s4main18testPassThroughAny1xypyp_tF"(%Any* noalias nocapture sret %0, %Any* noalias nocapture dereferenceable({{32|16}}) %1) +// CHECK: call i8* @_Z11passThroughIP11objc_objectET_S2_(i8* +// CHECK: ret void +public func testPassThroughAny(x: Any) -> Any { + return passThrough(x) +} + diff --git a/test/Interop/Cxx/templates/function-template-irgen.swift b/test/Interop/Cxx/templates/function-template-irgen.swift new file mode 100644 index 0000000000000..b3ce1ef6b0801 --- /dev/null +++ b/test/Interop/Cxx/templates/function-template-irgen.swift @@ -0,0 +1,39 @@ +// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s + +import FunctionTemplates + +// CHECK-LABEL: define {{.*}}i32 @"$s4main20testPassThroughConst1xs5Int32VAE_tF"(i32 %0) +// CHECK: [[RET_VAL:%.*]] = call i32 @{{_Z16passThroughConstIiEKT_S0_|"\?\?\$passThroughConst@H@@YA\?BHH@Z"}}(i32 %0) +// CHECK: ret i32 [[RET_VAL]] + +// CHECK-LABEL: define linkonce_odr {{.*}}i32 @{{_Z16passThroughConstIiEKT_S0_|"\?\?\$passThroughConst@H@@YA\?BHH@Z"}}(i32 %value) +public func testPassThroughConst(x: Int32) -> Int32 { + return passThroughConst(x) +} + +// CHECK-LABEL: define {{.*}}i32 @"$s4main15testPassThrough1xs5Int32VAE_tF"(i32 %0) +// CHECK: [[RET_VAL:%.*]] = call i32 @{{_Z11passThroughIiET_S0_|"\?\?\$passThrough@H@@YAHH@Z"}}(i32 %0) +// CHECK: ret i32 [[RET_VAL]] + +// CHECK-LABEL: define linkonce_odr {{.*}}i32 @{{_Z11passThroughIiET_S0_|"\?\?\$passThrough@H@@YAHH@Z"}}(i32 %value) +public func testPassThrough(x: Int32) -> Int32 { + return passThrough(x) +} + +// CHECK-LABEL: define {{.*}}i32 @"$s4main19testAddTwoTemplates1xs5Int32VAE_tF"(i32 %0) +// CHECK: [[OUT_VAL:%.*]] = call i32 @{{_Z15addTwoTemplatesIiiET_S0_T0_|"\?\?\$addTwoTemplates@HH@@YAHHH@Z"}}(i32 %0, i32 %0) +// CHECK: ret i32 [[OUT_VAL]] + +// CHECK-LABEL: define linkonce_odr {{.*}}i32 @{{_Z15addTwoTemplatesIiiET_S0_T0_|"\?\?\$addTwoTemplates@HH@@YAHHH@Z"}}(i32 %a, i32 %b) +public func testAddTwoTemplates(x: Int32) -> Int32 { + return addTwoTemplates(x, x) +} + +// CHECK-LABEL: define {{.*}}i32 @"$s4main7testAdd1xs5Int32VAE_tF"(i32 %0) +// CHECK: [[OUT_VAL:%.*]] = call i32 @{{_Z3addIiET_S0_S0_|"\?\?\$add@H@@YAHHH@Z"}}(i32 %0, i32 %0) +// CHECK: ret i32 [[OUT_VAL]] + +// CHECK-LABEL: define linkonce_odr {{.*}}i32 @{{_Z3addIiET_S0_S0_|"\?\?\$add@H@@YAHHH@Z"}}(i32 %a, i32 %b) +public func testAdd(x: Int32) -> Int32 { + return add(x, x) +} diff --git a/test/Interop/Cxx/templates/function-template-module-interface.swift b/test/Interop/Cxx/templates/function-template-module-interface.swift new file mode 100644 index 0000000000000..b4239dbeb690c --- /dev/null +++ b/test/Interop/Cxx/templates/function-template-module-interface.swift @@ -0,0 +1,8 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=FunctionTemplates -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s + +// CHECK: func add(_ a: T, _ b: T) -> T +// CHECK: func addTwoTemplates(_ a: A, _ b: B) -> A +// CHECK: func passThrough(_ value: T) -> T +// CHECK: func passThroughConst(_ value: T) -> T +// CHECK: func returns_template(_ a: T, _ b: U) -> R +// CHECK: func cannot_infer_template() diff --git a/test/Interop/Cxx/templates/function-template-silgen.swift b/test/Interop/Cxx/templates/function-template-silgen.swift new file mode 100644 index 0000000000000..bfc5256fe2baf --- /dev/null +++ b/test/Interop/Cxx/templates/function-template-silgen.swift @@ -0,0 +1,31 @@ +// RUN: %target-swift-emit-sil %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s + +import FunctionTemplates + +// CHECK-LABEL: sil @$s4main4test1xs5Int32VAE_tF + +// CHECK: bb0(%0 : $Int32): +// CHECK: [[IL_ZERO:%.*]] = integer_literal $Builtin.Int32, 0 +// CHECK: [[ZERO:%.*]] = struct $Int32 ([[IL_ZERO]] : $Builtin.Int32) +// CHECK: [[PASS_THROUGH_CONST_FN:%.*]] = function_ref @{{_Z16passThroughConstIiEKT_S0_|\?\?\$passThroughConst@H@@YA\?BHH@Z}} : $@convention(c) (Int32) -> Int32 +// CHECK: [[A:%.*]] = apply [[PASS_THROUGH_CONST_FN]]([[ZERO]]) : $@convention(c) (Int32) -> Int32 + +// CHECK: [[PASS_THROUGH_FN:%.*]] = function_ref @{{_Z11passThroughIiET_S0_|\?\?\$passThrough@H@@YAHH@Z}} : $@convention(c) (Int32) -> Int32 +// CHECK: [[B:%.*]] = apply [[PASS_THROUGH_FN]](%0) : $@convention(c) (Int32) -> Int32 + +// CHECK: [[ADD_TWO_FN:%.*]] = function_ref @{{_Z15addTwoTemplatesIiiET_S0_T0_|\?\?\$addTwoTemplates@HH@@YAHHH@Z}} : $@convention(c) (Int32, Int32) -> Int32 +// CHECK: [[C:%.*]] = apply [[ADD_TWO_FN]]([[A]], [[B]]) : $@convention(c) (Int32, Int32) -> Int32 + +// CHECK: [[C_32_ADDR:%.*]] = alloc_stack $Int32 +// CHECK: [[C_32:%.*]] = load [[C_32_ADDR]] : $*Int32 +// CHECK: [[ADD_FN:%.*]] = function_ref @{{_Z3addIiET_S0_S0_|\?\?\$add@H@@YAHHH@Z}} : $@convention(c) (Int32, Int32) -> Int32 +// CHECK: [[OUT:%.*]] = apply [[ADD_FN]]([[B]], [[C_32]]) : $@convention(c) (Int32, Int32) -> Int32 +// CHECK: return [[OUT]] : $Int32 + +// CHECK-LABEL: end sil function '$s4main4test1xs5Int32VAE_tF' +public func test(x: Int32) -> Int32 { + let a = passThroughConst(Int32(0)) + let b = passThrough(x) + let c = addTwoTemplates(a, b) + return add(b, Int32(c)) +} diff --git a/test/Interop/Cxx/templates/function-template.swift b/test/Interop/Cxx/templates/function-template.swift new file mode 100644 index 0000000000000..cb3b6310e235a --- /dev/null +++ b/test/Interop/Cxx/templates/function-template.swift @@ -0,0 +1,37 @@ +// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-cxx-interop) +// +// REQUIRES: executable_test + +import FunctionTemplates +import StdlibUnittest + +var FunctionTemplateTestSuite = TestSuite("Function Templates") + +FunctionTemplateTestSuite.test("passThrough where T == Int") { + let result = passThrough(42) + expectEqual(42, result) +} + +FunctionTemplateTestSuite.test("add where T == Int") { + let result = add(42, 23) + expectEqual(65, result) +} + +FunctionTemplateTestSuite.test("add where T, U == Int") { + let result = addTwoTemplates(42, 23) + expectEqual(65, result) +} + +// TODO: currently "Any" is imported as an Objective-C "id". +// This doesn't work without the Objective-C runtime. +#if _runtime(_ObjC) +FunctionTemplateTestSuite.test("passThrough where T == Any") { + let result = passThrough(42 as Any) + expectEqual(42, result as! Int) +} +#endif + +// TODO: Generics, Any, and Protocols should be tested here but need to be +// better supported in ClangTypeConverter first. + +runAllTests() From 24ea8becc8726859671d6086e5c8f724b3c6a13c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 21 Oct 2020 21:48:52 -0700 Subject: [PATCH 628/745] [Concurrency] Move removal of leading "get" for async imports. Name adjustments should be performed by omitNeedlessWords(), not the early classifcation of async imports. --- include/swift/Basic/StringExtras.h | 3 +++ lib/Basic/StringExtras.cpp | 9 +++++++ lib/ClangImporter/IAMInference.cpp | 2 +- lib/ClangImporter/ImportName.cpp | 42 +++++++++++------------------- lib/ClangImporter/ImportName.h | 1 - lib/Sema/MiscDiagnostics.cpp | 6 +++-- 6 files changed, 32 insertions(+), 31 deletions(-) diff --git a/include/swift/Basic/StringExtras.h b/include/swift/Basic/StringExtras.h index 08e4c2cfbe23d..4bc4fe7a8a964 100644 --- a/include/swift/Basic/StringExtras.h +++ b/include/swift/Basic/StringExtras.h @@ -442,6 +442,8 @@ class InheritedNameSet { /// /// \param allPropertyNames The set of property names in the enclosing context. /// +/// \param isAsync Whether this is a function imported as 'async'. +/// /// \param scratch Scratch space that will be used for modifications beyond /// just chopping names. /// @@ -455,6 +457,7 @@ bool omitNeedlessWords(StringRef &baseName, bool returnsSelf, bool isProperty, const InheritedNameSet *allPropertyNames, + bool isAsync, StringScratchSpace &scratch); } // end namespace swift diff --git a/lib/Basic/StringExtras.cpp b/lib/Basic/StringExtras.cpp index a33dd51e8caac..4810a0ca401d3 100644 --- a/lib/Basic/StringExtras.cpp +++ b/lib/Basic/StringExtras.cpp @@ -1220,6 +1220,7 @@ bool swift::omitNeedlessWords(StringRef &baseName, bool returnsSelf, bool isProperty, const InheritedNameSet *allPropertyNames, + bool isAsync, StringScratchSpace &scratch) { bool anyChanges = false; OmissionTypeName resultType = returnsSelf ? contextType : givenResultType; @@ -1287,6 +1288,14 @@ bool swift::omitNeedlessWords(StringRef &baseName, } } + // If the base name of a method imported as "async" starts with the word + // "get", drop the "get". + if (isAsync && camel_case::getFirstWord(baseName) == "get" && + baseName.size() > 3) { + baseName = baseName.substr(3); + anyChanges = true; + } + // If needed, split the base name. if (!argNames.empty() && splitBaseName(baseName, argNames[0], paramTypes[0], firstParamName)) diff --git a/lib/ClangImporter/IAMInference.cpp b/lib/ClangImporter/IAMInference.cpp index f0e4564714045..6cbf5ce8781fa 100644 --- a/lib/ClangImporter/IAMInference.cpp +++ b/lib/ClangImporter/IAMInference.cpp @@ -448,7 +448,7 @@ class IAMInference { baseName = humbleBaseName.userFacingName(); bool didOmit = omitNeedlessWords(baseName, argStrs, "", "", "", paramTypeNames, false, - false, nullptr, scratch); + false, nullptr, false, scratch); SmallVector argLabels; for (auto str : argStrs) argLabels.push_back(getIdentifier(str)); diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index b3eb09cc964de..078d11792dd2f 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -804,7 +804,7 @@ static bool omitNeedlessWordsInFunctionName( ArrayRef params, clang::QualType resultType, const clang::DeclContext *dc, const SmallBitVector &nonNullArgs, Optional errorParamIndex, bool returnsSelf, bool isInstanceMethod, - NameImporter &nameImporter) { + Optional completionHandlerIndex, NameImporter &nameImporter) { clang::ASTContext &clangCtx = nameImporter.getClangContext(); // Collect the parameter type names. @@ -817,10 +817,6 @@ static bool omitNeedlessWordsInFunctionName( if (i == 0) firstParamName = param->getName(); - // Determine the number of parameters. - unsigned numParams = params.size(); - if (errorParamIndex) --numParams; - bool isLastParameter = (i == params.size() - 1) || (i == params.size() - 2 && @@ -858,7 +854,8 @@ static bool omitNeedlessWordsInFunctionName( getClangTypeNameForOmission(clangCtx, resultType), getClangTypeNameForOmission(clangCtx, contextType), paramTypes, returnsSelf, /*isProperty=*/false, - allPropertyNames, nameImporter.getScratch()); + allPropertyNames, completionHandlerIndex.hasValue(), + nameImporter.getScratch()); } /// Prepare global name for importing onto a swift_newtype. @@ -1189,7 +1186,6 @@ Optional NameImporter::considerAsyncImport( const clang::ObjCMethodDecl *clangDecl, StringRef &baseName, - SmallVectorImpl &baseNameScratch, SmallVectorImpl ¶mNames, ArrayRef params, bool isInitializer, bool hasCustomName, @@ -1225,17 +1221,6 @@ NameImporter::considerAsyncImport( return None; } - // If there's no custom name, and the base name starts with "get", drop the - // get. - if (!hasCustomName) { - StringRef currentBaseName = newBaseName ? *newBaseName : baseName; - if (currentBaseName.size() > 3 && - camel_case::getFirstWord(currentBaseName) == "get") { - newBaseName = camel_case::toLowercaseInitialisms( - currentBaseName.substr(3), baseNameScratch); - } - } - // Used for returns once we've determined that the method cannot be // imported as async, even though it has what looks like a completion handler // parameter. @@ -1473,7 +1458,6 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, } // If we have a swift_name attribute, use that. - SmallString<32> asyncBaseNameScratch; if (auto *nameAttr = findSwiftNameAttr(D, version)) { bool skipCustomName = false; @@ -1552,9 +1536,9 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, if (version.supportsConcurrency()) { if (auto asyncInfo = considerAsyncImport( - method, parsedName.BaseName, asyncBaseNameScratch, - parsedName.ArgumentLabels, params, isInitializer, - /*hasCustomName=*/true, result.getErrorInfo())) { + method, parsedName.BaseName, parsedName.ArgumentLabels, + params, isInitializer, /*hasCustomName=*/true, + result.getErrorInfo())) { result.info.hasAsyncInfo = true; result.info.asyncInfo = *asyncInfo; @@ -1829,9 +1813,8 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, if (version.supportsConcurrency() && result.info.accessorKind == ImportedAccessorKind::None) { if (auto asyncInfo = considerAsyncImport( - objcMethod, baseName, asyncBaseNameScratch, - argumentNames, params, isInitializer, /*hasCustomName=*/false, - result.getErrorInfo())) { + objcMethod, baseName, argumentNames, params, isInitializer, + /*hasCustomName=*/false, result.getErrorInfo())) { result.info.hasAsyncInfo = true; result.info.asyncInfo = *asyncInfo; } @@ -1971,7 +1954,7 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, (void)omitNeedlessWords(baseName, {}, "", propertyTypeName, contextTypeName, {}, /*returnsSelf=*/false, /*isProperty=*/true, allPropertyNames, - scratch); + /*isAsync=*/false, scratch); } } @@ -1983,7 +1966,12 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, result.getErrorInfo() ? Optional(result.getErrorInfo()->ErrorParameterIndex) : None, - method->hasRelatedResultType(), method->isInstanceMethod(), *this); + method->hasRelatedResultType(), method->isInstanceMethod(), + result.getAsyncInfo().map( + [](const ForeignAsyncConvention::Info &info) { + return info.CompletionHandlerParamIndex; + }), + *this); } // If the result is a value, lowercase it. diff --git a/lib/ClangImporter/ImportName.h b/lib/ClangImporter/ImportName.h index 12fc9b2bca82d..5f5d9a027343e 100644 --- a/lib/ClangImporter/ImportName.h +++ b/lib/ClangImporter/ImportName.h @@ -456,7 +456,6 @@ class NameImporter { Optional considerAsyncImport(const clang::ObjCMethodDecl *clangDecl, StringRef &baseName, - SmallVectorImpl &baseNameScratch, SmallVectorImpl ¶mNames, ArrayRef params, bool isInitializer, bool hasCustomName, diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 98f2462be9cab..deb27a20ba1a4 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -4867,7 +4867,8 @@ Optional TypeChecker::omitNeedlessWords(AbstractFunctionDecl *afd) { getTypeNameForOmission(resultType), getTypeNameForOmission(contextType), paramTypes, returnsSelf, false, - /*allPropertyNames=*/nullptr, scratch)) + /*allPropertyNames=*/nullptr, + afd->hasAsync(), scratch)) return None; /// Retrieve a replacement identifier. @@ -4922,7 +4923,8 @@ Optional TypeChecker::omitNeedlessWords(VarDecl *var) { OmissionTypeName contextTypeName = getTypeNameForOmission(contextType); if (::omitNeedlessWords(name, { }, "", typeName, contextTypeName, { }, /*returnsSelf=*/false, true, - /*allPropertyNames=*/nullptr, scratch)) { + /*allPropertyNames=*/nullptr, /*isAsync=*/false, + scratch)) { return Context.getIdentifier(name); } From c18331c837e7c3d5deccbc37edc3b6c29d5c3ecd Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 15 Oct 2020 01:57:32 -0400 Subject: [PATCH 629/745] Move swift_task_alloc/dealloc into the Concurrency library. Also, rename them to follow the general namespacing scheme. --- include/swift/Runtime/Concurrency.h | 16 ++++++++++++++ include/swift/Runtime/RuntimeFunctions.def | 8 +++---- stdlib/public/Concurrency/CMakeLists.txt | 1 + .../TaskAlloc.cpp} | 22 +++++++------------ stdlib/public/Concurrency/TaskStatus.cpp | 5 +++-- stdlib/public/runtime/CMakeLists.txt | 1 - 6 files changed, 32 insertions(+), 21 deletions(-) rename stdlib/public/{runtime/Concurrency.cpp => Concurrency/TaskAlloc.cpp} (52%) diff --git a/include/swift/Runtime/Concurrency.h b/include/swift/Runtime/Concurrency.h index 46daae006991b..adc47f410572b 100644 --- a/include/swift/Runtime/Concurrency.h +++ b/include/swift/Runtime/Concurrency.h @@ -21,6 +21,22 @@ namespace swift { +/// Allocate memory in a task. +/// +/// This must be called synchronously with the task. +/// +/// All allocations will be rounded to a multiple of MAX_ALIGNMENT. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void *swift_task_alloc(AsyncTask *task, size_t size); + +/// Deallocate memory in a task. +/// +/// The pointer provided must be the last pointer allocated on +/// this task that has not yet been deallocated; that is, memory +/// must be allocated and deallocated in a strict stack discipline. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void swift_task_dealloc(AsyncTask *task, void *ptr); + /// Cancel a task and all of its child tasks. /// /// This can be called from any thread. diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index 566ca9040cbd8..d12c8eb570863 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -1461,17 +1461,17 @@ FUNCTION(GetTypeByMangledNameInContextInMetadataState, Int8PtrPtrTy), ATTRS(NoUnwind, ArgMemOnly)) -// void *swift_taskAlloc(SwiftTask *task, size_t size); +// void *swift_task_alloc(AsyncTask *task, size_t size); FUNCTION(TaskAlloc, - swift_taskAlloc, SwiftCC, + swift_task_alloc, SwiftCC, ConcurrencyAvailability, RETURNS(Int8PtrTy), ARGS(SwiftTaskPtrTy, SizeTy), ATTRS(NoUnwind, ArgMemOnly)) -// void swift_taskDealloc(SwiftTask *task, void *ptr); +// void swift_task_dealloc(AsyncTask *task, void *ptr); FUNCTION(TaskDealloc, - swift_taskDealloc, SwiftCC, + swift_task_dealloc, SwiftCC, ConcurrencyAvailability, RETURNS(VoidTy), ARGS(SwiftTaskPtrTy, Int8PtrTy), diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index 32541e85a27cd..3a0b98edc5318 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -13,6 +13,7 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB Actor.swift PartialAsyncTask.swift + TaskAlloc.cpp TaskStatus.cpp Mutex.cpp diff --git a/stdlib/public/runtime/Concurrency.cpp b/stdlib/public/Concurrency/TaskAlloc.cpp similarity index 52% rename from stdlib/public/runtime/Concurrency.cpp rename to stdlib/public/Concurrency/TaskAlloc.cpp index aa5d25df7cc70..939670337d5a1 100644 --- a/stdlib/public/runtime/Concurrency.cpp +++ b/stdlib/public/Concurrency/TaskAlloc.cpp @@ -1,4 +1,4 @@ -//===------------ Concurrency.cpp - Swift Concurrency Support ------------===// +//===--- TaskAlloc.cpp - Task-local stack allocator -----------------------===// // // This source file is part of the Swift.org open source project // @@ -10,28 +10,22 @@ // //===----------------------------------------------------------------------===// // -// Implementations of the concurrency runtime functions. +// A task-local allocator that obeys a stack discipline. // -// void *swift_taskAlloc(SwiftTask *task, size_t size); -// void swift_taskDealloc(SwiftTask *task, void *ptr); +// Because allocation is task-local, and there's at most one thread +// running a task at once, no synchronization is required. // //===----------------------------------------------------------------------===// -#include "../SwiftShims/Visibility.h" -#include "swift/Runtime/Config.h" -#include +#include "swift/Runtime/Concurrency.h" #include -struct SwiftTask; +using namespace swift; -SWIFT_RUNTIME_EXPORT -SWIFT_CC(swift) -void *swift_taskAlloc(SwiftTask *task, size_t size) { +void *swift::swift_task_alloc(AsyncTask *task, size_t size) { return malloc(size); } -SWIFT_RUNTIME_EXPORT -SWIFT_CC(swift) -void swift_taskDealloc(SwiftTask *task, void *ptr) { +void swift::swift_task_dealloc(AsyncTask *task, void *ptr) { free(ptr); } diff --git a/stdlib/public/Concurrency/TaskStatus.cpp b/stdlib/public/Concurrency/TaskStatus.cpp index 11f9518e3b20f..453f98f5bc893 100644 --- a/stdlib/public/Concurrency/TaskStatus.cpp +++ b/stdlib/public/Concurrency/TaskStatus.cpp @@ -1,4 +1,4 @@ -//===--- Metadata.cpp - Swift Language ABI Metadata Support ---------------===// +//===--- TaskStatus.cpp - Asynchronous task status tracking ---------------===// // // This source file is part of the Swift.org open source project // @@ -10,7 +10,8 @@ // //===----------------------------------------------------------------------===// // -// Routines for cancelling tasks. +// Routines for maintaining and interacting with the current state of a +// task, including tracking child tasks, deadlines, and cancellation. // //===----------------------------------------------------------------------===// diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index f2f660f45cf8c..56d5110ef94d8 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -31,7 +31,6 @@ set(swift_runtime_sources BackDeployment.cpp Casting.cpp CompatibilityOverride.cpp - Concurrency.cpp CygwinPort.cpp Demangle.cpp DynamicCast.cpp From a8464dcaf114ed8b17bddc814cf2de8141e15bed Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 21 Oct 2020 21:35:13 -0400 Subject: [PATCH 630/745] Implicitly import _Concurrency under -enable-experimental-concurrency --- include/swift/Frontend/Frontend.h | 4 ++++ include/swift/Strings.h | 2 ++ lib/Frontend/Frontend.cpp | 8 ++++++++ test/Casting/Casts.swift | 1 + test/ClangImporter/objc_async.swift | 1 + test/Concurrency/actor_isolation.swift | 2 -- test/Concurrency/global_actor_inference.swift | 2 -- test/Constraints/async.swift | 2 ++ test/IDE/print_clang_objc_async.swift | 1 + test/IRGen/async.swift | 2 ++ test/IRGen/async/run-call-classinstance-int64-to-void.sil | 1 + test/IRGen/async/run-call-classinstance-void-to-void.sil | 1 + test/IRGen/async/run-call-existential-to-void.sil | 1 + test/IRGen/async/run-call-generic-to-generic.sil | 1 + test/IRGen/async/run-call-generic-to-void.sil | 1 + test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil | 1 + test/IRGen/async/run-call-int64-and-int64-to-void.sil | 1 + test/IRGen/async/run-call-int64-to-void.sil | 1 + .../run-call-protocolextension_instance-void-to-int64.sil | 1 + .../run-call-protocolwitness_instance-void-to-int64.sil | 1 + .../IRGen/async/run-call-structinstance-int64-to-void.sil | 1 + test/IRGen/async/run-call-void-throws-to-int-throwing.sil | 1 + ...to-int-throwing_call-async-nothrow_call-sync-throw.sil | 1 + ...-call-void-throws-to-int-throwing_call-async-throw.sil | 1 + ...to-int-throwing_call-sync-nothrow_call-async-throw.sil | 1 + ...n-call-void-throws-to-int-throwing_call-sync-throw.sil | 1 + test/IRGen/async/run-call-void-to-existential.sil | 1 + test/IRGen/async/run-call-void-to-int64-and-int64.sil | 1 + test/IRGen/async/run-call-void-to-int64.sil | 1 + test/IRGen/async/run-call-void-to-struct_large.sil | 1 + ...tocolwitness_instance-generic-to-int64-and-generic.sil | 1 + ...all_generic-protocolwitness_instance-void-to-int64.sil | 1 + test/IRGen/ptrauth-actor.swift | 1 - test/ModuleInterface/actor_isolation.swift | 1 - test/ModuleInterface/concurrency.swift | 2 ++ test/Parse/async-syntax.swift | 2 ++ test/Parse/async.swift | 2 ++ test/PrintAsObjC/async.swift | 1 + test/Runtime/demangleToMetadata.swift | 1 + test/SILGen/synthesized_conformance_actor.swift | 2 -- test/Serialization/async.swift | 2 ++ test/Serialization/attr-actorindependent.swift | 4 ++++ test/TypeDecoder/concurrency.swift | 2 ++ test/attr/actorindependent.swift | 2 ++ test/attr/actorindependent_unsafe.swift | 2 ++ test/attr/asynchandler.swift | 2 ++ test/attr/attr_cdecl_async.swift | 2 ++ test/attr/global_actor.swift | 2 -- test/decl/async/objc.swift | 1 + test/decl/class/actor/basic.swift | 2 ++ test/decl/class/actor/conformance.swift | 2 ++ test/decl/class/actor/global_actor_conformance.swift | 2 -- test/decl/func/async.swift | 2 ++ test/decl/protocol/conforms/objc_async.swift | 1 + test/decl/protocol/special/Actor.swift | 1 - test/expr/unary/async_await.swift | 2 ++ .../compiler_crashers_2_fixed/rdar70144083.swift | 2 ++ 57 files changed, 79 insertions(+), 13 deletions(-) diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index 482a2be3983cb..64f29e9804f0e 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -337,6 +337,10 @@ class CompilerInvocation { /// Whether the Swift -Onone support library should be implicitly imported. bool shouldImportSwiftONoneSupport() const; + /// Whether the Swift Concurrency support library should be implicitly + /// imported. + bool shouldImportSwiftConcurrency() const; + /// Performs input setup common to these tools: /// sil-opt, sil-func-extractor, sil-llvm-gen, and sil-nm. /// Return value includes the buffer so caller can keep it alive. diff --git a/include/swift/Strings.h b/include/swift/Strings.h index 2573fa1c8e616..4fa1ca1e20231 100644 --- a/include/swift/Strings.h +++ b/include/swift/Strings.h @@ -22,6 +22,8 @@ namespace swift { constexpr static const StringLiteral STDLIB_NAME = "Swift"; /// The name of the Onone support library, which is a reserved module name. constexpr static const StringLiteral SWIFT_ONONE_SUPPORT = "SwiftOnoneSupport"; +/// The name of the Concurrency module, which supports that extension. +constexpr static const StringLiteral SWIFT_CONCURRENCY_NAME = "_Concurrency"; /// The name of the SwiftShims module, which contains private stdlib decls. constexpr static const StringLiteral SWIFT_SHIMS_NAME = "SwiftShims"; /// The name of the Builtin module, which contains Builtin functions. diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 9601547de0193..c1ec9198ff0c9 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -707,6 +707,10 @@ CompilerInstance::openModuleDoc(const InputFile &input) { return None; } +bool CompilerInvocation::shouldImportSwiftConcurrency() const { + return getLangOptions().EnableExperimentalConcurrency; +} + /// Implicitly import the SwiftOnoneSupport module in non-optimized /// builds. This allows for use of popular specialized functions /// from the standard library, which makes the non-optimized builds @@ -758,6 +762,10 @@ ImplicitImportInfo CompilerInstance::getImplicitImportInfo() const { pushImport(SWIFT_ONONE_SUPPORT); } + if (Invocation.shouldImportSwiftConcurrency()) { + pushImport(SWIFT_CONCURRENCY_NAME); + } + imports.ShouldImportUnderlyingModule = frontendOpts.ImportUnderlyingModule; imports.BridgingHeaderPath = frontendOpts.ImplicitObjCHeaderPath; return imports; diff --git a/test/Casting/Casts.swift b/test/Casting/Casts.swift index 970b0797f8531..484116833afe4 100644 --- a/test/Casting/Casts.swift +++ b/test/Casting/Casts.swift @@ -24,6 +24,7 @@ // RUN: %target-run %t/a.swift5.O.out // // REQUIRES: executable_test +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib import StdlibUnittest diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift index c3cb8a68ac511..c4299eeac8886 100644 --- a/test/ClangImporter/objc_async.swift +++ b/test/ClangImporter/objc_async.swift @@ -1,6 +1,7 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -enable-experimental-concurrency %s -verify // REQUIRES: objc_interop +// REQUIRES: concurrency import Foundation import ObjCConcurrency diff --git a/test/Concurrency/actor_isolation.swift b/test/Concurrency/actor_isolation.swift index e3932306ebcf2..c0cae0e6b3e9c 100644 --- a/test/Concurrency/actor_isolation.swift +++ b/test/Concurrency/actor_isolation.swift @@ -1,8 +1,6 @@ // RUN: %target-typecheck-verify-swift -enable-experimental-concurrency // REQUIRES: concurrency -import _Concurrency - let immutableGlobal: String = "hello" var mutableGlobal: String = "can't touch this" // expected-note 2{{var declared here}} diff --git a/test/Concurrency/global_actor_inference.swift b/test/Concurrency/global_actor_inference.swift index 1db013712493a..5a2ec5bcd160a 100644 --- a/test/Concurrency/global_actor_inference.swift +++ b/test/Concurrency/global_actor_inference.swift @@ -1,8 +1,6 @@ // RUN: %target-typecheck-verify-swift -enable-experimental-concurrency // REQUIRES: concurrency -import _Concurrency - actor class SomeActor { } @globalActor diff --git a/test/Constraints/async.swift b/test/Constraints/async.swift index e325ef20e7499..24e21eeb1af92 100644 --- a/test/Constraints/async.swift +++ b/test/Constraints/async.swift @@ -1,5 +1,7 @@ // RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// REQUIRES: concurrency + func doAsynchronously() async { } func doSynchronously() { } diff --git a/test/IDE/print_clang_objc_async.swift b/test/IDE/print_clang_objc_async.swift index a836ac927ff5f..8520c4dfdcdb0 100644 --- a/test/IDE/print_clang_objc_async.swift +++ b/test/IDE/print_clang_objc_async.swift @@ -4,6 +4,7 @@ // RUN: %FileCheck -input-file %t/ObjCConcurrency.printed.txt %s // REQUIRES: objc_interop +// REQUIRES: concurrency // CHECK-LABEL: class SlowServer : NSObject { // CHECK-DAG: func doSomethingSlow(_ operation: String, completionHandler handler: @escaping (Int) -> Void) diff --git a/test/IRGen/async.swift b/test/IRGen/async.swift index 86d5b33b90e80..46ee16dc50472 100644 --- a/test/IRGen/async.swift +++ b/test/IRGen/async.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend -primary-file %s -emit-ir -enable-experimental-concurrency | %FileCheck %s +// REQUIRES: concurrency + // CHECK: "$s5async1fyyYF" public func f() async { } diff --git a/test/IRGen/async/run-call-classinstance-int64-to-void.sil b/test/IRGen/async/run-call-classinstance-int64-to-void.sil index 1f1adbfd61d7c..c94939800be55 100644 --- a/test/IRGen/async/run-call-classinstance-int64-to-void.sil +++ b/test/IRGen/async/run-call-classinstance-int64-to-void.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib diff --git a/test/IRGen/async/run-call-classinstance-void-to-void.sil b/test/IRGen/async/run-call-classinstance-void-to-void.sil index 40da285fed9d8..8adf7d7c8d664 100644 --- a/test/IRGen/async/run-call-classinstance-void-to-void.sil +++ b/test/IRGen/async/run-call-classinstance-void-to-void.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib diff --git a/test/IRGen/async/run-call-existential-to-void.sil b/test/IRGen/async/run-call-existential-to-void.sil index 29019ab19464f..552ba2ecf0f40 100644 --- a/test/IRGen/async/run-call-existential-to-void.sil +++ b/test/IRGen/async/run-call-existential-to-void.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib diff --git a/test/IRGen/async/run-call-generic-to-generic.sil b/test/IRGen/async/run-call-generic-to-generic.sil index d5da1c9f97e21..82b8a5ea8c9a2 100644 --- a/test/IRGen/async/run-call-generic-to-generic.sil +++ b/test/IRGen/async/run-call-generic-to-generic.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib diff --git a/test/IRGen/async/run-call-generic-to-void.sil b/test/IRGen/async/run-call-generic-to-void.sil index 94f6d550c3edf..2c777da0c58e6 100644 --- a/test/IRGen/async/run-call-generic-to-void.sil +++ b/test/IRGen/async/run-call-generic-to-void.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib diff --git a/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil b/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil index 1bce84155f6de..b723b549f76df 100644 --- a/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil +++ b/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib diff --git a/test/IRGen/async/run-call-int64-and-int64-to-void.sil b/test/IRGen/async/run-call-int64-and-int64-to-void.sil index 8921d5ac538a9..149f60ae1c5ad 100644 --- a/test/IRGen/async/run-call-int64-and-int64-to-void.sil +++ b/test/IRGen/async/run-call-int64-and-int64-to-void.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib diff --git a/test/IRGen/async/run-call-int64-to-void.sil b/test/IRGen/async/run-call-int64-to-void.sil index 5425e64cc2a2d..6305887d0d837 100644 --- a/test/IRGen/async/run-call-int64-to-void.sil +++ b/test/IRGen/async/run-call-int64-to-void.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib diff --git a/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil b/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil index fae1f0549f1bf..9412c513a1dd3 100644 --- a/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil +++ b/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib import Builtin diff --git a/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil b/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil index b260aaf31ac30..dfb1bd9137d53 100644 --- a/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil +++ b/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib import Builtin diff --git a/test/IRGen/async/run-call-structinstance-int64-to-void.sil b/test/IRGen/async/run-call-structinstance-int64-to-void.sil index 612cbdb9c4b44..70bd7a4f47fd1 100644 --- a/test/IRGen/async/run-call-structinstance-int64-to-void.sil +++ b/test/IRGen/async/run-call-structinstance-int64-to-void.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib import Builtin diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing.sil index a2340dcb89146..5b0948212db96 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil index b8cb7270ebc1e..4a059a377a18a 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib import Builtin diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil index de2055a6f1756..a5b5fe01b403b 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil index 4d45943257fa6..2d437953436ea 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil index 64a5a42098f99..2e3fa62c06303 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib diff --git a/test/IRGen/async/run-call-void-to-existential.sil b/test/IRGen/async/run-call-void-to-existential.sil index 24cbcc6f91b8d..c7c15d72a208b 100644 --- a/test/IRGen/async/run-call-void-to-existential.sil +++ b/test/IRGen/async/run-call-void-to-existential.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib diff --git a/test/IRGen/async/run-call-void-to-int64-and-int64.sil b/test/IRGen/async/run-call-void-to-int64-and-int64.sil index 27a5ae1767ec6..f60833c102072 100644 --- a/test/IRGen/async/run-call-void-to-int64-and-int64.sil +++ b/test/IRGen/async/run-call-void-to-int64-and-int64.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib diff --git a/test/IRGen/async/run-call-void-to-int64.sil b/test/IRGen/async/run-call-void-to-int64.sil index 48efd5e337cc0..559868d71649a 100644 --- a/test/IRGen/async/run-call-void-to-int64.sil +++ b/test/IRGen/async/run-call-void-to-int64.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib diff --git a/test/IRGen/async/run-call-void-to-struct_large.sil b/test/IRGen/async/run-call-void-to-struct_large.sil index 006355b21a1f5..c554823ce16f6 100644 --- a/test/IRGen/async/run-call-void-to-struct_large.sil +++ b/test/IRGen/async/run-call-void-to-struct_large.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib import Builtin diff --git a/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil b/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil index 796e25df19519..d8c3c4c050da1 100644 --- a/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil +++ b/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib import Builtin diff --git a/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil b/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil index 99b78463e333e..f3cf6ebc3cc0c 100644 --- a/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil +++ b/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil @@ -8,6 +8,7 @@ // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib import Builtin diff --git a/test/IRGen/ptrauth-actor.swift b/test/IRGen/ptrauth-actor.swift index 4cd1f44ccb567..6bb2c23d8ed39 100644 --- a/test/IRGen/ptrauth-actor.swift +++ b/test/IRGen/ptrauth-actor.swift @@ -5,7 +5,6 @@ // REQUIRES: CPU=arm64e // REQUIRES: OS=macosx -import _Concurrency import Swift public actor class A1 { diff --git a/test/ModuleInterface/actor_isolation.swift b/test/ModuleInterface/actor_isolation.swift index dd565f4fc4bfe..018f802376026 100644 --- a/test/ModuleInterface/actor_isolation.swift +++ b/test/ModuleInterface/actor_isolation.swift @@ -5,7 +5,6 @@ // RUN: %FileCheck %s --check-prefix FROMMODULE --check-prefix CHECK < %t/TestFromModule.swiftinterface // REQUIRES: concurrency -import _Concurrency // CHECK: actor public class SomeActor public actor class SomeActor { diff --git a/test/ModuleInterface/concurrency.swift b/test/ModuleInterface/concurrency.swift index 27c5b55ac5d7d..a43116bd644ce 100644 --- a/test/ModuleInterface/concurrency.swift +++ b/test/ModuleInterface/concurrency.swift @@ -1,6 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -typecheck -enable-library-evolution -enable-experimental-concurrency -emit-module-interface-path %t/Library.swiftinterface -DLIBRARY -module-name Library %s +// REQUIRES: concurrency + #if LIBRARY public func fn() async { fatalError() diff --git a/test/Parse/async-syntax.swift b/test/Parse/async-syntax.swift index e4e3fc1c59867..340c00aa71403 100644 --- a/test/Parse/async-syntax.swift +++ b/test/Parse/async-syntax.swift @@ -1,5 +1,7 @@ // RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -verify-syntax-tree +// REQUIRES: concurrency + func asyncGlobal1() async { } func asyncGlobal2() async throws { } diff --git a/test/Parse/async.swift b/test/Parse/async.swift index 657048a27aec6..70c47f85a18e1 100644 --- a/test/Parse/async.swift +++ b/test/Parse/async.swift @@ -1,5 +1,7 @@ // RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// REQUIRES: concurrency + // Parsing function declarations with 'async' func asyncGlobal1() async { } func asyncGlobal2() async throws { } diff --git a/test/PrintAsObjC/async.swift b/test/PrintAsObjC/async.swift index bfa69136a9e54..6de2c30df46f6 100644 --- a/test/PrintAsObjC/async.swift +++ b/test/PrintAsObjC/async.swift @@ -9,6 +9,7 @@ // RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/AppKit.swift // FIXME: END -enable-source-import hackaround +// REQUIRES: concurrency // RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -parse-as-library %s -typecheck -I %S/Inputs/custom-modules -emit-objc-header-path %t/async.h -import-objc-header %S/../Inputs/empty.h -enable-experimental-concurrency -typecheck // RUN: %FileCheck %s < %t/async.h diff --git a/test/Runtime/demangleToMetadata.swift b/test/Runtime/demangleToMetadata.swift index 49a034a94850e..61630ff98724f 100644 --- a/test/Runtime/demangleToMetadata.swift +++ b/test/Runtime/demangleToMetadata.swift @@ -3,6 +3,7 @@ // RUN: %target-codesign %t/a.out // RUN: %target-run %t/a.out // REQUIRES: executable_test +// REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib import Swift diff --git a/test/SILGen/synthesized_conformance_actor.swift b/test/SILGen/synthesized_conformance_actor.swift index 365b3fee56e92..7abfd2c0fcd8f 100644 --- a/test/SILGen/synthesized_conformance_actor.swift +++ b/test/SILGen/synthesized_conformance_actor.swift @@ -1,8 +1,6 @@ // RUN: %target-swift-frontend -emit-silgen %s -swift-version 5 -enable-experimental-concurrency | %FileCheck -check-prefix CHECK %s // REQUIRES: concurrency -import _Concurrency - public protocol DefaultInit { init() } diff --git a/test/Serialization/async.swift b/test/Serialization/async.swift index cbf1019a3cdc8..ee56ec7288d35 100644 --- a/test/Serialization/async.swift +++ b/test/Serialization/async.swift @@ -4,6 +4,8 @@ // RUN: %target-swift-frontend -merge-modules -emit-module -parse-as-library -enable-testing %t-scratch/def_async~partial.swiftmodule -module-name def_async -o %t/def_async.swiftmodule -enable-experimental-concurrency // RUN: %target-swift-frontend -typecheck -I%t -verify %s -verify-ignore-unknown -enable-experimental-concurrency +// REQUIRES: concurrency + import def_async func testDoSomethingBig() { diff --git a/test/Serialization/attr-actorindependent.swift b/test/Serialization/attr-actorindependent.swift index a7a26c7d6ad6f..a2a7809db1888 100644 --- a/test/Serialization/attr-actorindependent.swift +++ b/test/Serialization/attr-actorindependent.swift @@ -5,6 +5,8 @@ // RUN: %target-swift-frontend -enable-experimental-concurrency -emit-module-path %t/b.swiftmodule -module-name a %t/a.swiftmodule // RUN: cmp -s %t/a.swiftmodule %t/b.swiftmodule +// REQUIRES: concurrency + /////////// // This test checks for correct serialization & deserialization of // @actorIndependent and @actorIndependent(unsafe) @@ -15,6 +17,8 @@ // MODULE-CHECK-NEXT: @actorIndependent(unsafe) var storage: Int // MODULE-CHECK-NEXT: @actorIndependent var count: Int // MODULE-CHECK-NEXT: var actorCount: Int +// MODULE-CHECK-NEXT: @actorIndependent(unsafe) func enqueue(partialTask: PartialAsyncTask) +// MODULE-CHECK-NEXT: var $__actor_storage: _DefaultActorQueue // MODULE-CHECK-NEXT: init() // MODULE-CHECK-NEXT: } diff --git a/test/TypeDecoder/concurrency.swift b/test/TypeDecoder/concurrency.swift index db05054cfac2e..a4fdd14b7b905 100644 --- a/test/TypeDecoder/concurrency.swift +++ b/test/TypeDecoder/concurrency.swift @@ -5,6 +5,8 @@ // RUN: sed -ne '/\/\/ *DEMANGLE-TYPE: /s/\/\/ *DEMANGLE-TYPE: *//p' < %s > %t/input // RUN: %lldb-moduleimport-test-with-sdk %t/concurrency -type-from-mangled=%t/input | %FileCheck %s --check-prefix=CHECK-TYPE +// REQUIRES: concurrency + func blackHole(_: Any...) {} public var lookAtMeeee: [(Int) async -> Void] = [] diff --git a/test/attr/actorindependent.swift b/test/attr/actorindependent.swift index 944355f331adc..157be2381bff3 100644 --- a/test/attr/actorindependent.swift +++ b/test/attr/actorindependent.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend -typecheck -verify %s -enable-experimental-concurrency +// REQUIRES: concurrency + // expected-error@+1{{'@actorIndependent' can only be applied to actor members and global/static variables}} @actorIndependent func globalFunction() { } diff --git a/test/attr/actorindependent_unsafe.swift b/test/attr/actorindependent_unsafe.swift index b33da2ab0165e..ee5deedd6862a 100644 --- a/test/attr/actorindependent_unsafe.swift +++ b/test/attr/actorindependent_unsafe.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend -typecheck -verify %s -enable-experimental-concurrency +// REQUIRES: concurrency + ////////////////////////// /// Cases that only work because of @actorIndependent(unsafe) ////////////////////////// diff --git a/test/attr/asynchandler.swift b/test/attr/asynchandler.swift index ce5d9a022f5a6..269dcdb778062 100644 --- a/test/attr/asynchandler.swift +++ b/test/attr/asynchandler.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend -typecheck -verify %s -enable-experimental-concurrency +// REQUIRES: concurrency + func globalAsyncFunction() async -> Int { 0 } @asyncHandler func asyncHandler1() { diff --git a/test/attr/attr_cdecl_async.swift b/test/attr/attr_cdecl_async.swift index 2cf765e8da20f..77df8548d2a13 100644 --- a/test/attr/attr_cdecl_async.swift +++ b/test/attr/attr_cdecl_async.swift @@ -1,5 +1,7 @@ // RUN: %target-typecheck-verify-swift -enable-objc-interop -enable-experimental-concurrency +// REQUIRES: concurrency + @_cdecl("async") // expected-error{{@_cdecl functions cannot be asynchronous}} func asynchronous() async { } diff --git a/test/attr/global_actor.swift b/test/attr/global_actor.swift index 52fedc4515a62..4ca4551d1c3bb 100644 --- a/test/attr/global_actor.swift +++ b/test/attr/global_actor.swift @@ -1,8 +1,6 @@ // RUN: %target-swift-frontend -typecheck -verify %s -enable-experimental-concurrency // REQUIRES: concurrency -import _Concurrency - actor class SomeActor { } // ----------------------------------------------------------------------- diff --git a/test/decl/async/objc.swift b/test/decl/async/objc.swift index 96fba77b9826b..0db971f7e241d 100644 --- a/test/decl/async/objc.swift +++ b/test/decl/async/objc.swift @@ -1,6 +1,7 @@ // RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -typecheck -verify %s -swift-version 5 -enable-experimental-concurrency // RUN: %target-swift-ide-test -skip-deinit=false -print-ast-typechecked -source-filename %s -function-definitions=true -prefer-type-repr=false -print-implicit-attrs=true -explode-pattern-binding-decls=true -disable-objc-attr-requires-foundation-module -swift-version 5 -enable-experimental-concurrency | %FileCheck %s // REQUIRES: objc_interop +// REQUIRES: concurrency import Foundation import ObjectiveC diff --git a/test/decl/class/actor/basic.swift b/test/decl/class/actor/basic.swift index 8c74be18808ca..51af7e7035e94 100644 --- a/test/decl/class/actor/basic.swift +++ b/test/decl/class/actor/basic.swift @@ -1,5 +1,7 @@ // RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// REQUIRES: concurrency + actor class MyActor { } class MyActorSubclass1: MyActor { } diff --git a/test/decl/class/actor/conformance.swift b/test/decl/class/actor/conformance.swift index 8bd77ebacdc4e..84ace1dd3c698 100644 --- a/test/decl/class/actor/conformance.swift +++ b/test/decl/class/actor/conformance.swift @@ -1,5 +1,7 @@ // RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// REQUIRES: concurrency + protocol AsyncProtocol { func asyncMethod() async -> Int } diff --git a/test/decl/class/actor/global_actor_conformance.swift b/test/decl/class/actor/global_actor_conformance.swift index 9bbdf4196a074..16df77e218fcb 100644 --- a/test/decl/class/actor/global_actor_conformance.swift +++ b/test/decl/class/actor/global_actor_conformance.swift @@ -1,8 +1,6 @@ // RUN: %target-typecheck-verify-swift -enable-experimental-concurrency // REQUIRES: concurrency -import _Concurrency - actor class SomeActor { } @globalActor diff --git a/test/decl/func/async.swift b/test/decl/func/async.swift index beeb27cdaf76d..53a20f0a395a9 100644 --- a/test/decl/func/async.swift +++ b/test/decl/func/async.swift @@ -1,5 +1,7 @@ // RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// REQUIRES: concurrency + // Redeclaration checking func redecl1() async { } // expected-note{{previously declared here}} func redecl1() async throws { } // expected-error{{invalid redeclaration of 'redecl1()'}} diff --git a/test/decl/protocol/conforms/objc_async.swift b/test/decl/protocol/conforms/objc_async.swift index 8284b0e00564f..5802eb9fa499a 100644 --- a/test/decl/protocol/conforms/objc_async.swift +++ b/test/decl/protocol/conforms/objc_async.swift @@ -1,6 +1,7 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -enable-experimental-concurrency %s -verify -verify-ignore-unknown // REQUIRES: objc_interop +// REQUIRES: concurrency import Foundation import ObjCConcurrency diff --git a/test/decl/protocol/special/Actor.swift b/test/decl/protocol/special/Actor.swift index b6b0efb547d23..ce5ed56e512bc 100644 --- a/test/decl/protocol/special/Actor.swift +++ b/test/decl/protocol/special/Actor.swift @@ -2,7 +2,6 @@ // REQUIRES: concurrency // Synthesis of for actor classes. -import _Concurrency actor class A1 { var x: Int = 17 diff --git a/test/expr/unary/async_await.swift b/test/expr/unary/async_await.swift index be9fcee0e1dad..87a6641cdab63 100644 --- a/test/expr/unary/async_await.swift +++ b/test/expr/unary/async_await.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend -typecheck -verify %s -enable-experimental-concurrency -verify-syntax-tree +// REQUIRES: concurrency + func test1(asyncfp : () async -> Int, fp : () -> Int) async { _ = await asyncfp() _ = await asyncfp() + asyncfp() diff --git a/validation-test/compiler_crashers_2_fixed/rdar70144083.swift b/validation-test/compiler_crashers_2_fixed/rdar70144083.swift index 39566bfbe31a6..ce431f2497f66 100644 --- a/validation-test/compiler_crashers_2_fixed/rdar70144083.swift +++ b/validation-test/compiler_crashers_2_fixed/rdar70144083.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend -emit-ir %s -enable-experimental-concurrency +// REQUIRES: concurrency + public protocol AsyncIteratorProtocol { associatedtype Element associatedtype Failure: Error From b717c7d82351d386402e8c8aad91139e1a20e0a4 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 16 Oct 2020 00:04:21 -0400 Subject: [PATCH 631/745] Prepare for a more real task-local alloocator implementation. --- stdlib/public/Concurrency/TaskAlloc.cpp | 39 +++++++++++++++++++++++-- stdlib/public/Concurrency/TaskPrivate.h | 32 ++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 stdlib/public/Concurrency/TaskPrivate.h diff --git a/stdlib/public/Concurrency/TaskAlloc.cpp b/stdlib/public/Concurrency/TaskAlloc.cpp index 939670337d5a1..1f4d3d0656025 100644 --- a/stdlib/public/Concurrency/TaskAlloc.cpp +++ b/stdlib/public/Concurrency/TaskAlloc.cpp @@ -17,15 +17,50 @@ // //===----------------------------------------------------------------------===// +#include "TaskPrivate.h" #include "swift/Runtime/Concurrency.h" #include using namespace swift; +namespace { + +class TaskAllocator { +public: + void *alloc(size_t size) { + return malloc(size); + } + + void dealloc(void *ptr) { + free(ptr); + } +}; + +static_assert(sizeof(TaskAllocator) <= sizeof(AsyncTask::AllocatorPrivate), + "task allocator must fit in allocator-private slot"); + +static_assert(alignof(TaskAllocator) <= alignof(decltype(AsyncTask::AllocatorPrivate)), + "task allocator must not be more aligned than " + "allocator-private slot"); + +} // end anonymous namespace + +void swift::_swift_task_alloc_initialize(AsyncTask *task) { + new (task->AllocatorPrivate) TaskAllocator(); +} + +static TaskAllocator &allocator(AsyncTask *task) { + return reinterpret_cast(task->AllocatorPrivate); +} + +void swift::_swift_task_alloc_destroy(AsyncTask *task) { + allocator(task).~TaskAllocator(); +} + void *swift::swift_task_alloc(AsyncTask *task, size_t size) { - return malloc(size); + return allocator(task).alloc(size); } void swift::swift_task_dealloc(AsyncTask *task, void *ptr) { - free(ptr); + return allocator(task).dealloc(ptr); } diff --git a/stdlib/public/Concurrency/TaskPrivate.h b/stdlib/public/Concurrency/TaskPrivate.h new file mode 100644 index 0000000000000..13d130ff44a77 --- /dev/null +++ b/stdlib/public/Concurrency/TaskPrivate.h @@ -0,0 +1,32 @@ +//===--- TaskPrivate.h - Concurrency library internal interface -*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Internal functions for the concurrency library. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_CONCURRENCY_TASKPRIVATE_H +#define SWIFT_CONCURRENCY_TASKPRIVATE_H + +namespace swift { + +class AsyncTask; + +/// Initialize the task-local allocator in the given task. +void _swift_task_alloc_initialize(AsyncTask *task); + +/// Destsroy the task-local allocator in the given task. +void _swift_task_alloc_destroy(AsyncTask *task); + +} // end namespace swift + +#endif From c31dab2fe332fc93ef3c6344b3b9068da9ef341d Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 16 Oct 2020 00:49:53 -0400 Subject: [PATCH 632/745] Add a missing `inline`. --- include/swift/ABI/TaskStatus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/ABI/TaskStatus.h b/include/swift/ABI/TaskStatus.h index 55b48c0675bba..f6ff0559fcf6b 100644 --- a/include/swift/ABI/TaskStatus.h +++ b/include/swift/ABI/TaskStatus.h @@ -82,7 +82,7 @@ class TaskStatusRecord { } }; -TaskStatusRecord * +inline TaskStatusRecord * ActiveTaskStatus::getStatusRecordParent(TaskStatusRecord *ptr) { return ptr->getParent(); } From 76d8f03ba4541ac01374cfb4e15bda1733c132a6 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 16 Oct 2020 01:40:29 -0400 Subject: [PATCH 633/745] Make the task allocator verify stack discipline in the laziest possible way. --- stdlib/public/Concurrency/TaskAlloc.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/stdlib/public/Concurrency/TaskAlloc.cpp b/stdlib/public/Concurrency/TaskAlloc.cpp index 1f4d3d0656025..37858b9312e80 100644 --- a/stdlib/public/Concurrency/TaskAlloc.cpp +++ b/stdlib/public/Concurrency/TaskAlloc.cpp @@ -19,19 +19,33 @@ #include "TaskPrivate.h" #include "swift/Runtime/Concurrency.h" +#include "swift/Runtime/Debug.h" #include +#include using namespace swift; namespace { class TaskAllocator { + // Just keep track of all allocations in a vector so that we can + // verify stack discipline. We should make sure the allocator + // implementation strictly verifies allocation order at least + // until we've stabilized the compiler implementation. + std::vector Allocations; + public: void *alloc(size_t size) { - return malloc(size); + void *ptr = malloc(size); + Allocations.push_back(ptr); + return ptr; } void dealloc(void *ptr) { + if (Allocations.empty() || Allocations.back() != ptr) + fatalError(0, "pointer was not the last allocation on this task"); + + Allocations.pop_back(); free(ptr); } }; @@ -50,7 +64,14 @@ void swift::_swift_task_alloc_initialize(AsyncTask *task) { } static TaskAllocator &allocator(AsyncTask *task) { - return reinterpret_cast(task->AllocatorPrivate); + if (task) + return reinterpret_cast(task->AllocatorPrivate); + + // FIXME: this fall-back shouldn't be necessary, but it's useful + // for now, since the current execution tests aren't setting up a task + // properly. + static TaskAllocator global; + return global; } void swift::_swift_task_alloc_destroy(AsyncTask *task) { From a06d18ce814879d56bc9b249508fc9da3e3c0543 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 21 Oct 2020 15:59:38 -0400 Subject: [PATCH 634/745] Add API for creating unscheduled tasks. Make all tasks into heap objects. --- include/swift/ABI/MetadataKind.def | 4 + include/swift/ABI/MetadataValues.h | 12 +- include/swift/ABI/Task.h | 115 ++++++++++++++----- include/swift/Runtime/Concurrency.h | 28 +++++ stdlib/public/Concurrency/CMakeLists.txt | 1 + stdlib/public/Concurrency/Task.cpp | 134 +++++++++++++++++++++++ unittests/runtime/TaskStatus.cpp | 55 ++++++---- 7 files changed, 292 insertions(+), 57 deletions(-) create mode 100644 stdlib/public/Concurrency/Task.cpp diff --git a/include/swift/ABI/MetadataKind.def b/include/swift/ABI/MetadataKind.def index 181292a0bcfb2..27d7e93e37c5d 100644 --- a/include/swift/ABI/MetadataKind.def +++ b/include/swift/ABI/MetadataKind.def @@ -85,6 +85,10 @@ METADATAKIND(HeapGenericLocalVariable, METADATAKIND(ErrorObject, 1 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate) +/// A heap-allocated simple task. +METADATAKIND(SimpleTask, + 2 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate) + // getEnumeratedMetadataKind assumes that all the enumerated values here // will be <= LastEnumeratedMetadataKind. diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index e3212c146807c..76c5fd75a3845 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -1914,9 +1914,8 @@ class JobFlags : public FlagSet { // Kind-specific flags. - Task_IsHeapObject = 24, - Task_IsChildTask = 25, - Task_IsFuture = 26 + Task_IsChildTask = 24, + Task_IsFuture = 25 }; explicit JobFlags(size_t bits) : FlagSet(bits) {} @@ -1933,9 +1932,6 @@ class JobFlags : public FlagSet { return getKind() == JobKind::Task; } - FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsHeapObject, - task_isHeapObject, - task_setIsHeapObject) FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsChildTask, task_isChildTask, task_setIsChildTask) @@ -2034,8 +2030,8 @@ class AsyncContextFlags : public FlagSet { /// allocated as part of the caller's context, or if the callee will /// be called multiple times. FLAGSET_DEFINE_FLAG_ACCESSORS(ShouldNotDeallocate, - shouldNotDeallocateInCaller, - setShouldNotDeallocateInCaller) + shouldNotDeallocateInCallee, + setShouldNotDeallocateInCallee) }; } // end namespace swift diff --git a/include/swift/ABI/Task.h b/include/swift/ABI/Task.h index fdd8465acd681..d5f1c822bd64c 100644 --- a/include/swift/ABI/Task.h +++ b/include/swift/ABI/Task.h @@ -17,6 +17,7 @@ #ifndef SWIFT_ABI_TASK_H #define SWIFT_ABI_TASK_H +#include "swift/Basic/RelativePointer.h" #include "swift/ABI/HeapObject.h" #include "swift/ABI/MetadataValues.h" #include "swift/Runtime/Config.h" @@ -32,7 +33,21 @@ class TaskStatusRecord; /// An ExecutorRef isn't necessarily just a pointer to an executor /// object; it may have other bits set. -using ExecutorRef = Executor *; +struct ExecutorRef { + Executor *Pointer; + + /// Get an executor ref that represents a lack of preference about + /// where execution resumes. This is only valid in continuations, + /// return contexts, and so on; it is not generally passed to + /// executing functions. + static ExecutorRef noPreference() { + return { nullptr }; + } + + bool operator==(ExecutorRef other) const { + return Pointer == other.Pointer; + } +}; using JobInvokeFunction = SWIFT_CC(swift) @@ -42,6 +57,33 @@ using TaskContinuationFunction = SWIFT_CC(swift) void (AsyncTask *, ExecutorRef, AsyncContext *); +template +struct AsyncFunctionTypeImpl; +template +struct AsyncFunctionTypeImpl { + // TODO: expand and include the arguments in the parameters. + using type = TaskContinuationFunction; +}; + +template +using AsyncFunctionType = typename AsyncFunctionTypeImpl::type; + +/// A "function pointer" for an async function. +/// +/// Eventually, this will always be signed with the data key +/// using a type-specific discriminator. +template +class AsyncFunctionPointer { +public: + /// The function to run. + RelativeDirectPointer, + /*nullable*/ false, + int32_t> Function; + + /// The expected size of the context. + uint32_t ExpectedContextSize; +}; + /// A schedulable job. class alignas(2 * alignof(void*)) Job { public: @@ -76,7 +118,7 @@ class alignas(2 * alignof(void*)) Job { } /// Run this job. - void run(Executor *currentExecutor); + void run(ExecutorRef currentExecutor); }; // The compiler will eventually assume these. @@ -128,7 +170,7 @@ class ActiveTaskStatus { /// An asynchronous task. Tasks are the analogue of threads for /// asynchronous functions: that is, they are a persistent identity /// for the overall async computation. -class AsyncTask : public Job { +class AsyncTask : public HeapObject, public Job { public: /// The context for resuming the job. When a task is scheduled /// as a job, the next continuation should be installed as the @@ -146,9 +188,10 @@ class AsyncTask : public Job { /// Reserved for the use of the task-local stack allocator. void *AllocatorPrivate[4]; - AsyncTask(JobFlags flags, TaskContinuationFunction *run, + AsyncTask(const HeapMetadata *metadata, JobFlags flags, + TaskContinuationFunction *run, AsyncContext *initialContext) - : Job(flags, run), + : HeapObject(metadata), Job(flags, run), ResumeContext(initialContext), Status(ActiveTaskStatus()) { assert(flags.isAsyncTask()); @@ -164,12 +207,6 @@ class AsyncTask : public Job { return Status.load(std::memory_order_relaxed).isCancelled(); } - bool isHeapObject() const { return Flags.task_isHeapObject(); } - HeapObject *heapObjectHeader() { - assert(isHeapObject()); - return reinterpret_cast(this) - 1; - } - /// A fragment of an async task structure that happens to be a child task. class ChildFragment { /// The parent task of this task. @@ -182,6 +219,8 @@ class AsyncTask : public Job { AsyncTask *NextChild = nullptr; public: + ChildFragment(AsyncTask *parent) : Parent(parent) {} + AsyncTask *getParent() const { return Parent; } @@ -191,6 +230,8 @@ class AsyncTask : public Job { } }; + bool isFuture() const { return Flags.task_isFuture(); } + bool hasChildFragment() const { return Flags.task_isChildTask(); } ChildFragment *childFragment() { assert(hasChildFragment()); @@ -205,7 +246,7 @@ class AsyncTask : public Job { }; // The compiler will eventually assume these. -static_assert(sizeof(AsyncTask) == 10 * sizeof(void*), +static_assert(sizeof(AsyncTask) == 12 * sizeof(void*), "AsyncTask size is wrong"); static_assert(alignof(AsyncTask) == 2 * alignof(void*), "AsyncTask alignment is wrong"); @@ -237,6 +278,9 @@ class alignas(MaximumAlignment) AsyncContext { TaskContinuationFunction * __ptrauth_swift_async_context_resume ResumeParent; + /// The executor that the parent needs to be resumed on. + ExecutorRef ResumeParentExecutor; + /// Flags describing this context. /// /// Note that this field is only 32 bits; any alignment padding @@ -245,31 +289,44 @@ class alignas(MaximumAlignment) AsyncContext { /// is of course interrupted by the YieldToParent field. AsyncContextFlags Flags; - // Fields following this point may not be valid in all instances - // of AsyncContext. - - /// The function to call to temporarily resume running in the - /// parent context temporarily. Generally this means a semantic - /// yield. Requires Flags.hasYieldFunction(). - TaskContinuationFunction * __ptrauth_swift_async_context_yield - YieldToParent; - - AsyncContext(AsyncContextFlags flags, - TaskContinuationFunction *resumeParent, - AsyncContext *parent) - : Parent(parent), ResumeParent(resumeParent), Flags(flags) {} - AsyncContext(AsyncContextFlags flags, TaskContinuationFunction *resumeParent, - TaskContinuationFunction *yieldToParent, + ExecutorRef resumeParentExecutor, AsyncContext *parent) - : Parent(parent), ResumeParent(resumeParent), Flags(flags), - YieldToParent(yieldToParent) {} + : Parent(parent), ResumeParent(resumeParent), + ResumeParentExecutor(resumeParentExecutor), + Flags(flags) {} AsyncContext(const AsyncContext &) = delete; AsyncContext &operator=(const AsyncContext &) = delete; }; +/// An async context that supports yielding. +class YieldingAsyncContext : public AsyncContext { +public: + /// The function to call to temporarily resume running in the + /// parent context. Generally this means a semantic yield. + TaskContinuationFunction * __ptrauth_swift_async_context_yield + YieldToParent; + + /// The executor that the parent context needs to be yielded to on. + ExecutorRef YieldToParentExecutor; + + YieldingAsyncContext(AsyncContextFlags flags, + TaskContinuationFunction *resumeParent, + ExecutorRef resumeParentExecutor, + TaskContinuationFunction *yieldToParent, + ExecutorRef yieldToParentExecutor, + AsyncContext *parent) + : AsyncContext(flags, resumeParent, resumeParentExecutor, parent), + YieldToParent(yieldToParent), + YieldToParentExecutor(yieldToParentExecutor) {} + + static bool classof(const AsyncContext *context) { + return context->Flags.getKind() == AsyncContextKind::Yielding; + } +}; + } // end namespace swift #endif diff --git a/include/swift/Runtime/Concurrency.h b/include/swift/Runtime/Concurrency.h index adc47f410572b..b72c22a0c0050 100644 --- a/include/swift/Runtime/Concurrency.h +++ b/include/swift/Runtime/Concurrency.h @@ -21,6 +21,34 @@ namespace swift { +struct AsyncTaskAndContext { + AsyncTask *Task; + AsyncContext *InitialContext; +}; + +/// Create a task object with no future which will run the given +/// function. +/// +/// The task is not yet scheduled. +/// +/// If a parent task is provided, flags.task_hasChildFragment() must +/// be true, and this must be called synchronously with the parent. +/// The parent is responsible for creating a ChildTaskStatusRecord. +/// TODO: should we have a single runtime function for creating a task +/// and doing this child task status record management? +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +AsyncTaskAndContext swift_task_create(JobFlags flags, + AsyncTask *parent, + const AsyncFunctionPointer *function); + +/// Create a task object with no future which will run the given +/// function. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +AsyncTaskAndContext swift_task_create_f(JobFlags flags, + AsyncTask *parent, + AsyncFunctionType *function, + size_t initialContextSize); + /// Allocate memory in a task. /// /// This must be called synchronously with the task. diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index 3a0b98edc5318..61df3b9d24622 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -13,6 +13,7 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB Actor.swift PartialAsyncTask.swift + Task.cpp TaskAlloc.cpp TaskStatus.cpp Mutex.cpp diff --git a/stdlib/public/Concurrency/Task.cpp b/stdlib/public/Concurrency/Task.cpp new file mode 100644 index 0000000000000..c163f9791fb47 --- /dev/null +++ b/stdlib/public/Concurrency/Task.cpp @@ -0,0 +1,134 @@ +//===--- Task.cpp - Task object and management ----------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Object management routines for asynchronous task objects. +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/Concurrency.h" +#include "swift/ABI/Task.h" +#include "swift/ABI/Metadata.h" +#include "swift/Runtime/HeapObject.h" +#include "TaskPrivate.h" + +using namespace swift; + +SWIFT_CC(swift) +static void destroySimpleTask(SWIFT_CONTEXT HeapObject *_obj) { + auto obj = static_cast(_obj); + + assert(!obj->isFuture()); + + // The task execution itself should always hold a reference to it, so + // if we get here, we know the task has finished running, which means + // swift_task_complete should have been run, which will have torn down + // the task-local allocator. There's actually nothing else to clean up + // here. + + free(obj); +} + +/// Heap metadata for a simple asynchronous task that does not +/// include a future. +static FullMetadata simpleTaskHeapMetadata = { + { + { + &destroySimpleTask + }, + { + /*value witness table*/ nullptr + } + }, + { + MetadataKind::SimpleTask + } +}; + +/// The function that we put in the context of a simple task +/// (one with no future) to handle the final return. +SWIFT_CC(swift) +static void completeTask(AsyncTask *task, ExecutorRef executor, + AsyncContext *context) { + // Tear down the task-local allocator immediately; there's no need + // to wait for the object to be destroyed. + _swift_task_alloc_destroy(task); + + // TODO: set something in the status? + // TODO: notify the parent somehow? + // TODO: remove this task from the child-task chain? + // TODO: notify tasks waiting on the future? + + // Release the task, balancing the retain that a running task + // has on itself. + swift_release(task); +} + +AsyncTaskAndContext +swift::swift_task_create(JobFlags flags, AsyncTask *parent, + const AsyncFunctionPointer *function) { + return swift_task_create_f(flags, parent, function->Function.get(), + function->ExpectedContextSize); +} + +AsyncTaskAndContext +swift::swift_task_create_f(JobFlags flags, AsyncTask *parent, + AsyncFunctionType *function, + size_t initialContextSize) { + assert(!flags.task_isFuture() && "function doesn't support creating futures"); + assert((parent != nullptr) == flags.task_isChildTask()); + + // Figure out the size of the header. + size_t headerSize = sizeof(AsyncTask); + if (parent) headerSize += sizeof(AsyncTask::ChildFragment); + + // Allocate the initial context together with the job. + // This means that we never get rid of this allocation. + size_t amountToAllocate = headerSize + initialContextSize; + + assert(amountToAllocate % MaximumAlignment == 0); + + void *allocation = malloc(amountToAllocate); + + AsyncContext *initialContext = + reinterpret_cast( + reinterpret_cast(allocation) + headerSize); + + // Initialize the task so that resuming it will run the given + // function on the initial context. + AsyncTask *task = + new(allocation) AsyncTask(&simpleTaskHeapMetadata, flags, + function, initialContext); + + // Initialize the child fragment if applicable. + // TODO: propagate information from the parent? + if (parent) { + auto childFragment = task->childFragment(); + new (childFragment) AsyncTask::ChildFragment(parent); + } + + // Configure the initial context. + // + // FIXME: if we store a null pointer here using the standard ABI for + // signed null pointers, then we'll have to authenticate context pointers + // as if they might be null, even though the only time they ever might + // be is the final hop. Store a signed null instead. + initialContext->Parent = nullptr; + initialContext->ResumeParent = &completeTask; + initialContext->ResumeParentExecutor = ExecutorRef::noPreference(); + initialContext->Flags = AsyncContextKind::Ordinary; + initialContext->Flags.setShouldNotDeallocateInCallee(true); + + // Initialize the task-local allocator. + _swift_task_alloc_initialize(task); + + return {task, initialContext}; +} diff --git a/unittests/runtime/TaskStatus.cpp b/unittests/runtime/TaskStatus.cpp index d4fb10c4ebcc9..bb73b6f89b8f4 100644 --- a/unittests/runtime/TaskStatus.cpp +++ b/unittests/runtime/TaskStatus.cpp @@ -27,13 +27,9 @@ using InvokeFunctionRef = using BodyFunctionRef = llvm::function_ref; -template struct ValueContext : AsyncContext, Storage { +template struct ValueContext : AsyncContext { + Storage Value; InvokeFunctionRef StoredInvokeFn; - ValueContext(JobFlags flags, InvokeFunctionRef invokeFn, - Storage &&value) - : AsyncContext(AsyncContextKind::Ordinary, nullptr, nullptr), - Storage(std::forward(value)), - StoredInvokeFn(invokeFn) {} }; // Disable template argument deduction. @@ -47,16 +43,33 @@ static void simpleTaskInvokeFunction(AsyncTask *task, ExecutorRef executor, AsyncContext *context) { auto valueContext = static_cast*>(context); valueContext->StoredInvokeFn(task, executor, valueContext); + + // Destroy the stored value. + valueContext->Value.T::~T(); + + // Return to finish off the task. + // In a normal situation we'd need to free the context, but here + // we know we're at the top level. + valueContext->ResumeParent(task, executor, valueContext->Parent); } template static void withSimpleTask(JobFlags flags, T &&value, undeduced> invokeFn, BodyFunctionRef body) { - TaskContinuationFunction *invokeFP = &simpleTaskInvokeFunction; - ValueContext initialContext(flags, invokeFn, std::forward(value)); - AsyncTask task(flags, invokeFP, &initialContext); - body(&task); + auto taskAndContext = + swift_task_create_f(flags, /*parent*/ nullptr, + &simpleTaskInvokeFunction, + sizeof(ValueContext)); + + auto valueContext = + static_cast*>(taskAndContext.InitialContext); + new (&valueContext->Value) T(std::forward(value)); + valueContext->StoredInvokeFn = invokeFn; + + // Forward our owning reference to the task into its execution, + // causing it to be destroyed when it completes. + body(taskAndContext.Task); } template @@ -67,7 +80,7 @@ static void withSimpleTask(T &&value, } static ExecutorRef createFakeExecutor(uintptr_t value) { - return reinterpret_cast(value); + return {reinterpret_cast(value)}; } } // end anonymous namespace @@ -83,28 +96,30 @@ TEST(TaskStatusTest, basicTasks) { ValueContext *context) { // The task passed in should be the task we created. EXPECT_EQ(createdTask, task); + + // The executor passed in should be the executor we created. EXPECT_EQ(createdExecutor, executor); - if (hasRun) { - EXPECT_EQ(94, context->value); - } else { - EXPECT_EQ(47, context->value); - context->value *= 2; - } + // We shouldn't have run yet. + EXPECT_FALSE(hasRun); - hasRun = true; + // The context should've been initialized correctly. + // (This is really just testing our harness, not the runtime.) + EXPECT_EQ(47, context->Value.value); + hasRun = true; }, [&](AsyncTask *task) { createdTask = task; EXPECT_FALSE(hasRun); createdTask->run(createdExecutor); EXPECT_TRUE(hasRun); - createdTask->run(createdExecutor); - EXPECT_TRUE(hasRun); + + createdTask = nullptr; }); EXPECT_TRUE(hasRun); + EXPECT_EQ(nullptr, createdTask); } TEST(TaskStatusTest, cancellation_simple) { From 8521453af39abae6fa29fbeaa474bcbbc64597ff Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 21 Oct 2020 21:57:30 -0700 Subject: [PATCH 635/745] [Concurrency] Drop "Asynchronously" suffix from imported 'async' methods. The "Asynchronously" is a needless word for an "async" function. Remove it. --- lib/Basic/StringExtras.cpp | 9 +++++++++ test/ClangImporter/objc_async.swift | 1 + .../clang-importer-sdk/usr/include/ObjCConcurrency.h | 3 ++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/Basic/StringExtras.cpp b/lib/Basic/StringExtras.cpp index 4810a0ca401d3..51f8970dffcce 100644 --- a/lib/Basic/StringExtras.cpp +++ b/lib/Basic/StringExtras.cpp @@ -1301,6 +1301,15 @@ bool swift::omitNeedlessWords(StringRef &baseName, splitBaseName(baseName, argNames[0], paramTypes[0], firstParamName)) anyChanges = true; + // For a method imported as "async", drop the "Asynchronously" suffix from + // the base name. It is redundant with 'async'. + const StringRef asynchronously = "Asynchronously"; + if (isAsync && camel_case::getLastWord(baseName) == asynchronously && + baseName.size() > asynchronously.size()) { + baseName = baseName.drop_back(asynchronously.size()); + anyChanges = true; + } + // Omit needless words based on parameter types. for (unsigned i = 0, n = argNames.size(); i != n; ++i) { // If there is no corresponding parameter, there is nothing to diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift index 018c3a64eee1e..8d2e691d49857 100644 --- a/test/ClangImporter/objc_async.swift +++ b/test/ClangImporter/objc_async.swift @@ -20,6 +20,7 @@ func testSlowServer(slowServer: SlowServer) async throws { // expected-error@-1{{call is 'async' but is not marked with 'await'}} let _: String? = await try slowServer.fortune() + let _: Int = await try slowServer.magicNumber(withSeed: 42) } func testSlowServerSynchronous(slowServer: SlowServer) { diff --git a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h index 5a4da7ccf0d45..b968a5561e7fe 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h @@ -10,7 +10,8 @@ -(void)findAnswerAsynchronously:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswer(completionHandler:)"))); -(BOOL)findAnswerFailinglyWithError:(NSError * _Nullable * _Nullable)error completion:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswerFailingly(completionHandler:)"))); -(void)doSomethingFun:(NSString *)operation then:(void (^)(void))completionHandler; --(void)getFortuneWithCompletionHandler:(void (^)(NSString *_Nullable, NSError * _Nullable))handler; +-(void)getFortuneAsynchronouslyWithCompletionHandler:(void (^)(NSString *_Nullable, NSError * _Nullable))handler; +-(void)getMagicNumberAsynchronouslyWithSeed:(NSInteger)seed completionHandler:(void (^)(NSInteger, NSError * _Nullable))handler; @property(readwrite) void (^completionHandler)(NSInteger); -(void)doSomethingConflicted:(NSString *)operation completionHandler:(void (^)(NSInteger))handler; From 051f4c41df712561601eb4c66066e9f938aa5397 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 21 Oct 2020 18:03:29 -0400 Subject: [PATCH 636/745] Sema: Improve comments based on code review feedback --- lib/Sema/TypeCheckAvailability.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index 153bedf733df4..9d23c4efbb80e 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -66,11 +66,13 @@ enum class ExportabilityReason : unsigned { }; /// A description of the restrictions on what declarations can be referenced -/// from a the signature or body of a declaration. +/// from the signature or body of a declaration. /// -/// We say a declaration is "exported" if it is `public` or -/// `@usableFromInline`, not `_@spi`, and not visible via an -/// `@_implementationOnly` import. +/// We say a declaration is "exported" if all of the following holds: +/// +/// - the declaration is `public` or `@usableFromInline` +/// - the declaration is not `@_spi` +/// - the declaration was not imported from an `@_implementationOnly` import /// /// The "signature" of a declaration is the set of all types written in the /// declaration (such as function parameter and return types), but not @@ -81,8 +83,8 @@ enum class ExportabilityReason : unsigned { /// /// The body of an inlinable function can only reference other `public` and /// `@usableFromInline` declarations; furthermore, if the inlinable -/// function is also exported, its body is restricted to referencing other -/// exported declarations. +/// function is not `@_spi`, its body can only reference other exported +/// declarations. /// /// The ExportContext also stores if the location in the program is inside /// of a function or type body with deprecated or unavailable availability. From 0f27312790af84e76b1fd53fc265482257fc9020 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 21 Oct 2020 00:33:54 -0400 Subject: [PATCH 637/745] Sema: Refactor TypeChecker::diagnoseInlinableDeclRef() --- lib/Sema/ResilienceDiagnostics.cpp | 38 +++++++----------------------- lib/Sema/TypeCheckAvailability.cpp | 18 +++++++------- lib/Sema/TypeCheckStmt.cpp | 4 ++-- lib/Sema/TypeChecker.h | 5 ++-- 4 files changed, 23 insertions(+), 42 deletions(-) diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index 38e08afd1a9ca..c8ad9b5dd37a3 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -27,44 +27,19 @@ using namespace swift; -bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc, - const ValueDecl *D, - ExportContext where) { - auto fragileKind = where.getFragileFunctionKind(); - if (fragileKind.kind == FragileFunctionKind::None) - return false; - - // Do some important fast-path checks that apply to all cases. - - // Type parameters are OK. - if (isa(D)) - return false; - - // Check whether the declaration is accessible. - if (diagnoseInlinableDeclRefAccess(loc, D, where)) - return true; - - // Check whether the declaration comes from a publically-imported module. - // Skip this check for accessors because the associated property or subscript - // will also be checked, and will provide a better error message. - if (!isa(D)) - if (diagnoseDeclRefExportability(loc, D, where)) - return true; - - return false; -} - bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, const ValueDecl *D, ExportContext where) { - auto *DC = where.getDeclContext(); auto fragileKind = where.getFragileFunctionKind(); - assert(fragileKind.kind != FragileFunctionKind::None); + if (fragileKind.kind == FragileFunctionKind::None) + return false; // Local declarations are OK. if (D->getDeclContext()->isLocalContext()) return false; + auto *DC = where.getDeclContext(); + // Public declarations or SPI used from SPI are OK. if (D->getFormalAccessScope(/*useDC=*/nullptr, fragileKind.allowUsableFromInline).isPublic() && @@ -145,6 +120,11 @@ bool TypeChecker::diagnoseDeclRefExportability(SourceLoc loc, const ValueDecl *D, ExportContext where) { + // Accessors cannot have exportability that's different than the storage, + // so skip them for now. + if (isa(D)) + return false; + if (!where.mustOnlyReferenceExportedDecls()) return false; diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index e9ae5c693301c..4ecb67ff75061 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -2716,6 +2716,10 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, return false; const ValueDecl *D = declRef.getDecl(); + // Generic parameters are always available. + if (isa(D)) + return false; + if (auto *attr = AvailableAttr::isUnavailable(D)) { if (diagnoseIncDecRemoval(D, R, attr)) return true; @@ -2733,14 +2737,12 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, return false; } - if (Where.getFragileFunctionKind().kind != FragileFunctionKind::None) { - if (R.isValid()) - if (TypeChecker::diagnoseInlinableDeclRef(R.Start, D, Where)) - return true; - } else if (Where.getExportabilityReason().hasValue()) { - if (R.isValid()) - if (TypeChecker::diagnoseDeclRefExportability(R.Start, D, Where)) - return true; + if (R.isValid()) { + if (TypeChecker::diagnoseInlinableDeclRefAccess(R.Start, D, Where)) + return true; + + if (TypeChecker::diagnoseDeclRefExportability(R.Start, D, Where)) + return true; } if (R.isValid()) { diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 51c0744e3c7ef..dfe330d236a00 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1696,8 +1696,8 @@ static bool checkSuperInit(ConstructorDecl *fromCtor, } // Make sure we can reference the designated initializer correctly. - TypeChecker::diagnoseInlinableDeclRef( - fromCtor->getLoc(), ctor, + diagnoseDeclAvailability( + ctor, fromCtor->getLoc(), ExportContext::forFunctionBody(fromCtor)); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 25b7524ea6e5b..ecea93a230fe6 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -947,11 +947,10 @@ DeclName getObjectLiteralConstructorName(ASTContext &ctx, /// we're parsing the standard library. ModuleDecl *getStdlibModule(const DeclContext *dc); -/// \name Resilience diagnostics -bool diagnoseInlinableDeclRef(SourceLoc loc, const ValueDecl *D, ExportContext where); - Expr *buildDefaultInitializer(Type type); +/// \name Resilience diagnostics + bool diagnoseInlinableDeclRefAccess(SourceLoc loc, const ValueDecl *D, ExportContext where); From b3dadc8973dd680d75f4ab110b60660c5811b6a3 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 21 Oct 2020 14:45:46 -0400 Subject: [PATCH 638/745] AST: Use VarDecl::isInitExposedToClients() from DeclContext::getFragileFunctionKind() getFragileFunctionKind() would report that all initializers in non-resilient public types were inlinable, including static properties. This was later patched by VarDecl::isInitExposedToClients(), which was checked in diagnoseInlinableDeclRefAccess(). However, the latter function only looked at the innermost DeclContexts, not all parent contexts, so it would incorrectly diagnose code with a nested DeclContext inside of a static property initializer. Fix this by changing getFragileFunctionKind() to call isInitExposedToClients() and simplifying diagnoseInlinableDeclRefAccess(). This commit also introduces a new isLayoutExposedToClients() method, which is similar to isInitExposedToClients(), except it also returns 'true' if the property does not have an initializer (and in fact the latter is implemented in terms of the former). --- include/swift/AST/Decl.h | 10 +++++++++- lib/AST/Decl.cpp | 21 ++++++++++++++++++--- lib/AST/DeclContext.cpp | 27 ++++++++++++--------------- lib/Sema/ResilienceDiagnostics.cpp | 8 -------- test/attr/attr_inlinable.swift | 13 +++++++++++++ 5 files changed, 52 insertions(+), 27 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index f53fff7dff473..4937bc125b34f 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -4763,11 +4763,19 @@ class VarDecl : public AbstractStorageDecl { /// Determines if this var has an initializer expression that should be /// exposed to clients. + /// /// There's a very narrow case when we would: if the decl is an instance /// member with an initializer expression and the parent type is /// @frozen and resides in a resilient module. bool isInitExposedToClients() const; - + + /// Determines if this var is exposed as part of the layout of a + /// @frozen struct. + /// + /// From the standpoint of access control and exportability checking, this + /// var will behave as if it was public, even if it is internal or private. + bool isLayoutExposedToClients() const; + /// Is this a special debugger variable? bool isDebuggerVar() const { return Bits.VarDecl.IsDebuggerVar; } void setDebuggerVar(bool IsDebuggerVar) { diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index f67316d4b4ce7..d1d0bb062bc38 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1582,12 +1582,27 @@ VarDecl *PatternBindingDecl::getAnchoringVarDecl(unsigned i) const { } bool VarDecl::isInitExposedToClients() const { + return hasInitialValue() && isLayoutExposedToClients(); +} + +bool VarDecl::isLayoutExposedToClients() const { auto parent = dyn_cast(getDeclContext()); if (!parent) return false; - if (!hasInitialValue()) return false; if (isStatic()) return false; - return parent->getAttrs().hasAttribute() || - parent->getAttrs().hasAttribute(); + + if (!hasStorage() && + !getAttrs().hasAttribute() && + !hasAttachedPropertyWrapper()) { + return false; + } + + auto nominalAccess = + parent->getFormalAccessScope(/*useDC=*/nullptr, + /*treatUsableFromInlineAsPublic=*/true); + if (!nominalAccess.isPublic()) return false; + + return (parent->getAttrs().hasAttribute() || + parent->getAttrs().hasAttribute()); } /// Check whether the given type representation will be diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index e0da769881d85..37792d2d5abf2 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -368,21 +368,17 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator, // Stored property initializer contexts use minimal resilience expansion // if the type is formally fixed layout. - if (isa(dc)) { - if (auto *NTD = dyn_cast(dc->getParent())) { - auto nominalAccess = - NTD->getFormalAccessScope(/*useDC=*/nullptr, - /*treatUsableFromInlineAsPublic=*/true); - if (!nominalAccess.isPublic()) - return {FragileFunctionKind::None, - /*allowUsableFromInline=*/false}; - - if (NTD->isFormallyResilient()) - return {FragileFunctionKind::None, - /*allowUsableFromInline=*/false}; - - return {FragileFunctionKind::PropertyInitializer, true}; + if (auto *init = dyn_cast (dc)) { + auto bindingIndex = init->getBindingIndex(); + if (auto *varDecl = init->getBinding()->getAnchoringVarDecl(bindingIndex)) { + if (varDecl->isInitExposedToClients()) { + return {FragileFunctionKind::PropertyInitializer, + /*allowUsableFromInline=*/true}; + } } + + return {FragileFunctionKind::None, + /*allowUsableFromInline=*/false}; } if (auto *AFD = dyn_cast(dc)) { @@ -434,7 +430,8 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator, } } - return {FragileFunctionKind::None, false}; + return {FragileFunctionKind::None, + /*allowUsableFromInline=*/false}; } /// Determine whether the innermost context is generic. diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index c8ad9b5dd37a3..4c943c7d4bf08 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -56,14 +56,6 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, return false; } - // Property initializers that are not exposed to clients are OK. - if (auto pattern = dyn_cast(DC)) { - auto bindingIndex = pattern->getBindingIndex(); - auto *varDecl = pattern->getBinding()->getAnchoringVarDecl(bindingIndex); - if (!varDecl->isInitExposedToClients()) - return false; - } - DowngradeToWarning downgradeToWarning = DowngradeToWarning::No; // Swift 4.2 did not perform any checks for type aliases. diff --git a/test/attr/attr_inlinable.swift b/test/attr/attr_inlinable.swift index 2ce90feff2374..2b8e785e8e60f 100644 --- a/test/attr/attr_inlinable.swift +++ b/test/attr/attr_inlinable.swift @@ -275,8 +275,21 @@ internal func internalIntReturningFunc() -> Int { return 0 } public struct PublicFixedStructWithInit { var x = internalGlobal // expected-error {{let 'internalGlobal' is internal and cannot be referenced from a property initializer in a '@frozen' type}} var y = publicGlobal // OK + + // Static property initializers are not inlinable contexts. static var z = privateIntReturningFunc() // OK static var a = internalIntReturningFunc() // OK + + // Test the same with a multi-statement closure, which introduces a + // new DeclContext. + static var zz: Int = { + let x = privateIntReturningFunc() + return x + }() + static var aa: Int = { + let x = internalIntReturningFunc() + return x + }() } public struct KeypathStruct { From 5f5372a3fca19e7fd9f67e79b7f9ddbc12e467fe Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 21 Oct 2020 17:38:54 -0400 Subject: [PATCH 639/745] Sema: Don't check SPI violations in diagnoseInlinableDeclRefAccess() There's no need to check for that here, because we also run diagnoseDeclRefExportability() on declarations referenced from inlinable code. This changes some diagnostics; we now produce the same diagnostic for references to SPI types in declaration signatures and for references to non-type SPI declarations in inlinable function bodies. Also note that the old inlinable reference diagnostic no longer has to handle the 'public' and 'open' access levels, which previously happened for '@_spi'; so I changed those entries in the %select to %error. --- include/swift/AST/DiagnosticsSema.def | 4 +- lib/Sema/ResilienceDiagnostics.cpp | 7 +- lib/Sema/TypeCheckAvailability.cpp | 54 ++++----- ...ntation_only_spi_import_exposability.swift | 6 +- test/SPI/local_spi_decls.swift | 38 +++---- test/SPI/spi_client.swift | 10 +- test/SPI/spi_members.swift | 105 +++++++++++++++++- 7 files changed, 153 insertions(+), 71 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index a292f6e771793..4ddc17810fe14 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5061,12 +5061,12 @@ ERROR(local_type_in_inlinable_function, (Identifier, unsigned)) ERROR(resilience_decl_unavailable, - none, DECL_OR_ACCESSOR "4 %1 is %select{private|fileprivate|internal|'@_spi'|'@_spi'}2 and " + none, DECL_OR_ACCESSOR "4 %1 is %select{private|fileprivate|internal|%error|%error}2 and " "cannot be referenced from " FRAGILE_FUNC_KIND "3", (DescriptiveDeclKind, DeclName, AccessLevel, unsigned, bool)) WARNING(resilience_decl_unavailable_warn, - none, DECL_OR_ACCESSOR "4 %1 is %select{private|fileprivate|internal|'@_spi'|'@_spi'}2 and " + none, DECL_OR_ACCESSOR "4 %1 is %select{private|fileprivate|internal|%error|%error}2 and " "should not be referenced from " FRAGILE_FUNC_KIND "3", (DescriptiveDeclKind, DeclName, AccessLevel, unsigned, bool)) diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index 4c943c7d4bf08..dfade1e1094aa 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -40,10 +40,11 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, auto *DC = where.getDeclContext(); - // Public declarations or SPI used from SPI are OK. + // Public declarations are OK, even if they're SPI or came from an + // implementation-only import. We'll diagnose exportability violations + // from diagnoseDeclRefExportability(). if (D->getFormalAccessScope(/*useDC=*/nullptr, - fragileKind.allowUsableFromInline).isPublic() && - !(D->isSPI() && !DC->getInnermostDeclarationDeclContext()->isSPI())) + fragileKind.allowUsableFromInline).isPublic()) return false; auto &Context = DC->getASTContext(); diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 4ecb67ff75061..09e10314b2421 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -148,14 +148,15 @@ static void forEachOuterDecl(DeclContext *DC, Fn fn) { } } -static void computeExportContextBits(Decl *D, - bool *implicit, bool *deprecated, +static void computeExportContextBits(ASTContext &Ctx, Decl *D, + bool *spi, bool *implicit, bool *deprecated, Optional *unavailablePlatformKind) { + if (D->isSPI()) + *spi = true; + if (D->isImplicit()) *implicit = true; - auto &Ctx = D->getASTContext(); - if (D->getAttrs().getDeprecated(Ctx)) *deprecated = true; @@ -166,39 +167,31 @@ static void computeExportContextBits(Decl *D, if (auto *PBD = dyn_cast(D)) { for (unsigned i = 0, e = PBD->getNumPatternEntries(); i < e; ++i) { if (auto *VD = PBD->getAnchoringVarDecl(i)) - computeExportContextBits(VD, implicit, deprecated, + computeExportContextBits(Ctx, VD, spi, implicit, deprecated, unavailablePlatformKind); } } } ExportContext ExportContext::forDeclSignature(Decl *D) { + auto &Ctx = D->getASTContext(); + + auto *DC = D->getInnermostDeclContext(); + auto fragileKind = DC->getFragileFunctionKind(); + + bool spi = false; bool implicit = false; bool deprecated = false; Optional unavailablePlatformKind; - computeExportContextBits(D, &implicit, &deprecated, + computeExportContextBits(Ctx, D, &spi, &implicit, &deprecated, &unavailablePlatformKind); forEachOuterDecl(D->getDeclContext(), [&](Decl *D) { - computeExportContextBits(D, &implicit, &deprecated, + computeExportContextBits(Ctx, D, + &spi, &implicit, &deprecated, &unavailablePlatformKind); }); - auto *DC = D->getInnermostDeclContext(); - auto fragileKind = DC->getFragileFunctionKind(); - - bool spi = D->isSPI(); - if (auto *PBD = dyn_cast(D)) { - for (unsigned i = 0, e = PBD->getNumPatternEntries(); i < e; ++i) { - if (auto *VD = PBD->getAnchoringVarDecl(i)) { - if (VD->isSPI()) { - spi = true; - break; - } - } - } - } - bool exported = ::isExported(D); return ExportContext(DC, fragileKind, spi, exported, implicit, deprecated, @@ -206,26 +199,23 @@ ExportContext ExportContext::forDeclSignature(Decl *D) { } ExportContext ExportContext::forFunctionBody(DeclContext *DC) { + auto &Ctx = DC->getASTContext(); + + auto fragileKind = DC->getFragileFunctionKind(); + + bool spi = false; bool implicit = false; bool deprecated = false; Optional unavailablePlatformKind; forEachOuterDecl(DC, [&](Decl *D) { - computeExportContextBits(D, &implicit, &deprecated, + computeExportContextBits(Ctx, D, + &spi, &implicit, &deprecated, &unavailablePlatformKind); }); - auto fragileKind = DC->getFragileFunctionKind(); - - bool spi = false; bool exported = false; - if (auto *D = DC->getInnermostDeclarationDeclContext()) { - spi = D->isSPI(); - } else { - assert(fragileKind.kind == FragileFunctionKind::None); - } - return ExportContext(DC, fragileKind, spi, exported, implicit, deprecated, unavailablePlatformKind); } diff --git a/test/SPI/implementation_only_spi_import_exposability.swift b/test/SPI/implementation_only_spi_import_exposability.swift index c4c230b901bad..c9e37b82d3a87 100644 --- a/test/SPI/implementation_only_spi_import_exposability.swift +++ b/test/SPI/implementation_only_spi_import_exposability.swift @@ -36,10 +36,10 @@ public struct PublicStruct : IOIProtocol, SPIProtocol { // expected-error {{cann @inlinable public func publicInlinable() { - spiFunc() // expected-error {{global function 'spiFunc()' is '@_spi' and cannot be referenced from an '@inlinable' function}} + spiFunc() // expected-error {{global function 'spiFunc()' cannot be used in an '@inlinable' function because 'Lib' was imported implementation-only}} ioiFunc() // expected-error {{global function 'ioiFunc()' cannot be used in an '@inlinable' function because 'Lib' was imported implementation-only}} - let _ = SPIStruct() // expected-error {{struct 'SPIStruct' is '@_spi' and cannot be referenced from an '@inlinable' function}} - // expected-error@-1 {{initializer 'init()' is '@_spi' and cannot be referenced from an '@inlinable' function}} + let _ = SPIStruct() // expected-error {{struct 'SPIStruct' cannot be used in an '@inlinable' function because 'Lib' was imported implementation-only}} + // expected-error@-1 {{initializer 'init()' cannot be used in an '@inlinable' function because 'Lib' was imported implementation-only}} let _ = IOIStruct() // expected-error {{struct 'IOIStruct' cannot be used in an '@inlinable' function because 'Lib' was imported implementation-only}} // expected-error@-1 {{initializer 'init()' cannot be used in an '@inlinable' function because 'Lib' was imported implementation-only}} } diff --git a/test/SPI/local_spi_decls.swift b/test/SPI/local_spi_decls.swift index cc8abe6baeb6c..6c531611de5a1 100644 --- a/test/SPI/local_spi_decls.swift +++ b/test/SPI/local_spi_decls.swift @@ -7,18 +7,14 @@ // RUN: %target-typecheck-verify-swift -I %t -verify-ignore-unknown -swift-version 5 // SPI declarations -@_spi(MySPI) public func spiFunc() {} // expected-note 2 {{global function 'spiFunc()' is not '@usableFromInline' or public}} +@_spi(MySPI) public func spiFunc() {} @_spi(+) public func invalidSPIName() {} // expected-error {{expected an SPI identifier as subject of the '@_spi' attribute}} @_spi(🤔) public func emojiNamedSPI() {} @_spi() public func emptyParensSPI() {} // expected-error {{expected an SPI identifier as subject of the '@_spi' attribute}} @_spi(set) public func keywordSPI() {} // expected-error {{expected an SPI identifier as subject of the '@_spi' attribute}} @_spi(S) public class SPIClass { // expected-note 6 {{type declared here}} - // expected-note@-1 3 {{class 'SPIClass' is not '@usableFromInline' or public}} - // expected-note@-2 {{class 'SPIClass' is not public}} public init() {} - // expected-note@-1 2 {{initializer 'init()' is not '@usableFromInline' or public}} - // expected-note@-2 {{initializer 'init()' is not public}} } class InternalClass {} // expected-note 2 {{type declared here}} private class PrivateClass {} // expected-note 2 {{type declared here}} @@ -31,28 +27,24 @@ public func useOfSPITypeInvalid() -> SPIClass { fatalError() } // expected-error @_spi(S) public func spiUseOfPrivateType(_ a: PrivateClass) { fatalError() } // expected-error{{function cannot be declared public because its parameter uses a private type}} @inlinable -func inlinable() -> SPIClass { // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}} - spiFunc() // expected-error {{global function 'spiFunc()' is '@_spi' and cannot be referenced from an '@inlinable' function}} - _ = SPIClass() // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}} - // expected-error@-1 {{initializer 'init()' is '@_spi' and cannot be referenced from an '@inlinable' function}} +func inlinable() -> SPIClass { // expected-error {{class 'SPIClass' cannot be used in an '@inlinable' function because it is SPI}} + spiFunc() // expected-error {{global function 'spiFunc()' cannot be used in an '@inlinable' function because it is SPI}} + _ = SPIClass() // expected-error {{class 'SPIClass' cannot be used in an '@inlinable' function because it is SPI}} + // expected-error@-1 {{initializer 'init()' cannot be used in an '@inlinable' function because it is SPI}} } -@_spi(S) public struct SPIStruct { // expected-note 2 {{struct 'SPIStruct' is not '@usableFromInline' or public}} -// expected-note@-1 2 {{type declared here}} - // FIXME: Misleading diagnostic here +@_spi(S) public struct SPIStruct { +// expected-note@-1 {{type declared here}} public init() {} - // expected-note@-1 2 {{initializer 'init()' is not '@usableFromInline' or public}} } @frozen public struct FrozenStruct { - @_spi(S) public var spiInFrozen = SPIStruct() // expected-error {{struct 'SPIStruct' is '@_spi' and cannot be referenced from a property initializer in a '@frozen' type}} + @_spi(S) public var spiInFrozen = SPIStruct() // expected-error@-1 {{stored property 'spiInFrozen' cannot be declared '@_spi' in a '@frozen' struct}} - // expected-error@-2 {{cannot use struct 'SPIStruct' here; it is SPI}} - // expected-error@-3 {{initializer 'init()' is '@_spi' and cannot be referenced from a property initializer in a '@frozen' type}} - var spiTypeInFrozen = SPIStruct() // expected-error {{struct 'SPIStruct' is '@_spi' and cannot be referenced from a property initializer in a '@frozen' type}} + var spiTypeInFrozen = SPIStruct() // expected-error {{struct 'SPIStruct' cannot be used in a property initializer in a '@frozen' type because it is SPI}} // expected-error@-1 {{cannot use struct 'SPIStruct' here; it is SPI}} - // expected-error@-2 {{initializer 'init()' is '@_spi' and cannot be referenced from a property initializer in a '@frozen' type}} + // expected-error@-2 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} private var spiTypeInFrozen1: SPIClass // expected-error {{cannot use class 'SPIClass' here; it is SPI}} } @@ -109,17 +101,17 @@ public struct NestedParent { } public func publicFuncWithDefaultValue(_ p: SPIClass = SPIClass()) {} // expected-error {{cannot use class 'SPIClass' here; it is SPI}} -// expected-error@-1 {{class 'SPIClass' is '@_spi' and cannot be referenced from a default argument value}} -// expected-error@-2 {{initializer 'init()' is '@_spi' and cannot be referenced from a default argument value}} +// expected-error@-1 {{class 'SPIClass' cannot be used in a default argument value because it is SPI}} +// expected-error@-2 {{initializer 'init()' cannot be used in a default argument value because it is SPI}} @_spi(S) public func spiFuncWithDefaultValue(_ p: SPIClass = SPIClass()) {} @inlinable public func inlinablePublic() { - spiFunc() // expected-error {{global function 'spiFunc()' is '@_spi' and cannot be referenced from an '@inlinable' function}} - let _ = SPIClass() // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}} - // expected-error@-1 {{initializer 'init()' is '@_spi' and cannot be referenced from an '@inlinable' function}} + spiFunc() // expected-error {{global function 'spiFunc()' cannot be used in an '@inlinable' function because it is SPI}} + let _ = SPIClass() // expected-error {{class 'SPIClass' cannot be used in an '@inlinable' function because it is SPI}} + // expected-error@-1 {{initializer 'init()' cannot be used in an '@inlinable' function because it is SPI}} } @_spi(S) diff --git a/test/SPI/spi_client.swift b/test/SPI/spi_client.swift index c3d803b8a722a..fbaf7fbaca9f3 100644 --- a/test/SPI/spi_client.swift +++ b/test/SPI/spi_client.swift @@ -53,11 +53,11 @@ public func publicUseOfSPI(param: SPIClass) -> SPIClass {} // expected-error 2{{ public func publicUseOfSPI2() -> [SPIClass] {} // expected-error {{cannot use class 'SPIClass' here; it is an SPI imported from 'SPIHelper'}} @inlinable -public func inlinable1() -> SPIClass { // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}} - spiFunc() // expected-error {{global function 'spiFunc()' is '@_spi' and cannot be referenced from an '@inlinable' function}} - _ = SPIClass() // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}} - // expected-error@-1 {{initializer 'init()' is '@_spi' and cannot be referenced from an '@inlinable' function}} - _ = [SPIClass]() // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}} +public func inlinable1() -> SPIClass { // expected-error {{class 'SPIClass' cannot be used in an '@inlinable' function because it is an SPI imported from 'SPIHelper'}} + spiFunc() // expected-error {{global function 'spiFunc()' cannot be used in an '@inlinable' function because it is an SPI imported from 'SPIHelper'}} + _ = SPIClass() // expected-error {{class 'SPIClass' cannot be used in an '@inlinable' function because it is an SPI imported from 'SPIHelper'}} + // expected-error@-1 {{initializer 'init()' cannot be used in an '@inlinable' function because it is an SPI imported from 'SPIHelper'}} + _ = [SPIClass]() // expected-error {{class 'SPIClass' cannot be used in an '@inlinable' function because it is an SPI imported from 'SPIHelper'}} } @_spi(S) diff --git a/test/SPI/spi_members.swift b/test/SPI/spi_members.swift index 45c70d9c88c5a..c4fd81431ae41 100644 --- a/test/SPI/spi_members.swift +++ b/test/SPI/spi_members.swift @@ -1,11 +1,110 @@ // RUN: %target-typecheck-verify-swift +@propertyWrapper +public struct Wrapper { + public init(wrappedValue: T) {} + + public var wrappedValue: T { fatalError() } +} + @_spi(Foo) -public class Bar {} +public class Bar { + // expected-note@-1 11{{type declared here}} + + public init() {} +} + +public struct Resilient { + public init() {} + + @_spi(Foo) public func method(_: Bar) {} + @_spi(Foo) public var computedProperty: Bar { Bar() } + + @_spi(Foo) public var storedProperty1: Bar + @_spi(Foo) public var storedProperty2 = Bar() + @_spi(Foo) public lazy var lazyProperty1 = Bar() + @_spi(Foo) public lazy var lazyProperty2: Bar = Bar() + @_spi(Foo) @Wrapper public var wrappedProperty1: Bar + @_spi(Foo) @Wrapper public var wrappedProperty2 = Bar() +} -public struct Foo { +@frozen public struct Good { public init() {} @_spi(Foo) public func method(_: Bar) {} - @_spi(Foo) public var property: Bar { Bar() } + @_spi(Foo) public var computedProperty: Bar { Bar() } + + @_spi(Foo) public var storedProperty1: Bar + // expected-error@-1 {{cannot use class 'Bar' here; it is SPI}} + // expected-error@-2 {{stored property 'storedProperty1' cannot be declared '@_spi' in a '@frozen' struct}} + + @_spi(Foo) public var storedProperty2 = Bar() + // expected-error@-1 {{stored property 'storedProperty2' cannot be declared '@_spi' in a '@frozen' struct}} + + @_spi(Foo) public lazy var lazyProperty1 = Bar() + @_spi(Foo) public lazy var lazyProperty2: Bar = Bar() + @_spi(Foo) @Wrapper public var wrappedProperty1: Bar + @_spi(Foo) @Wrapper public var wrappedProperty2 = Bar() +} + +@frozen public struct Bad { + public init() {} + + public func method(_: Bar) {} // expected-error {{cannot use class 'Bar' here; it is SPI}} + + public var storedProperty1: Bar + // expected-error@-1 {{cannot use class 'Bar' here; it is SPI}} + + public var storedProperty2 = Bar() + // expected-error@-1 {{cannot use class 'Bar' here; it is SPI}} + // expected-error@-2 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + // expected-error@-3 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + + public var computedProperty: Bar { Bar() } // expected-error {{cannot use class 'Bar' here; it is SPI}} + + public lazy var lazyProperty1 = Bar() // expected-error {{cannot use class 'Bar' here; it is SPI}} + // expected-error@-1 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + // expected-error@-2 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + + public lazy var lazyProperty2: Bar = Bar() // expected-error {{cannot use class 'Bar' here; it is SPI}} + // expected-error@-1 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + // expected-error@-2 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + + @Wrapper public var wrappedProperty1: Bar + // expected-error@-1 {{cannot use class 'Bar' here; it is SPI}} + + @Wrapper public var wrappedProperty2 = Bar() + // expected-error@-1 {{cannot use class 'Bar' here; it is SPI}} + // expected-error@-2 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + // expected-error@-3 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} +} + +@frozen public struct BadPrivate { + private init() {} + + private func method(_: Bar) {} + + private var storedProperty1: Bar + // expected-error@-1 {{cannot use class 'Bar' here; it is SPI}} + + private var storedProperty2 = Bar() + // expected-error@-1 {{cannot use class 'Bar' here; it is SPI}} + // expected-error@-2 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + // expected-error@-3 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + + private var computedProperty: Bar { Bar() } + + private lazy var lazyProperty1 = Bar() + // expected-error@-1 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + // expected-error@-2 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + + private lazy var lazyProperty2: Bar = Bar() + // expected-error@-1 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + // expected-error@-2 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + + @Wrapper private var wrappedProperty1: Bar + + @Wrapper private var wrappedProperty2 = Bar() + // expected-error@-1 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + // expected-error@-2 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} } From 0dffe5c3484e610eb23f289d34dfd461cea48aac Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 21 Oct 2020 21:55:22 -0400 Subject: [PATCH 640/745] Sema: Use VarDecl::isLayoutExposedToClients() when checking access in @frozen structs We require that all stored properties in a @frozen struct have public or @usableFromInline types, even if the property itself is not public. This is so that clients can correctly generate code to manipulate the @frozen struct. This check was only looking for bona-fide stored properties, and missing out looking at properties that have backing storage, namely 'lazy' properties and property wrappers. --- lib/Sema/TypeCheckAccess.cpp | 43 +++++++------------ .../attr_fixed_layout_property_wrapper.swift | 22 ++++++++++ 2 files changed, 37 insertions(+), 28 deletions(-) create mode 100644 test/attr/attr_fixed_layout_property_wrapper.swift diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index a3e6ab7c2ddef..ea764f86d2b02 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -1051,29 +1051,25 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, UNINTERESTING(Accessor) // Handled by the Var or Subscript. UNINTERESTING(OpaqueType) // Handled by the Var or Subscript. - /// If \p PBD declared stored instance properties in a fixed-contents struct, - /// return said struct. - static const StructDecl * - getFixedLayoutStructContext(const PatternBindingDecl *PBD) { - auto *parentStruct = dyn_cast(PBD->getDeclContext()); - if (!parentStruct) - return nullptr; - if (!(parentStruct->getAttrs().hasAttribute() || - parentStruct->getAttrs().hasAttribute()) || - PBD->isStatic() || !PBD->hasStorage()) { - return nullptr; - } - // We don't check for "in resilient modules" because there's no reason to - // write '@_fixedLayout' on a struct in a non-resilient module. - return parentStruct; + /// If \p VD's layout is exposed by a @frozen struct, return said struct. + /// + /// Stored instance properties in @frozen structs must always use + /// public/@usableFromInline types. In these cases, check the access against + /// the struct instead of the VarDecl, and customize the diagnostics. + static const ValueDecl * + getFixedLayoutStructContext(const VarDecl *VD) { + if (VD->isLayoutExposedToClients()) + return dyn_cast(VD->getDeclContext()); + + return nullptr; } /// \see visitPatternBindingDecl void checkNamedPattern(const NamedPattern *NP, - const ValueDecl *fixedLayoutStructContext, bool isTypeContext, const llvm::DenseSet &seenVars) { const VarDecl *theVar = NP->getDecl(); + auto *fixedLayoutStructContext = getFixedLayoutStructContext(theVar); if (!fixedLayoutStructContext && shouldSkipChecking(theVar)) return; // Only check individual variables if we didn't check an enclosing @@ -1101,7 +1097,6 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, /// \see visitPatternBindingDecl void checkTypedPattern(const TypedPattern *TP, - const ValueDecl *fixedLayoutStructContext, bool isTypeContext, llvm::DenseSet &seenVars) { // FIXME: We need an access level to check against, so we pull one out @@ -1114,6 +1109,7 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, }); if (!anyVar) return; + auto *fixedLayoutStructContext = getFixedLayoutStructContext(anyVar); if (!fixedLayoutStructContext && shouldSkipChecking(anyVar)) return; @@ -1154,27 +1150,18 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, void visitPatternBindingDecl(PatternBindingDecl *PBD) { bool isTypeContext = PBD->getDeclContext()->isTypeContext(); - // Stored instance properties in public/@usableFromInline fixed-contents - // structs in resilient modules must always use public/@usableFromInline - // types. In these cases, check the access against the struct instead of the - // VarDecl, and customize the diagnostics. - const ValueDecl *fixedLayoutStructContext = - getFixedLayoutStructContext(PBD); - llvm::DenseSet seenVars; for (auto idx : range(PBD->getNumPatternEntries())) { PBD->getPattern(idx)->forEachNode([&](const Pattern *P) { if (auto *NP = dyn_cast(P)) { - checkNamedPattern(NP, fixedLayoutStructContext, isTypeContext, - seenVars); + checkNamedPattern(NP, isTypeContext, seenVars); return; } auto *TP = dyn_cast(P); if (!TP) return; - checkTypedPattern(TP, fixedLayoutStructContext, isTypeContext, - seenVars); + checkTypedPattern(TP, isTypeContext, seenVars); }); seenVars.clear(); } diff --git a/test/attr/attr_fixed_layout_property_wrapper.swift b/test/attr/attr_fixed_layout_property_wrapper.swift new file mode 100644 index 0000000000000..a2b4417e1da00 --- /dev/null +++ b/test/attr/attr_fixed_layout_property_wrapper.swift @@ -0,0 +1,22 @@ +// RUN: %target-typecheck-verify-swift -swift-version 5 + +private class PrivateType {} // expected-note {{class 'PrivateType' is not '@usableFromInline' or public}} +// expected-note@-1 {{initializer 'init()' is not '@usableFromInline' or public}} +// expected-note@-2 {{type declared here}} + +@propertyWrapper +public struct Wrapper { + public init(wrappedValue: T) {} + + public var wrappedValue: T { fatalError() } +} + +@frozen public struct UsesPrivateType { + @Wrapper private var y1: PrivateType + // expected-error@-1 {{type referenced from a stored property in a '@frozen' struct must be '@usableFromInline' or public}} + + @Wrapper private var y2 = PrivateType() + // expected-error@-1 {{class 'PrivateType' is private and cannot be referenced from a property initializer in a '@frozen' type}} + // expected-error@-2 {{initializer 'init()' is private and cannot be referenced from a property initializer in a '@frozen' type}} + // expected-error@-3 {{type referenced from a stored property with inferred type 'PrivateType' in a '@frozen' struct must be '@usableFromInline' or public}} +} From b4c568e2d1256d834c9bda5f9306c50a20c8ea63 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 21 Oct 2020 21:57:47 -0400 Subject: [PATCH 641/745] Sema: Use VarDecl::isLayoutExposedToClients() when checking if @_spi attribute is allowed We need to prohibit 'lazy' properties and property wrappers from being declared @_spi inside of a @frozen struct. Making them SPI doesn't make sense, because these properties will be omitted from the module interface and not taken into account when clients manipulate values of this type. --- lib/Sema/TypeCheckAttr.cpp | 11 ++++++----- test/SPI/spi_members.swift | 12 +++++++++++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 535c127bbdd8c..64ce157cbe64a 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -967,14 +967,15 @@ void AttributeChecker::visitSPIAccessControlAttr(SPIAccessControlAttr *attr) { } // Forbid stored properties marked SPI in frozen types. - if (auto property = dyn_cast(VD)) - if (auto DC = dyn_cast(D->getDeclContext())) - if (property->hasStorage() && - !DC->isFormallyResilient() && - !DC->isSPI()) + if (auto property = dyn_cast(VD)) { + if (auto NTD = dyn_cast(D->getDeclContext())) { + if (property->isLayoutExposedToClients() && !NTD->isSPI()) { diagnoseAndRemoveAttr(attr, diag::spi_attribute_on_frozen_stored_properties, VD->getName()); + } + } + } } } diff --git a/test/SPI/spi_members.swift b/test/SPI/spi_members.swift index c4fd81431ae41..8d43340205437 100644 --- a/test/SPI/spi_members.swift +++ b/test/SPI/spi_members.swift @@ -9,7 +9,7 @@ public struct Wrapper { @_spi(Foo) public class Bar { - // expected-note@-1 11{{type declared here}} + // expected-note@-1 12{{type declared here}} public init() {} } @@ -42,9 +42,19 @@ public struct Resilient { // expected-error@-1 {{stored property 'storedProperty2' cannot be declared '@_spi' in a '@frozen' struct}} @_spi(Foo) public lazy var lazyProperty1 = Bar() + // expected-error@-1 {{stored property 'lazyProperty1' cannot be declared '@_spi' in a '@frozen' struct}} + @_spi(Foo) public lazy var lazyProperty2: Bar = Bar() + // expected-error@-1 {{cannot use class 'Bar' here; it is SPI}} + // expected-error@-2 {{stored property 'lazyProperty2' cannot be declared '@_spi' in a '@frozen' struct}} + // expected-error@-3 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + // expected-error@-4 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + @_spi(Foo) @Wrapper public var wrappedProperty1: Bar + // expected-error@-1 {{stored property 'wrappedProperty1' cannot be declared '@_spi' in a '@frozen' struct}} + @_spi(Foo) @Wrapper public var wrappedProperty2 = Bar() + // expected-error@-1 {{stored property 'wrappedProperty2' cannot be declared '@_spi' in a '@frozen' struct}} } @frozen public struct Bad { From 01bc77406194d90b6288054e73caea6bb6d8b9b6 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 22 Oct 2020 00:20:44 -0400 Subject: [PATCH 642/745] Sema: Enforce that stored properties of @_fixed_layout classes only reference public types This is the same restriction as with @frozen structs. Even though classes are lowered as a single reference and clients don't usually manipulate their stored property layout directly, @frozen classes can have @inlinable designated initializers. --- lib/Sema/TypeCheckAccess.cpp | 7 ++++--- test/attr/attr_fixed_layout_class.swift | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 test/attr/attr_fixed_layout_class.swift diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index ea764f86d2b02..dd6cc34657844 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -1051,15 +1051,16 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, UNINTERESTING(Accessor) // Handled by the Var or Subscript. UNINTERESTING(OpaqueType) // Handled by the Var or Subscript. - /// If \p VD's layout is exposed by a @frozen struct, return said struct. + /// If \p VD's layout is exposed by a @frozen struct or class, return said + /// struct or class. /// - /// Stored instance properties in @frozen structs must always use + /// Stored instance properties in @frozen structs and classes must always use /// public/@usableFromInline types. In these cases, check the access against /// the struct instead of the VarDecl, and customize the diagnostics. static const ValueDecl * getFixedLayoutStructContext(const VarDecl *VD) { if (VD->isLayoutExposedToClients()) - return dyn_cast(VD->getDeclContext()); + return dyn_cast(VD->getDeclContext()); return nullptr; } diff --git a/test/attr/attr_fixed_layout_class.swift b/test/attr/attr_fixed_layout_class.swift new file mode 100644 index 0000000000000..72d6be8a56c26 --- /dev/null +++ b/test/attr/attr_fixed_layout_class.swift @@ -0,0 +1,18 @@ +// RUN: %target-typecheck-verify-swift -swift-version 5 + +private struct PrivateType {} +// expected-note@-1 {{struct 'PrivateType' is not '@usableFromInline' or public}} +// expected-note@-2 {{initializer 'init()' is not '@usableFromInline' or public}} +// expected-note@-3 {{type declared here}} + +@_fixed_layout public class UsesPrivateType { + private var y1: PrivateType + // expected-error@-1 {{type referenced from a stored property in a '@frozen' struct must be '@usableFromInline' or public}} + + private var y2 = PrivateType() + // expected-error@-1 {{struct 'PrivateType' is private and cannot be referenced from a property initializer in a '@frozen' type}} + // expected-error@-2 {{initializer 'init()' is private and cannot be referenced from a property initializer in a '@frozen' type}} + // expected-error@-3 {{type referenced from a stored property with inferred type 'PrivateType' in a '@frozen' struct must be '@usableFromInline' or public}} + + init() {} +} From fa50221d355ec383690c236dbe790116b36c0733 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 22 Oct 2020 00:24:14 -0400 Subject: [PATCH 643/745] Sema: Use VarDecl::isLayoutExposedToClients() when checking VarDecl exportability --- lib/Sema/TypeCheckAvailability.cpp | 17 ++++------------- test/SPI/spi_members.swift | 12 +++++++----- .../implementation-only-import-in-decls.swift | 9 ++++++--- test/Sema/spi-in-decls.swift | 9 ++++++--- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 09e10314b2421..810fd022fc0a7 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -67,19 +67,10 @@ bool swift::isExported(const ValueDecl *VD) { if (accessScope.isPublic()) return true; - // Is this a stored property in a non-resilient struct or class? - auto *property = dyn_cast(VD); - if (!property || !property->hasStorage() || property->isStatic()) - return false; - auto *parentNominal = dyn_cast(property->getDeclContext()); - if (!parentNominal || parentNominal->isResilient()) - return false; - - // Is that struct or class part of the module's API or ABI? - AccessScope parentAccessScope = parentNominal->getFormalAccessScope( - nullptr, /*treatUsableFromInlineAsPublic*/true); - if (parentAccessScope.isPublic()) - return true; + // Is this a stored property in a @frozen struct or class? + if (auto *property = dyn_cast(VD)) + if (property->isLayoutExposedToClients()) + return true; return false; } diff --git a/test/SPI/spi_members.swift b/test/SPI/spi_members.swift index 8d43340205437..6eca7ee7ec1ca 100644 --- a/test/SPI/spi_members.swift +++ b/test/SPI/spi_members.swift @@ -9,7 +9,7 @@ public struct Wrapper { @_spi(Foo) public class Bar { - // expected-note@-1 12{{type declared here}} + // expected-note@-1 16{{type declared here}} public init() {} } @@ -104,17 +104,19 @@ public struct Resilient { private var computedProperty: Bar { Bar() } - private lazy var lazyProperty1 = Bar() + private lazy var lazyProperty1 = Bar() // expected-error {{cannot use class 'Bar' here; it is SPI}} // expected-error@-1 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} // expected-error@-2 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} - private lazy var lazyProperty2: Bar = Bar() + private lazy var lazyProperty2: Bar = Bar() // expected-error {{cannot use class 'Bar' here; it is SPI}} // expected-error@-1 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} // expected-error@-2 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} @Wrapper private var wrappedProperty1: Bar + // expected-error@-1 {{cannot use class 'Bar' here; it is SPI}} @Wrapper private var wrappedProperty2 = Bar() - // expected-error@-1 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} - // expected-error@-2 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + // expected-error@-1 {{cannot use class 'Bar' here; it is SPI}} + // expected-error@-2 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} + // expected-error@-3 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} } diff --git a/test/Sema/implementation-only-import-in-decls.swift b/test/Sema/implementation-only-import-in-decls.swift index 0423ad246d4ca..17539f9357a62 100644 --- a/test/Sema/implementation-only-import-in-decls.swift +++ b/test/Sema/implementation-only-import-in-decls.swift @@ -135,33 +135,36 @@ precedencegroup TestHigherThan { higherThan: BadPrecedence // expected-error {{cannot use precedence group 'BadPrecedence' here; 'BADLibrary' has been imported as implementation-only}} } -public struct PublicStructStoredProperties { +@frozen public struct PublicStructStoredProperties { public var publiclyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} internal var internallyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} private var privatelyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} private let letIsLikeVar = [BadStruct]() // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} + // expected-error@-1 {{struct 'BadStruct' cannot be used in a property initializer in a '@frozen' type because 'BADLibrary' was imported implementation-only}} private var computedIsOkay: BadStruct? { return nil } // okay private static var staticIsOkay: BadStruct? // okay @usableFromInline internal var computedUFIIsNot: BadStruct? { return nil } // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} } -@usableFromInline internal struct UFIStructStoredProperties { +@frozen @usableFromInline internal struct UFIStructStoredProperties { @usableFromInline var publiclyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} internal var internallyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} private var privatelyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} private let letIsLikeVar = [BadStruct]() // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} + // expected-error@-1 {{struct 'BadStruct' cannot be used in a property initializer in a '@frozen' type because 'BADLibrary' was imported implementation-only}} private var computedIsOkay: BadStruct? { return nil } // okay private static var staticIsOkay: BadStruct? // okay @usableFromInline internal var computedUFIIsNot: BadStruct? { return nil } // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} } -public class PublicClassStoredProperties { +@_fixed_layout public class PublicClassStoredProperties { public var publiclyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} internal var internallyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} private var privatelyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} private let letIsLikeVar = [BadStruct]() // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} + // expected-error@-1 {{struct 'BadStruct' cannot be used in a property initializer in a '@frozen' type because 'BADLibrary' was imported implementation-only}} private var computedIsOkay: BadStruct? { return nil } // okay private static var staticIsOkay: BadStruct? // okay diff --git a/test/Sema/spi-in-decls.swift b/test/Sema/spi-in-decls.swift index 2d1e655d168d8..14d96f0032698 100644 --- a/test/Sema/spi-in-decls.swift +++ b/test/Sema/spi-in-decls.swift @@ -169,33 +169,36 @@ extension Array: TestConstrainedExtensionProto where Element == BadStruct { // e // higherThan: BadPrecedence //} -public struct PublicStructStoredProperties { +@frozen public struct PublicStructStoredProperties { public var publiclyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; it is SPI}} internal var internallyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; it is SPI}} private var privatelyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; it is SPI}} private let letIsLikeVar = [BadStruct]() // expected-error {{cannot use struct 'BadStruct' here; it is SPI}} + // expected-error@-1 {{struct 'BadStruct' cannot be used in a property initializer in a '@frozen' type because it is SPI}} private var computedIsOkay: BadStruct? { return nil } // okay private static var staticIsOkay: BadStruct? // okay @usableFromInline internal var computedUFIIsNot: BadStruct? { return nil } // expected-error {{cannot use struct 'BadStruct' here; it is SPI}} } -@usableFromInline internal struct UFIStructStoredProperties { +@frozen @usableFromInline internal struct UFIStructStoredProperties { @usableFromInline var publiclyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; it is SPI}} internal var internallyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; it is SPI}} private var privatelyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; it is SPI}} private let letIsLikeVar = [BadStruct]() // expected-error {{cannot use struct 'BadStruct' here; it is SPI}} + // expected-error@-1 {{struct 'BadStruct' cannot be used in a property initializer in a '@frozen' type because it is SPI}} private var computedIsOkay: BadStruct? { return nil } // okay private static var staticIsOkay: BadStruct? // okay @usableFromInline internal var computedUFIIsNot: BadStruct? { return nil } // expected-error {{cannot use struct 'BadStruct' here; it is SPI}} } -public class PublicClassStoredProperties { +@_fixed_layout public class PublicClassStoredProperties { public var publiclyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; it is SPI}} internal var internallyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; it is SPI}} private var privatelyBad: BadStruct? // expected-error {{cannot use struct 'BadStruct' here; it is SPI}} private let letIsLikeVar = [BadStruct]() // expected-error {{cannot use struct 'BadStruct' here; it is SPI}} + // expected-error@-1 {{struct 'BadStruct' cannot be used in a property initializer in a '@frozen' type because it is SPI}} private var computedIsOkay: BadStruct? { return nil } // okay private static var staticIsOkay: BadStruct? // okay From 587a8dafb9d1ea8959c9721878e2f4c430697df0 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 22 Oct 2020 00:55:15 -0400 Subject: [PATCH 644/745] Sema: Don't forget to visit conformances introduced by non-generic types Even if a nominal type does not have its own generic parameters, we need to visit its conformances, because it might be defined in a constrained extension or have a 'where' clause of its own. --- lib/Sema/TypeCheckAvailability.cpp | 15 ++++++++++++++- .../implementation-only-import-in-decls.swift | 7 +++++++ test/Sema/spi-in-decls.swift | 7 +++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 810fd022fc0a7..517a0f44482b0 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -3070,7 +3070,20 @@ class ProblematicTypeFinder : public TypeDeclFinder { Action visitNominalType(NominalType *ty) override { visitTypeDecl(ty->getDecl()); - /// FIXME + // If some generic parameters are missing, don't check conformances. + if (ty->hasUnboundGenericType()) + return Action::Continue; + + // When the DeclContext parameter to getContextSubstitutionMap() + // is a protocol declaration, the receiver must be a concrete + // type, so it doesn't make sense to perform this check on + // protocol types. + if (isa(ty)) + return Action::Continue; + + ModuleDecl *useModule = Where.getDeclContext()->getParentModule(); + auto subs = ty->getContextSubstitutionMap(useModule, ty->getDecl()); + (void) diagnoseSubstitutionMapAvailability(Loc, subs, Where); return Action::Continue; } diff --git a/test/Sema/implementation-only-import-in-decls.swift b/test/Sema/implementation-only-import-in-decls.swift index 17539f9357a62..3583696735ca8 100644 --- a/test/Sema/implementation-only-import-in-decls.swift +++ b/test/Sema/implementation-only-import-in-decls.swift @@ -179,6 +179,13 @@ public struct NormalProtoAssocHolder { } public func testConformanceInBoundGeneric(_: NormalProtoAssocHolder) {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}} +public struct OuterGenericHolder { + public struct Nested where T : NormalProto { + public var value: T.Assoc + } +} +public func testConformanceInNestedNonGeneric(_: OuterGenericHolder.Nested) {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}} + public class SubclassOfNormalClass: NormalClass {} public func testInheritedConformance(_: NormalProtoAssocHolder) {} // expected-error {{cannot use conformance of 'NormalClass' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}} diff --git a/test/Sema/spi-in-decls.swift b/test/Sema/spi-in-decls.swift index 14d96f0032698..5a7bbd4b7ba75 100644 --- a/test/Sema/spi-in-decls.swift +++ b/test/Sema/spi-in-decls.swift @@ -213,6 +213,13 @@ public struct NormalProtoAssocHolder { } public func testConformanceInBoundGeneric(_: NormalProtoAssocHolder) {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; the conformance is declared as SPI}} +public struct OuterGenericHolder { + public struct Nested where T : NormalProto { + public var value: T.Assoc + } +} +public func testConformanceInNestedNonGeneric(_: OuterGenericHolder.Nested) {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; the conformance is declared as SPI}} + public class SubclassOfNormalClass: NormalClass {} public func testInheritedConformance(_: NormalProtoAssocHolder) {} // expected-error {{cannot use conformance of 'NormalClass' to 'NormalProto' here; the conformance is declared as SPI}} From 8af4405f36e214cd6f6b1789431a5c505548e89b Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 22 Oct 2020 01:00:19 -0400 Subject: [PATCH 645/745] AST: 'lazy' property initializers are never @inlinable In @frozen structs, stored properties and property wrappers must have inlinable initial value expressions, since they are re-emitted into designated initializer bodies, which themselves might be @inlinable. However, 'lazy' properties don't follow this pattern; the initial value expression is emitted inside the getter, which is never @inlinable. --- lib/AST/Decl.cpp | 5 +++++ test/SPI/spi_members.swift | 10 ---------- test/attr/attr_inlinable.swift | 10 ++++++++++ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index d1d0bb062bc38..6471e5ce6e9a2 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1582,6 +1582,11 @@ VarDecl *PatternBindingDecl::getAnchoringVarDecl(unsigned i) const { } bool VarDecl::isInitExposedToClients() const { + // 'lazy' initializers are emitted inside the getter, which is never + // @inlinable. + if (getAttrs().hasAttribute()) + return false; + return hasInitialValue() && isLayoutExposedToClients(); } diff --git a/test/SPI/spi_members.swift b/test/SPI/spi_members.swift index 6eca7ee7ec1ca..cbf1728c0c80d 100644 --- a/test/SPI/spi_members.swift +++ b/test/SPI/spi_members.swift @@ -47,8 +47,6 @@ public struct Resilient { @_spi(Foo) public lazy var lazyProperty2: Bar = Bar() // expected-error@-1 {{cannot use class 'Bar' here; it is SPI}} // expected-error@-2 {{stored property 'lazyProperty2' cannot be declared '@_spi' in a '@frozen' struct}} - // expected-error@-3 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} - // expected-error@-4 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} @_spi(Foo) @Wrapper public var wrappedProperty1: Bar // expected-error@-1 {{stored property 'wrappedProperty1' cannot be declared '@_spi' in a '@frozen' struct}} @@ -73,12 +71,8 @@ public struct Resilient { public var computedProperty: Bar { Bar() } // expected-error {{cannot use class 'Bar' here; it is SPI}} public lazy var lazyProperty1 = Bar() // expected-error {{cannot use class 'Bar' here; it is SPI}} - // expected-error@-1 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} - // expected-error@-2 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} public lazy var lazyProperty2: Bar = Bar() // expected-error {{cannot use class 'Bar' here; it is SPI}} - // expected-error@-1 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} - // expected-error@-2 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} @Wrapper public var wrappedProperty1: Bar // expected-error@-1 {{cannot use class 'Bar' here; it is SPI}} @@ -105,12 +99,8 @@ public struct Resilient { private var computedProperty: Bar { Bar() } private lazy var lazyProperty1 = Bar() // expected-error {{cannot use class 'Bar' here; it is SPI}} - // expected-error@-1 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} - // expected-error@-2 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} private lazy var lazyProperty2: Bar = Bar() // expected-error {{cannot use class 'Bar' here; it is SPI}} - // expected-error@-1 {{initializer 'init()' cannot be used in a property initializer in a '@frozen' type because it is SPI}} - // expected-error@-2 {{class 'Bar' cannot be used in a property initializer in a '@frozen' type because it is SPI}} @Wrapper private var wrappedProperty1: Bar // expected-error@-1 {{cannot use class 'Bar' here; it is SPI}} diff --git a/test/attr/attr_inlinable.swift b/test/attr/attr_inlinable.swift index 2b8e785e8e60f..fecf32df1d09f 100644 --- a/test/attr/attr_inlinable.swift +++ b/test/attr/attr_inlinable.swift @@ -340,3 +340,13 @@ public struct PrivateInlinableCrash { func innerFunction4(x: () = publicFunction()) {} } + +// This is OK -- lazy property initializers are emitted inside the getter, +// which is never @inlinable. +@frozen public struct LazyField { + public lazy var y: () = privateFunction() + + @inlinable private lazy var z: () = privateFunction() + // expected-error@-1 {{'@inlinable' attribute cannot be applied to stored properties}} +} + From a9af09856041faabf8e7da091f8515445c914872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=85=B7=E9=85=B7=E7=9A=84=E5=93=80=E6=AE=BF?= Date: Thu, 22 Oct 2020 22:43:31 +0800 Subject: [PATCH 646/745] Update ObjectIdentifier.swift --- stdlib/public/core/ObjectIdentifier.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/stdlib/public/core/ObjectIdentifier.swift b/stdlib/public/core/ObjectIdentifier.swift index 39b73cbd027ff..2ef5a9493a8ac 100644 --- a/stdlib/public/core/ObjectIdentifier.swift +++ b/stdlib/public/core/ObjectIdentifier.swift @@ -16,9 +16,7 @@ /// is no notion of identity for structs, enums, functions, or tuples. /// /// `ObjectIdentifier` is only guaranteed to remain unique for the -/// lifetime of an object. When the instance gets deallocated, its object -/// identifier may be reused for a different object. (Internally, objects are -/// identified by their memory location.) +/// lifetime of an object. @frozen // trivial-implementation public struct ObjectIdentifier { @usableFromInline // trivial-implementation From 506473dfba40cde3bfb5480b6d44f2d9c26f2cab Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 21 Oct 2020 16:17:38 -0700 Subject: [PATCH 647/745] [Async CC] Supported partial application. The majority of support comes in the form of emitting partial application forwarders for partial applications of async functions. Such a partial application forwarder must take an async context which has been partially populated at the apply site. It is responsible for populating it "the rest of the way". To do so, like sync partial application forwarders, it takes a second argument, its context, from which it pulls the additional arguments which were capture at partial_apply time. The size of the async context that is passed to the forwarder, however, can't be known at the apply site by simply looking at the signature of the function to be applied (not even by looking at the size associated with the function in the special async function pointer constant which will soon be emitted). The reason is that there are an unknown (at the apply site) number of additional arguments which will be filled by the partial apply forwarder (and in the case of repeated partial applications, further filled in incrementally at each level). To enable this, there will always be a heap object for thick async functions. These heap objects will always store the size of the async context to be allocated as their first element. (Note that it may be possible to apply the same optimization that was applied for thick sync functions where a single refcounted object could be used as the context; doing so, however, must be made to interact properly with the async context size stored in the heap object.) To continue to allow promoting thin async functions to thick async functions without incurring a thunk, at the apply site, a null-check will be performed on the context pointer. If it is null, then the async context size will be determined based on the signature. (When async function pointers become pointers to a constant with a size i32 and a relative address to the underlying function, the size will be read from that constant.) When it is not-null, the size will be pulled from the first field of the context (which will in that case be cast to <{%swift.refcounted, i32}>). To facilitate sharing code and preserving the original structure of emitPartialApplicationForwarder (which weighed in at roughly 700 lines prior to this change), a new small class hierarchy, descending from PartialApplicationForwarderEmission has been added, with subclasses for the sync and async case. The shuffling of arguments into and out of the final explosion that was being performed in the synchronous case has been preserved there, though the arguments are added and removed through a number of methods on the superclass with more descriptive names. That was necessary to enable the async class to handle these different flavors of parameters correctly. To get some initial test coverage, the preexisting IRGen/partial_apply.sil and IRGen/partial_apply_forwarder.sil tests have been duplicated into the async folder. Those tests cases within these files which happened to have been crashing have each been extracted into its own runnable test that both verifies that the compiler does not crash and also that the partial application forwarder behaves correctly. The FileChecks in these tests are extremely minimal, providing only enough information to be sure that arguments are in fact squeezed into an async context. --- lib/IRGen/GenCall.cpp | 153 +++- lib/IRGen/GenCall.h | 20 +- lib/IRGen/GenFunc.cpp | 713 +++++++++++++++--- lib/IRGen/GenHeap.cpp | 20 +- lib/IRGen/GenHeap.h | 11 +- lib/IRGen/GenReflection.cpp | 6 +- lib/IRGen/IRGenSIL.cpp | 32 +- test/IRGen/async/partial_apply.sil | 527 +++++++++++++ test/IRGen/async/partial_apply_forwarder.sil | 231 ++++++ ...run-partialapply-capture-class-to-void.sil | 93 +++ ...-generic_conformer-and-generic-to-void.sil | 87 +++ ...nout-generic-and-in-generic-to-generic.sil | 68 ++ ...tialapply-capture-int64-int64-to-int64.sil | 71 ++ ...-partialapply-capture-int64-to-generic.sil | 64 ++ ...tance_classinstance-and-int64-to-int64.sil | 105 +++ ...eric_classinstance_to_struct_and_error.sil | 97 +++ ...eneric_polymorphic_constrained-to-void.sil | 68 ++ ...eneric_polymorphic_constrained-to-void.sil | 71 ++ ...re-type_thin-and-classinstance-to-void.sil | 102 +++ test/Inputs/print-shims.swift | 5 + 20 files changed, 2383 insertions(+), 161 deletions(-) create mode 100644 test/IRGen/async/partial_apply.sil create mode 100644 test/IRGen/async/partial_apply_forwarder.sil create mode 100644 test/IRGen/async/run-partialapply-capture-class-to-void.sil create mode 100644 test/IRGen/async/run-partialapply-capture-generic_conformer-and-generic-to-void.sil create mode 100644 test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil create mode 100644 test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil create mode 100644 test/IRGen/async/run-partialapply-capture-int64-to-generic.sil create mode 100644 test/IRGen/async/run-partialapply-capture-struct_classinstance_classinstance-and-int64-to-int64.sil create mode 100644 test/IRGen/async/run-partialapply-capture-structgeneric_classinstance_to_struct_and_error.sil create mode 100644 test/IRGen/async/run-partialapply-capture-structgeneric_polymorphic_constrained-to-void.sil create mode 100644 test/IRGen/async/run-partialapply-capture-type_structgeneric_polymorphic_constrained-to-void.sil create mode 100644 test/IRGen/async/run-partialapply-capture-type_thin-and-classinstance-to-void.sil diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 66ad478939cef..3b79222fc3e26 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -20,6 +20,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/GenericEnvironment.h" #include "swift/Runtime/Config.h" +#include "swift/SIL/SILModule.h" #include "swift/SIL/SILType.h" #include "clang/AST/ASTContext.h" #include "clang/AST/RecordLayout.h" @@ -135,9 +136,7 @@ AsyncContextLayout irgen::getAsyncContextLayout( // SelfType self?; bool hasLocalContextParameter = hasSelfContextParameter(substitutedType); bool canHaveValidError = substitutedType->hasErrorResult(); - bool hasLocalContext = (hasLocalContextParameter || canHaveValidError || - substitutedType->getRepresentation() == - SILFunctionTypeRepresentation::Thick); + bool hasLocalContext = (hasLocalContextParameter || canHaveValidError); SILParameterInfo localContextParameter = hasLocalContextParameter ? parameters.back() : SILParameterInfo(); if (hasLocalContextParameter) { @@ -694,6 +693,10 @@ void SignatureExpansion::addAsyncParameters() { ParamIRTypes.push_back(IGM.SwiftContextPtrTy); // TODO: Add actor. // TODO: Add task. + if (FnType->getRepresentation() == SILFunctionTypeRepresentation::Thick) { + IGM.addSwiftSelfAttributes(Attrs, ParamIRTypes.size()); + ParamIRTypes.push_back(IGM.RefCountedPtrTy); + } } void SignatureExpansion::addCoroutineContextParameter() { @@ -1740,6 +1743,116 @@ static void externalizeArguments(IRGenFunction &IGF, const Callee &callee, Explosion &in, Explosion &out, TemporarySet &temporaries, bool isOutlined); +llvm::Value *irgen::getDynamicAsyncContextSize(IRGenFunction &IGF, + AsyncContextLayout layout, + CanSILFunctionType functionType, + llvm::Value *thickContext) { + switch (functionType->getRepresentation()) { + case SILFunctionTypeRepresentation::Thick: { + // If the called function is thick, the size of the called function's + // async context may not be statically knowable. + // + // Specifically, if the thick function was produced by a partial_apply, + // the function which was originally partially applied determines the + // size of the needed async context. That original function isn't known + // statically. The dynamic size is available within the context as an + // i32 at the first index: <{ %swift.refcounted*, /*size*/ i32, ... }>. + // + // On the other hand, if the thick function was produced by a + // thin_to_thick_function, then the context will be nullptr. In that + // case, the size of the needed async context is known statically to + // be the size dictated by the function signature. + // + // We are currently emitting into some basic block. To handle these two + // cases, we need to branch based on whether the context is nullptr; each + // branch must then determine the size in the manner appropriate to it. + // Finally, both blocks must join back together to make the call: + // + // SIL: IR: + // +-----+ +-------------------------+ + // |.....| |%cond = %ctx == nullptr | + // |apply| |br %cond, static, dynamic| + // |.....| +--------/--------------\-+ + // +-----+ / \ + // +-static-------+ +-dynamic----------------------------------------------+ + // |%size = K | |%layout = bitcast %context to <{%swift.context*, i32}>| + // |br join(%size)| |%size_addr = getelementptr %layout, i32 1, i32 0 | + // +-----\--------+ |%size = load %size_addr | + // \ |br join(%size) | + // \ +------------------------------------------------------+ + // \ / + // +-join(%size)-----------------------------------------------------------+ + // |%dataAddr = swift_taskAlloc(%task, %size) | + // |%async_context = bitcast %dataAddr to ASYNC_CONTEXT(static_callee_type)| + // |... // populate the fields %context with arguments | + // |call %callee(%async_context, %context) | + // +-----------------------------------------------------------------------+ + auto *staticSizeBlock = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext()); + auto *dynamicSizeBlock = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext()); + auto *joinBlock = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext()); + + auto hasThickContext = + IGF.Builder.CreateICmpNE(thickContext, IGF.IGM.RefCountedNull); + IGF.Builder.CreateCondBr(hasThickContext, dynamicSizeBlock, + staticSizeBlock); + + SmallVector, 2> phiValues; + { + IGF.Builder.emitBlock(staticSizeBlock); + auto size = getAsyncContextSize(layout); + auto *sizeValue = + llvm::ConstantInt::get(IGF.IGM.Int32Ty, size.getValue()); + phiValues.push_back({staticSizeBlock, sizeValue}); + IGF.Builder.CreateBr(joinBlock); + } + + { + IGF.Builder.emitBlock(dynamicSizeBlock); + SmallVector argTypeInfos; + SmallVector argValTypes; + auto int32ASTType = + BuiltinIntegerType::get(32, IGF.IGM.IRGen.SIL.getASTContext()) + ->getCanonicalType(); + auto int32SILType = SILType::getPrimitiveObjectType(int32ASTType); + const TypeInfo &int32TI = IGF.IGM.getTypeInfo(int32SILType); + argValTypes.push_back(int32SILType); + argTypeInfos.push_back(&int32TI); + HeapLayout layout(IGF.IGM, LayoutStrategy::Optimal, argValTypes, + argTypeInfos, + /*typeToFill*/ nullptr, NecessaryBindings()); + auto castThickContext = + layout.emitCastTo(IGF, thickContext, "context.prefix"); + auto sizeLayout = layout.getElement(0); + auto sizeAddr = sizeLayout.project(IGF, castThickContext, + /*NonFixedOffsets*/ llvm::None); + auto *sizeValue = IGF.Builder.CreateLoad(sizeAddr); + phiValues.push_back({dynamicSizeBlock, sizeValue}); + IGF.Builder.CreateBr(joinBlock); + } + + { + IGF.Builder.emitBlock(joinBlock); + auto *phi = IGF.Builder.CreatePHI(IGF.IGM.Int32Ty, phiValues.size()); + for (auto &entry : phiValues) { + phi->addIncoming(entry.second, entry.first); + } + return phi; + } + } + case SILFunctionTypeRepresentation::Thin: + case SILFunctionTypeRepresentation::CFunctionPointer: + case SILFunctionTypeRepresentation::Method: + case SILFunctionTypeRepresentation::ObjCMethod: + case SILFunctionTypeRepresentation::WitnessMethod: + case SILFunctionTypeRepresentation::Closure: + case SILFunctionTypeRepresentation::Block: { + auto size = getAsyncContextSize(layout); + auto *sizeValue = llvm::ConstantInt::get(IGF.IGM.Int32Ty, size.getValue()); + return sizeValue; + } + } +} + namespace { class SyncCallEmission final : public CallEmission { @@ -1947,6 +2060,7 @@ class AsyncCallEmission final : public CallEmission { Address contextBuffer; Size contextSize; Address context; + llvm::Value *thickContext = nullptr; AsyncContextLayout getAsyncContextLayout() { return ::getAsyncContextLayout(IGF, getCallee().getOrigFunctionType(), @@ -1975,9 +2089,14 @@ class AsyncCallEmission final : public CallEmission { super::begin(); assert(!contextBuffer.isValid()); assert(!context.isValid()); - // Allocate space for the async arguments. auto layout = getAsyncContextLayout(); - std::tie(contextBuffer, contextSize) = emitAllocAsyncContext(IGF, layout); + // Allocate space for the async arguments. + auto *dynamicContextSize32 = getDynamicAsyncContextSize( + IGF, layout, CurCallee.getOrigFunctionType(), thickContext); + auto *dynamicContextSize = + IGF.Builder.CreateZExt(dynamicContextSize32, IGF.IGM.SizeTy); + std::tie(contextBuffer, contextSize) = emitAllocAsyncContext( + IGF, layout, dynamicContextSize, getAsyncContextSize(layout)); context = layout.emitCastTo(IGF, contextBuffer.getAddress()); if (layout.canHaveError()) { auto fieldLayout = layout.getErrorLayout(); @@ -1993,7 +2112,10 @@ class AsyncCallEmission final : public CallEmission { emitDeallocAsyncContext(IGF, contextBuffer, contextSize); super::end(); } - void setFromCallee() override { super::setFromCallee(); } + void setFromCallee() override { + super::setFromCallee(); + thickContext = CurCallee.getSwiftContext(); + } SILType getParameterType(unsigned index) override { return getAsyncContextLayout().getParameterType(index); } @@ -2001,6 +2123,10 @@ class AsyncCallEmission final : public CallEmission { WitnessMetadata *witnessMetadata) override { Explosion asyncExplosion; asyncExplosion.add(contextBuffer.getAddress()); + if (getCallee().getRepresentation() == + SILFunctionTypeRepresentation::Thick) { + asyncExplosion.add(getCallee().getSwiftContext()); + } super::setArgs(asyncExplosion, false, witnessMetadata); SILFunctionConventions fnConv(getCallee().getSubstFunctionType(), IGF.getSILModule()); @@ -3196,14 +3322,21 @@ void irgen::emitTaskDealloc(IRGenFunction &IGF, Address address, llvm::Attribute::ReadNone); } +std::pair irgen::emitAllocAsyncContext(IRGenFunction &IGF, + AsyncContextLayout layout, + llvm::Value *sizeValue, + Size sizeLowerBound) { + auto alignment = getAsyncContextAlignment(IGF.IGM); + auto address = emitTaskAlloc(IGF, sizeValue, alignment); + IGF.Builder.CreateLifetimeStart(address, sizeLowerBound); + return {address, sizeLowerBound}; +} + std::pair irgen::emitAllocAsyncContext(IRGenFunction &IGF, AsyncContextLayout layout) { auto size = getAsyncContextSize(layout); auto *sizeValue = llvm::ConstantInt::get(IGF.IGM.SizeTy, size.getValue()); - auto alignment = getAsyncContextAlignment(IGF.IGM); - auto address = emitTaskAlloc(IGF, sizeValue, alignment); - IGF.Builder.CreateLifetimeStart(address, size); - return {address, size}; + return emitAllocAsyncContext(IGF, layout, sizeValue, size); } void irgen::emitDeallocAsyncContext(IRGenFunction &IGF, Address context, diff --git a/lib/IRGen/GenCall.h b/lib/IRGen/GenCall.h index ce1a692c31285..8f0b58da52721 100644 --- a/lib/IRGen/GenCall.h +++ b/lib/IRGen/GenCall.h @@ -72,8 +72,6 @@ namespace irgen { // SwiftPartialFunction * __ptrauth(...) yieldToCaller?; // SwiftError *errorResult; // IndirectResultTypes *indirectResults...; - // SelfType self?; - // ArgTypes formalArguments...; // union { // struct { // SwiftPartialFunction * __ptrauth(...) resumeFromYield?; @@ -173,10 +171,6 @@ namespace irgen { assert(hasLocalContext()); return getElement(getLocalContextIndex()); } - ParameterConvention getLocalContextConvention() { - assert(hasLocalContext()); - return localContextInfo->convention; - } SILType getLocalContextType() { assert(hasLocalContext()); return localContextInfo->type; @@ -230,6 +224,10 @@ namespace irgen { Optional localContextInfo); }; + llvm::Value *getDynamicAsyncContextSize(IRGenFunction &IGF, + AsyncContextLayout layout, + CanSILFunctionType functionType, + llvm::Value *thickContext); AsyncContextLayout getAsyncContextLayout(IRGenFunction &IGF, SILFunction *function); @@ -320,6 +318,16 @@ namespace irgen { Address emitTaskAlloc(IRGenFunction &IGF, llvm::Value *size, Alignment alignment); void emitTaskDealloc(IRGenFunction &IGF, Address address, llvm::Value *size); + /// Allocate task local storage for the specified layout but using the + /// provided dynamic size. Allowing the size to be specified dynamically is + /// necessary for applies of thick functions the sizes of whose async contexts + /// are dependent on the underlying, already partially applied, called + /// function. The provided sizeLowerBound will be used to track the lifetime + /// of the allocation that is known statically. + std::pair emitAllocAsyncContext(IRGenFunction &IGF, + AsyncContextLayout layout, + llvm::Value *sizeValue, + Size sizeLowerBound); std::pair emitAllocAsyncContext(IRGenFunction &IGF, AsyncContextLayout layout); void emitDeallocAsyncContext(IRGenFunction &IGF, Address context, Size size); diff --git a/lib/IRGen/GenFunc.cpp b/lib/IRGen/GenFunc.cpp index ba6eaa593aed7..f723af0cdeab4 100644 --- a/lib/IRGen/GenFunc.cpp +++ b/lib/IRGen/GenFunc.cpp @@ -720,57 +720,93 @@ static unsigned findSinglePartiallyAppliedParameterIndexIgnoringEmptyTypes( return firstNonEmpty; } -/// Emit the forwarding stub function for a partial application. -/// -/// If 'layout' is null, there is a single captured value of -/// Swift-refcountable type that is being used directly as the -/// context object. -static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, - const Optional &staticFnPtr, - bool calleeHasContext, - const Signature &origSig, - CanSILFunctionType origType, - CanSILFunctionType substType, - CanSILFunctionType outType, - SubstitutionMap subs, - HeapLayout const *layout, - ArrayRef conventions) { - auto outSig = IGM.getSignature(outType); - llvm::AttributeList outAttrs = outSig.getAttributes(); - llvm::FunctionType *fwdTy = outSig.getType(); - SILFunctionConventions outConv(outType, IGM.getSILModule()); - - StringRef FnName; - if (staticFnPtr) - FnName = staticFnPtr->getPointer()->getName(); - - IRGenMangler Mangler; - std::string thunkName = Mangler.manglePartialApplyForwarder(FnName); - - // FIXME: Maybe cache the thunk by function and closure types?. - llvm::Function *fwd = - llvm::Function::Create(fwdTy, llvm::Function::InternalLinkage, - llvm::StringRef(thunkName), &IGM.Module); - fwd->setCallingConv(outSig.getCallingConv()); - - fwd->setAttributes(outAttrs); - // Merge initial attributes with outAttrs. - llvm::AttrBuilder b; - IGM.constructInitialFnAttributes(b); - fwd->addAttributes(llvm::AttributeList::FunctionIndex, b); - - IRGenFunction subIGF(IGM, fwd); - if (IGM.DebugInfo) - IGM.DebugInfo->emitArtificialFunction(subIGF, fwd); - - Explosion origParams = subIGF.collectParameters(); - +namespace { +class PartialApplicationForwarderEmission { +protected: + IRGenModule &IGM; + IRGenFunction &subIGF; + llvm::Function *fwd; + const Optional &staticFnPtr; + bool calleeHasContext; + const Signature &origSig; + CanSILFunctionType origType; + CanSILFunctionType substType; + CanSILFunctionType outType; + SubstitutionMap subs; + HeapLayout const *layout; + const ArrayRef conventions; + SILFunctionConventions origConv; + SILFunctionConventions outConv; + Explosion origParams; + + PartialApplicationForwarderEmission( + IRGenModule &IGM, IRGenFunction &subIGF, llvm::Function *fwd, + const Optional &staticFnPtr, bool calleeHasContext, + const Signature &origSig, CanSILFunctionType origType, + CanSILFunctionType substType, CanSILFunctionType outType, + SubstitutionMap subs, HeapLayout const *layout, + ArrayRef conventions) + : IGM(IGM), subIGF(subIGF), fwd(fwd), staticFnPtr(staticFnPtr), + calleeHasContext(calleeHasContext), origSig(origSig), + origType(origType), substType(substType), outType(outType), subs(subs), + conventions(conventions), origConv(origType, IGM.getSILModule()), + outConv(outType, IGM.getSILModule()), + origParams(subIGF.collectParameters()) {} + +public: + enum class DynamicFunctionKind { + Witness, + PartialApply, + }; + virtual void begin(){}; + virtual void gatherArgumentsFromApply() = 0; + virtual unsigned getCurrentArgumentIndex() = 0; + virtual bool transformArgumentToNative(SILParameterInfo origParamInfo, + Explosion &in, Explosion &out) = 0; + virtual void addArgument(Explosion &explosion) = 0; + virtual void addArgument(llvm::Value *argValue) = 0; + virtual void addArgument(Explosion &explosion, unsigned index) = 0; + virtual void addArgument(llvm::Value *argValue, unsigned index) = 0; + virtual SILParameterInfo getParameterInfo(unsigned index) = 0; + virtual llvm::Value *getContext() = 0; + virtual llvm::Value *getDynamicFunctionPointer() = 0; + virtual llvm::Value *getDynamicFunctionContext() = 0; + virtual void addDynamicFunctionContext(Explosion &explosion, + DynamicFunctionKind kind) = 0; + virtual void addDynamicFunctionPointer(Explosion &explosion, + DynamicFunctionKind kind) = 0; + virtual void addSelf(Explosion &explosion) = 0; + virtual void addWitnessSelfMetadata(llvm::Value *value) = 0; + virtual void addWitnessSelfWitnessTable(llvm::Value *value) = 0; + virtual void forwardErrorResult() = 0; + virtual bool originalParametersConsumed() = 0; + virtual void addPolymorphicArguments(Explosion polyArgs) = 0; + virtual llvm::CallInst *createCall(FunctionPointer &fnPtr) = 0; + virtual void createReturn(llvm::CallInst *call) = 0; + virtual void end(){}; + virtual ~PartialApplicationForwarderEmission() {} +}; +class SyncPartialApplicationForwarderEmission + : public PartialApplicationForwarderEmission { + using super = PartialApplicationForwarderEmission; // Create a new explosion for potentially reabstracted parameters. Explosion args; - Address resultValueAddr; - { +public: + SyncPartialApplicationForwarderEmission( + IRGenModule &IGM, IRGenFunction &subIGF, llvm::Function *fwd, + const Optional &staticFnPtr, bool calleeHasContext, + const Signature &origSig, CanSILFunctionType origType, + CanSILFunctionType substType, CanSILFunctionType outType, + SubstitutionMap subs, HeapLayout const *layout, + ArrayRef conventions) + : PartialApplicationForwarderEmission( + IGM, subIGF, fwd, staticFnPtr, calleeHasContext, origSig, origType, + substType, outType, subs, layout, conventions) {} + + void begin() override { super::begin(); } + void gatherArgumentsFromApply() override { // Lower the forwarded arguments in the original function's generic context. GenericContextScope scope(IGM, origType->getInvocationGenericSignature()); @@ -876,6 +912,403 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, nativeApplyArg.transferInto(args, nativeApplyArg.size()); } } + unsigned getCurrentArgumentIndex() override { return args.size(); } + bool transformArgumentToNative(SILParameterInfo origParamInfo, Explosion &in, + Explosion &out) override { + return addNativeArgument(subIGF, in, origType, origParamInfo, out, false); + } + void addArgument(Explosion &explosion) override { + args.add(explosion.claimAll()); + } + void addArgument(llvm::Value *argValue) override { args.add(argValue); } + void addArgument(Explosion &explosion, unsigned index) override { + addArgument(explosion); + } + void addArgument(llvm::Value *argValue, unsigned index) override { + addArgument(argValue); + } + SILParameterInfo getParameterInfo(unsigned index) override { + return substType->getParameters()[index]; + } + llvm::Value *getContext() override { return origParams.claimNext(); } + llvm::Value *getDynamicFunctionPointer() override { return args.takeLast(); } + llvm::Value *getDynamicFunctionContext() override { return args.takeLast(); } + void addDynamicFunctionContext(Explosion &explosion, + DynamicFunctionKind kind) override { + addArgument(explosion); + } + void addDynamicFunctionPointer(Explosion &explosion, + DynamicFunctionKind kind) override { + addArgument(explosion); + } + void addSelf(Explosion &explosion) override { addArgument(explosion); } + void addWitnessSelfMetadata(llvm::Value *value) override { + addArgument(value); + } + void addWitnessSelfWitnessTable(llvm::Value *value) override { + addArgument(value); + } + void forwardErrorResult() override { + llvm::Value *errorResultPtr = origParams.claimNext(); + args.add(errorResultPtr); + } + bool originalParametersConsumed() override { return origParams.empty(); } + void addPolymorphicArguments(Explosion polyArgs) override { + polyArgs.transferInto(args, polyArgs.size()); + } + llvm::CallInst *createCall(FunctionPointer &fnPtr) override { + return subIGF.Builder.CreateCall(fnPtr, args.claimAll()); + } + void createReturn(llvm::CallInst *call) override { + // Reabstract the result value as substituted. + SILFunctionConventions origConv(origType, IGM.getSILModule()); + auto &outResultTI = IGM.getTypeInfo( + outConv.getSILResultType(IGM.getMaximalTypeExpansionContext())); + auto &nativeResultSchema = outResultTI.nativeReturnValueSchema(IGM); + if (call->getType()->isVoidTy()) { + if (!resultValueAddr.isValid()) + subIGF.Builder.CreateRetVoid(); + else { + // Okay, we have called a function that expects an indirect return type + // but the partially applied return type is direct. + assert(!nativeResultSchema.requiresIndirect()); + Explosion loadedResult; + cast(outResultTI) + .loadAsTake(subIGF, resultValueAddr, loadedResult); + Explosion nativeResult = nativeResultSchema.mapIntoNative( + IGM, subIGF, loadedResult, + outConv.getSILResultType(IGM.getMaximalTypeExpansionContext()), + false); + outResultTI.deallocateStack( + subIGF, resultValueAddr, + outConv.getSILResultType(IGM.getMaximalTypeExpansionContext())); + if (nativeResult.size() == 1) + subIGF.Builder.CreateRet(nativeResult.claimNext()); + else { + llvm::Value *nativeAgg = + llvm::UndefValue::get(nativeResultSchema.getExpandedType(IGM)); + for (unsigned i = 0, e = nativeResult.size(); i != e; ++i) { + auto *elt = nativeResult.claimNext(); + nativeAgg = subIGF.Builder.CreateInsertValue(nativeAgg, elt, i); + } + subIGF.Builder.CreateRet(nativeAgg); + } + } + } else { + llvm::Value *callResult = call; + // If the result type is dependent on a type parameter we might have to + // cast to the result type - it could be substituted. + if (origConv.getSILResultType(IGM.getMaximalTypeExpansionContext()) + .hasTypeParameter()) { + auto ResType = fwd->getReturnType(); + if (ResType != callResult->getType()) + callResult = + subIGF.coerceValue(callResult, ResType, subIGF.IGM.DataLayout); + } + subIGF.Builder.CreateRet(callResult); + } + } + void end() override { super::end(); } +}; +class AsyncPartialApplicationForwarderEmission + : public PartialApplicationForwarderEmission { + using super = PartialApplicationForwarderEmission; + AsyncContextLayout layout; + llvm::Value *contextBuffer; + Size contextSize; + Address context; + llvm::Value *heapContextBuffer; + unsigned currentArgumentIndex; + struct DynamicFunction { + using Kind = DynamicFunctionKind; + Kind kind; + llvm::Value *pointer; + llvm::Value *context; + }; + Optional dynamicFunction = llvm::None; + struct Self { + enum class Kind { + Method, + WitnessMethod, + }; + Kind kind; + llvm::Value *value; + }; + Optional self = llvm::None; + + llvm::Value *loadValue(ElementLayout layout) { + Address addr = layout.project(subIGF, context, /*offsets*/ llvm::None); + auto &ti = cast(layout.getType()); + Explosion explosion; + ti.loadAsTake(subIGF, addr, explosion); + return explosion.claimNext(); + } + void saveValue(ElementLayout layout, Explosion &explosion) { + Address addr = layout.project(subIGF, context, /*offsets*/ llvm::None); + auto &ti = cast(layout.getType()); + ti.initialize(subIGF, explosion, addr, /*isOutlined*/ false); + } + void loadValue(ElementLayout layout, Explosion &explosion) { + Address addr = layout.project(subIGF, context, /*offsets*/ llvm::None); + auto &ti = cast(layout.getType()); + ti.loadAsTake(subIGF, addr, explosion); + } + +public: + AsyncPartialApplicationForwarderEmission( + IRGenModule &IGM, IRGenFunction &subIGF, llvm::Function *fwd, + const Optional &staticFnPtr, bool calleeHasContext, + const Signature &origSig, CanSILFunctionType origType, + CanSILFunctionType substType, CanSILFunctionType outType, + SubstitutionMap subs, HeapLayout const *layout, + ArrayRef conventions) + : PartialApplicationForwarderEmission( + IGM, subIGF, fwd, staticFnPtr, calleeHasContext, origSig, origType, + substType, outType, subs, layout, conventions), + layout(getAsyncContextLayout(subIGF, origType, substType, subs)), + currentArgumentIndex(outType->getNumParameters()) { + contextBuffer = origParams.claimNext(); + heapContextBuffer = origParams.claimNext(); + } + + void begin() override { + super::begin(); + assert(contextBuffer); + assert(heapContextBuffer); + context = layout.emitCastTo(subIGF, contextBuffer); + } + bool transformArgumentToNative(SILParameterInfo origParamInfo, Explosion &in, + Explosion &out) override { + out.add(in.claimAll()); + return false; + } + unsigned getCurrentArgumentIndex() override { return currentArgumentIndex; } + void gatherArgumentsFromApply() override { + // The provided %swift.context* already contains all the values from the + // apply site. All that remains to do is bind polymorphic parameters. + for (unsigned index = 0; index < outType->getParameters().size(); ++index) { + auto fieldLayout = layout.getArgumentLayout(index); + Explosion explosion; + loadValue(fieldLayout, explosion); + bindPolymorphicParameter(subIGF, origType, substType, explosion, index); + (void)explosion.claimAll(); + // TODO: Rather than just discard this explosion, avoid loading the + // parameters if no polymorphic binding is necessary. + } + } + void addArgument(llvm::Value *argValue) override { + addArgument(argValue, currentArgumentIndex); + } + void addArgument(Explosion &explosion) override { + addArgument(explosion, currentArgumentIndex); + } + void addArgument(llvm::Value *argValue, unsigned index) override { + Explosion explosion; + explosion.add(argValue); + addArgument(explosion, index); + } + void addArgument(Explosion &explosion, unsigned index) override { + currentArgumentIndex = index + 1; + auto isLocalContext = (hasSelfContextParameter(origType) && + index == origType->getParameters().size() - 1); + if (isLocalContext) { + addSelf(explosion); + return; + } + auto fieldLayout = layout.getArgumentLayout(index); + saveValue(fieldLayout, explosion); + } + SILParameterInfo getParameterInfo(unsigned index) override { + return origType->getParameters()[index]; + } + llvm::Value *getContext() override { return heapContextBuffer; } + llvm::Value *getDynamicFunctionPointer() override { + assert(dynamicFunction && dynamicFunction->pointer); + return dynamicFunction->pointer; + } + llvm::Value *getDynamicFunctionContext() override { + assert((dynamicFunction && dynamicFunction->context) || + (self && self->value)); + return dynamicFunction ? dynamicFunction->context : self->value; + } + void addDynamicFunctionContext(Explosion &explosion, + DynamicFunction::Kind kind) override { + auto *value = explosion.claimNext(); + assert(explosion.empty()); + if (dynamicFunction) { + assert(dynamicFunction->kind == kind); + if (dynamicFunction->context) { + assert(dynamicFunction->context == value); + } else { + dynamicFunction->context = value; + } + return; + } + dynamicFunction = {kind, /*pointer*/ nullptr, /*context*/ value}; + } + void addDynamicFunctionPointer(Explosion &explosion, + DynamicFunction::Kind kind) override { + auto *value = explosion.claimNext(); + assert(explosion.empty()); + if (dynamicFunction) { + assert(dynamicFunction->kind == kind); + if (dynamicFunction->pointer) { + assert(dynamicFunction->pointer == value); + } else { + dynamicFunction->pointer = value; + } + return; + } + dynamicFunction = {kind, /*pointer*/ value, /*context*/ nullptr}; + } + void addSelf(Explosion &explosion) override { + auto *value = explosion.claimNext(); + assert(explosion.empty()); + Self::Kind kind = [&](SILFunctionTypeRepresentation representation) { + switch (representation) { + case SILFunctionTypeRepresentation::Method: + return Self::Kind::Method; + case SILFunctionTypeRepresentation::WitnessMethod: + return Self::Kind::WitnessMethod; + default: + llvm_unreachable("representation does not have a self"); + } + }(origType->getRepresentation()); + if (self) { + assert(self->kind == kind); + if (self->value) { + assert(self->value == value); + } else { + self->value = value; + } + return; + } + self = {kind, value}; + + Explosion toSave; + toSave.add(value); + auto fieldLayout = layout.getLocalContextLayout(); + saveValue(fieldLayout, toSave); + } + void addWitnessSelfMetadata(llvm::Value *value) override { + auto fieldLayout = layout.getSelfMetadataLayout(); + Explosion explosion; + explosion.add(value); + saveValue(fieldLayout, explosion); + } + void addWitnessSelfWitnessTable(llvm::Value *value) override { + auto fieldLayout = layout.getSelfWitnessTableLayout(); + Explosion explosion; + explosion.add(value); + saveValue(fieldLayout, explosion); + } + void forwardErrorResult() override { + // Nothing to do here. The error result pointer is already in the + // appropriate position. + } + bool originalParametersConsumed() override { + // The original parameters remain in the initially allocated + // %swift.context*, so they have always already been consumed. + return true; + } + void addPolymorphicArguments(Explosion polyArgs) override { + if (polyArgs.size() == 0) { + return; + } + assert(layout.hasBindings()); + auto bindingsLayout = layout.getBindingsLayout(); + auto bindingsAddr = + bindingsLayout.project(subIGF, context, /*offsets*/ None); + layout.getBindings().save(subIGF, bindingsAddr, polyArgs); + } + llvm::CallInst *createCall(FunctionPointer &fnPtr) override { + Explosion asyncExplosion; + asyncExplosion.add(contextBuffer); + if (dynamicFunction && + dynamicFunction->kind == DynamicFunction::Kind::PartialApply) { + assert(dynamicFunction->context); + asyncExplosion.add(dynamicFunction->context); + } + + return subIGF.Builder.CreateCall(fnPtr, asyncExplosion.claimAll()); + } + void createReturn(llvm::CallInst *call) override { + subIGF.Builder.CreateRetVoid(); + } + void end() override { + assert(context.isValid()); + super::end(); + } +}; +std::unique_ptr +getPartialApplicationForwarderEmission( + IRGenModule &IGM, IRGenFunction &subIGF, llvm::Function *fwd, + const Optional &staticFnPtr, bool calleeHasContext, + const Signature &origSig, CanSILFunctionType origType, + CanSILFunctionType substType, CanSILFunctionType outType, + SubstitutionMap subs, HeapLayout const *layout, + ArrayRef conventions) { + if (origType->isAsync()) { + return std::make_unique( + IGM, subIGF, fwd, staticFnPtr, calleeHasContext, origSig, origType, + substType, outType, subs, layout, conventions); + } else { + return std::make_unique( + IGM, subIGF, fwd, staticFnPtr, calleeHasContext, origSig, origType, + substType, outType, subs, layout, conventions); + } +} + +} // end anonymous namespace + +/// Emit the forwarding stub function for a partial application. +/// +/// If 'layout' is null, there is a single captured value of +/// Swift-refcountable type that is being used directly as the +/// context object. +static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, + const Optional &staticFnPtr, + bool calleeHasContext, + const Signature &origSig, + CanSILFunctionType origType, + CanSILFunctionType substType, + CanSILFunctionType outType, + SubstitutionMap subs, + HeapLayout const *layout, + ArrayRef conventions) { + auto outSig = IGM.getSignature(outType); + llvm::AttributeList outAttrs = outSig.getAttributes(); + llvm::FunctionType *fwdTy = outSig.getType(); + SILFunctionConventions outConv(outType, IGM.getSILModule()); + + StringRef FnName; + if (staticFnPtr) + FnName = staticFnPtr->getPointer()->getName(); + + IRGenMangler Mangler; + std::string thunkName = Mangler.manglePartialApplyForwarder(FnName); + + // FIXME: Maybe cache the thunk by function and closure types?. + llvm::Function *fwd = + llvm::Function::Create(fwdTy, llvm::Function::InternalLinkage, + llvm::StringRef(thunkName), &IGM.Module); + fwd->setCallingConv(outSig.getCallingConv()); + + fwd->setAttributes(outAttrs); + // Merge initial attributes with outAttrs. + llvm::AttrBuilder b; + IGM.constructInitialFnAttributes(b); + fwd->addAttributes(llvm::AttributeList::FunctionIndex, b); + + IRGenFunction subIGF(IGM, fwd); + if (IGM.DebugInfo) + IGM.DebugInfo->emitArtificialFunction(subIGF, fwd); + + auto emission = getPartialApplicationForwarderEmission( + IGM, subIGF, fwd, staticFnPtr, calleeHasContext, origSig, origType, + substType, outType, subs, layout, conventions); + emission->begin(); + emission->gatherArgumentsFromApply(); struct AddressToDeallocate { SILType Type; @@ -912,10 +1345,15 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, Address data; unsigned nextCapturedField = 0; if (!layout) { - rawData = origParams.claimNext(); + rawData = emission->getContext(); } else if (!layout->isKnownEmpty()) { - rawData = origParams.claimNext(); + rawData = emission->getContext(); data = layout->emitCastTo(subIGF, rawData); + if (origType->isAsync()) { + // Async layouts contain the size of the needed async context as their + // first element. It is not a parameter and needs to be skipped. + ++nextCapturedField; + } // Restore type metadata bindings, if we have them. if (layout->hasBindings()) { @@ -931,7 +1369,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // or there's an error result. } else if (outType->getRepresentation()==SILFunctionTypeRepresentation::Thick || outType->hasErrorResult()) { - llvm::Value *contextPtr = origParams.claimNext(); (void)contextPtr; + llvm::Value *contextPtr = emission->getContext(); (void)contextPtr; assert(contextPtr->getType() == IGM.RefCountedPtrTy); } @@ -989,6 +1427,8 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // arguments come before this. bool isWitnessMethodCallee = origType->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod; + bool isMethodCallee = + origType->getRepresentation() == SILFunctionTypeRepresentation::Method; Explosion witnessMethodSelfValue; llvm::Value *lastCapturedFieldPtr = nullptr; @@ -1038,7 +1478,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // If there is a context argument, it comes after the polymorphic // arguments. - auto argIndex = args.size(); + auto argIndex = emission->getCurrentArgumentIndex(); if (haveContextArgument) argIndex += polyArgs.size(); @@ -1064,12 +1504,13 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, } else { argValue = subIGF.Builder.CreateBitCast(rawData, expectedArgTy); } - args.add(argValue); + emission->addArgument(argValue); // If there's a data pointer required, grab it and load out the // extra, previously-curried parameters. } else { unsigned origParamI = outType->getParameters().size(); + unsigned extraFieldIndex = 0; assert(layout->getElements().size() == conventions.size() && "conventions don't match context layout"); @@ -1175,18 +1616,41 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, if (hasPolymorphicParams) bindPolymorphicParameter(subIGF, origType, substType, param, origParamI); - emitApplyArgument(subIGF, - origType, origParamInfo, - substType, substType->getParameters()[origParamI], - param, origParam); + emitApplyArgument(subIGF, origType, origParamInfo, substType, + emission->getParameterInfo(origParamI), param, + origParam); bool isWitnessMethodCalleeSelf = (isWitnessMethodCallee && origParamI + 1 == origType->getParameters().size()); - needsAllocas |= addNativeArgument( - subIGF, origParam, origType, origParamInfo, - isWitnessMethodCalleeSelf ? witnessMethodSelfValue : args, false); + Explosion arg; + needsAllocas |= emission->transformArgumentToNative( + origParamInfo, origParam, + isWitnessMethodCalleeSelf ? witnessMethodSelfValue : arg); + if (!isWitnessMethodCalleeSelf) { + emission->addArgument(arg, origParamI); + } ++origParamI; } else { - args.add(param.claimAll()); + switch (extraFieldIndex) { + case 0: + emission->addDynamicFunctionContext( + param, isWitnessMethodCallee + ? PartialApplicationForwarderEmission:: + DynamicFunctionKind::Witness + : PartialApplicationForwarderEmission:: + DynamicFunctionKind::PartialApply); + break; + case 1: + emission->addDynamicFunctionPointer( + param, isWitnessMethodCallee + ? PartialApplicationForwarderEmission:: + DynamicFunctionKind::Witness + : PartialApplicationForwarderEmission:: + DynamicFunctionKind::PartialApply); + break; + default: + llvm_unreachable("unexpected extra field in thick context"); + } + ++extraFieldIndex; } } @@ -1224,7 +1688,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // The dynamic function pointer is packed "last" into the context, // and we pulled it out as an argument. Just pop it off. - auto fnPtr = args.takeLast(); + auto fnPtr = emission->getDynamicFunctionPointer(); // It comes out of the context as an i8*. Cast to the function type. fnPtr = subIGF.Builder.CreateBitCast(fnPtr, fnTy); @@ -1246,9 +1710,9 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // In either case, it's the last thing in 'args'. llvm::Value *fnContext = nullptr; if (haveContextArgument) - fnContext = args.takeLast(); + fnContext = emission->getDynamicFunctionContext(); - polyArgs.transferInto(args, polyArgs.size()); + emission->addPolymorphicArguments(std::move(polyArgs)); // If we have a witness method call, the inner context is the // witness table. Metadata for Self is derived inside the partial @@ -1263,35 +1727,45 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // Okay, this is where the callee context goes. } else if (fnContext) { - args.add(fnContext); + Explosion explosion; + explosion.add(fnContext); + if (isMethodCallee) { + emission->addSelf(explosion); + } else { + emission->addDynamicFunctionContext( + explosion, isWitnessMethodCallee + ? PartialApplicationForwarderEmission:: + DynamicFunctionKind::Witness + : PartialApplicationForwarderEmission:: + DynamicFunctionKind::PartialApply); + } // Pass a placeholder for thin function calls. } else if (origType->hasErrorResult()) { - args.add(llvm::UndefValue::get(IGM.RefCountedPtrTy)); + emission->addArgument(llvm::UndefValue::get(IGM.RefCountedPtrTy)); } // Add the witness methods self argument before the error parameter after the // polymorphic arguments. if (isWitnessMethodCallee) - witnessMethodSelfValue.transferInto(args, witnessMethodSelfValue.size()); + emission->addSelf(witnessMethodSelfValue); // Pass down the error result. if (origType->hasErrorResult()) { - llvm::Value *errorResultPtr = origParams.claimNext(); - args.add(errorResultPtr); + emission->forwardErrorResult(); } - assert(origParams.empty()); + assert(emission->originalParametersConsumed()); if (isWitnessMethodCallee) { assert(witnessMetadata.SelfMetadata->getType() == IGM.TypeMetadataPtrTy); - args.add(witnessMetadata.SelfMetadata); + emission->addWitnessSelfMetadata(witnessMetadata.SelfMetadata); assert(witnessMetadata.SelfWitnessTable->getType() == IGM.WitnessTablePtrTy); - args.add(witnessMetadata.SelfWitnessTable); + emission->addWitnessSelfWitnessTable(witnessMetadata.SelfWitnessTable); } - llvm::CallInst *call = subIGF.Builder.CreateCall(fnPtr, args.claimAll()); - + llvm::CallInst *call = emission->createCall(fnPtr); + if (addressesToDeallocate.empty() && !needsAllocas && (!consumesContext || !dependsOnContextLifetime)) call->setTailCall(); @@ -1308,52 +1782,8 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, subIGF.emitNativeStrongRelease(rawData, subIGF.getDefaultAtomicity()); } - // Reabstract the result value as substituted. - SILFunctionConventions origConv(origType, IGM.getSILModule()); - auto &outResultTI = IGM.getTypeInfo( - outConv.getSILResultType(IGM.getMaximalTypeExpansionContext())); - auto &nativeResultSchema = outResultTI.nativeReturnValueSchema(IGM); - if (call->getType()->isVoidTy()) { - if (!resultValueAddr.isValid()) - subIGF.Builder.CreateRetVoid(); - else { - // Okay, we have called a function that expects an indirect return type - // but the partially applied return type is direct. - assert(!nativeResultSchema.requiresIndirect()); - Explosion loadedResult; - cast(outResultTI) - .loadAsTake(subIGF, resultValueAddr, loadedResult); - Explosion nativeResult = nativeResultSchema.mapIntoNative( - IGM, subIGF, loadedResult, - outConv.getSILResultType(IGM.getMaximalTypeExpansionContext()), - false); - outResultTI.deallocateStack( - subIGF, resultValueAddr, - outConv.getSILResultType(IGM.getMaximalTypeExpansionContext())); - if (nativeResult.size() == 1) - subIGF.Builder.CreateRet(nativeResult.claimNext()); - else { - llvm::Value *nativeAgg = - llvm::UndefValue::get(nativeResultSchema.getExpandedType(IGM)); - for (unsigned i = 0, e = nativeResult.size(); i != e; ++i) { - auto *elt = nativeResult.claimNext(); - nativeAgg = subIGF.Builder.CreateInsertValue(nativeAgg, elt, i); - } - subIGF.Builder.CreateRet(nativeAgg); - } - } - } else { - llvm::Value *callResult = call; - // If the result type is dependent on a type parameter we might have to - // cast to the result type - it could be substituted. - if (origConv.getSILResultType(IGM.getMaximalTypeExpansionContext()) - .hasTypeParameter()) { - auto ResType = fwd->getReturnType(); - if (ResType != callResult->getType()) - callResult = subIGF.coerceValue(callResult, ResType, subIGF.IGM.DataLayout); - } - subIGF.Builder.CreateRet(callResult); - } + emission->createReturn(call); + emission->end(); return fwd; } @@ -1376,6 +1806,19 @@ Optional irgen::emitFunctionPartialApplication( SmallVector argValTypes; SmallVector argConventions; + if (origType->isAsync()) { + // Store the size of the partially applied async function's context here so + // that it can be recovered by at the apply site. + auto int32ASTType = + BuiltinIntegerType::get(32, IGF.IGM.IRGen.SIL.getASTContext()) + ->getCanonicalType(); + auto int32SILType = SILType::getPrimitiveObjectType(int32ASTType); + const TypeInfo &int32TI = IGF.IGM.getTypeInfo(int32SILType); + argValTypes.push_back(int32SILType); + argTypeInfos.push_back(&int32TI); + argConventions.push_back(ParameterConvention::Direct_Unowned); + } + // A context's HeapLayout stores all of the partially applied args. // A HeapLayout is "fixed" if all of its fields have a fixed layout. // Otherwise the HeapLayout is "non-fixed". @@ -1405,6 +1848,23 @@ Optional irgen::emitFunctionPartialApplication( auto bindings = NecessaryBindings::forPartialApplyForwarder( IGF.IGM, origType, subs, considerParameterSources); + if (origType->isAsync()) { + // The size of the async context needs to be available at the apply site. + // + // TODO: In the "single refcounted context" case the async "function + // pointer" (actually a pointer to a + // constant { + // /*context size*/ i32, + // /*relative address of function*/ i32 + // } + // rather than a pointer directly to the function) would be able to + // provide the async context size required. At the apply site, it is + // possible to determine whether we're in the "single refcounted + // context" by looking at the metadata of a nonnull context pointer + // and checking whether it is TargetHeapMetadata. + hasSingleSwiftRefcountedContext = No; + } + if (!bindings.empty()) { hasSingleSwiftRefcountedContext = No; auto bindingsSize = bindings.getBufferSize(IGF.IGM); @@ -1590,8 +2050,8 @@ Optional irgen::emitFunctionPartialApplication( && argTypeInfos.size() == argConventions.size() && "argument info lists out of sync"); HeapLayout layout(IGF.IGM, LayoutStrategy::Optimal, argValTypes, argTypeInfos, - /*typeToFill*/ nullptr, - std::move(bindings)); + /*typeToFill*/ nullptr, std::move(bindings), + /*bindingsIndex*/ origType->isAsync() ? 1 : 0); llvm::Value *data; @@ -1622,7 +2082,16 @@ Optional irgen::emitFunctionPartialApplication( Address dataAddr = layout.emitCastTo(IGF, data); unsigned i = 0; - + + if (origType->isAsync()) { + auto &fieldLayout = layout.getElement(i); + auto &fieldTI = fieldLayout.getType(); + Address fieldAddr = fieldLayout.project(IGF, dataAddr, offsets); + cast(fieldTI).initialize(IGF, args, fieldAddr, + isOutlined); + ++i; + } + // Store necessary bindings, if we have them. if (layout.hasBindings()) { auto &bindingsLayout = layout.getElement(i); diff --git a/lib/IRGen/GenHeap.cpp b/lib/IRGen/GenHeap.cpp index ec3d205149d3a..09f7e396e87b4 100644 --- a/lib/IRGen/GenHeap.cpp +++ b/lib/IRGen/GenHeap.cpp @@ -257,18 +257,19 @@ HeapLayout::HeapLayout(IRGenModule &IGM, LayoutStrategy strategy, ArrayRef fieldTypes, ArrayRef fieldTypeInfos, llvm::StructType *typeToFill, - NecessaryBindings &&bindings) - : StructLayout(IGM, /*decl=*/nullptr, LayoutKind::HeapObject, strategy, - fieldTypeInfos, typeToFill), - ElementTypes(fieldTypes.begin(), fieldTypes.end()), - Bindings(std::move(bindings)) -{ + NecessaryBindings &&bindings, unsigned bindingsIndex) + : StructLayout(IGM, /*decl=*/nullptr, LayoutKind::HeapObject, strategy, + fieldTypeInfos, typeToFill), + ElementTypes(fieldTypes.begin(), fieldTypes.end()), + Bindings(std::move(bindings)), BindingsIndex(bindingsIndex) { #ifndef NDEBUG assert(fieldTypeInfos.size() == fieldTypes.size() && "type infos don't match types"); if (!Bindings.empty()) { - assert(fieldTypeInfos.size() >= 1 && "no field for bindings"); - auto fixedBindingsField = dyn_cast(fieldTypeInfos[0]); + assert(fieldTypeInfos.size() >= (bindingsIndex + 1) && + "no field for bindings"); + auto fixedBindingsField = + dyn_cast(fieldTypeInfos[bindingsIndex]); assert(fixedBindingsField && "bindings field is not fixed size"); assert(fixedBindingsField->getFixedSize() @@ -425,7 +426,8 @@ static llvm::Function *createDtorFn(IRGenModule &IGM, if (layout.hasBindings()) { // The type metadata bindings should be at a fixed offset, so we can pass // None for NonFixedOffsets. If we didn't, we'd have a chicken-egg problem. - auto bindingsAddr = layout.getElement(0).project(IGF, structAddr, None); + auto bindingsAddr = layout.getElement(layout.getBindingsIndex()) + .project(IGF, structAddr, None); layout.getBindings().restore(IGF, bindingsAddr, MetadataState::Complete); } diff --git a/lib/IRGen/GenHeap.h b/lib/IRGen/GenHeap.h index 7bc4792877bf0..2b35a56affd3a 100644 --- a/lib/IRGen/GenHeap.h +++ b/lib/IRGen/GenHeap.h @@ -37,6 +37,7 @@ namespace irgen { class HeapLayout : public StructLayout { SmallVector ElementTypes; NecessaryBindings Bindings; + unsigned BindingsIndex; mutable llvm::Constant *privateMetadata = nullptr; public: @@ -44,8 +45,8 @@ class HeapLayout : public StructLayout { ArrayRef elementTypes, ArrayRef elementTypeInfos, llvm::StructType *typeToFill = 0, - NecessaryBindings &&bindings = {}); - + NecessaryBindings &&bindings = {}, unsigned bindingsIndex = 0); + /// True if the heap object carries type bindings. /// /// If true, the first element of the heap layout will be the type metadata @@ -58,6 +59,12 @@ class HeapLayout : public StructLayout { return Bindings; } + unsigned getBindingsIndex() const { return BindingsIndex; } + + unsigned getIndexAfterBindings() const { + return BindingsIndex + (hasBindings() ? 1 : 0); + } + /// Get the types of the elements. ArrayRef getElementTypes() const { return ElementTypes; diff --git a/lib/IRGen/GenReflection.cpp b/lib/IRGen/GenReflection.cpp index 6790794422dd0..bc3ab970407b2 100644 --- a/lib/IRGen/GenReflection.cpp +++ b/lib/IRGen/GenReflection.cpp @@ -1025,8 +1025,8 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder { return true; } - auto ElementTypes = Layout.getElementTypes().slice( - Layout.hasBindings() ? 1 : 0); + auto ElementTypes = + Layout.getElementTypes().slice(Layout.getIndexAfterBindings()); for (auto ElementType : ElementTypes) { auto SwiftType = ElementType.getASTType(); if (SwiftType->hasOpenedExistential()) @@ -1040,7 +1040,7 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder { /// We'll keep track of how many things are in the bindings struct with its /// own count in the capture descriptor. ArrayRef getElementTypes() { - return Layout.getElementTypes().slice(Layout.hasBindings() ? 1 : 0); + return Layout.getElementTypes().slice(Layout.getIndexAfterBindings()); } /// Build a map from generic parameter -> source of its metadata at runtime. diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 542f6c0ac5e17..e35b43b3f1ddf 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1215,11 +1215,15 @@ class AsyncNativeCCEntryPointArgumentEmission final const Address dataAddr; unsigned polymorphicParameterIndex = 0; - llvm::Value *loadValue(ElementLayout layout) { + Explosion loadExplosion(ElementLayout layout) { Address addr = layout.project(IGF, dataAddr, /*offsets*/ llvm::None); auto &ti = cast(layout.getType()); Explosion explosion; ti.loadAsTake(IGF, addr, explosion); + return explosion; + } + llvm::Value *loadValue(ElementLayout layout) { + auto explosion = loadExplosion(layout); return explosion.claimNext(); } @@ -1242,12 +1246,9 @@ class AsyncNativeCCEntryPointArgumentEmission final } Explosion getArgumentExplosion(unsigned index, unsigned size) override { assert(size > 0); - Explosion result; - for (unsigned i = index, end = index + size; i < end; ++i) { - auto argumentLayout = layout.getArgumentLayout(i); - auto *value = loadValue(argumentLayout); - result.add(value); - } + auto argumentLayout = layout.getArgumentLayout(index); + auto result = loadExplosion(argumentLayout); + assert(result.size() == size); return result; } bool requiresIndirectResult(SILType retType) override { return false; } @@ -1309,7 +1310,8 @@ class AsyncNativeCCEntryPointArgumentEmission final return loadValue(fieldLayout); } llvm::Value *getCoroutineBuffer() override { - llvm_unreachable("unimplemented"); + llvm_unreachable( + "async functions do not use a fixed size coroutine buffer"); } }; @@ -2936,7 +2938,6 @@ static bool isSimplePartialApply(IRGenFunction &IGF, PartialApplyInst *i) { } void IRGenSILFunction::visitPartialApplyInst(swift::PartialApplyInst *i) { - // TODO: Handle async! SILValue v(i); if (isSimplePartialApply(*this, i)) { @@ -2985,6 +2986,19 @@ void IRGenSILFunction::visitPartialApplyInst(swift::PartialApplyInst *i) { Explosion llArgs; + if (i->getOrigCalleeType()->isAsync()) { + auto result = getPartialApplicationFunction(*this, i->getCallee(), + i->getSubstitutionMap(), + i->getSubstCalleeType()); + llvm::Value *innerContext = std::get<1>(result); + auto layout = + getAsyncContextLayout(*this, i->getOrigCalleeType(), + i->getSubstCalleeType(), i->getSubstitutionMap()); + auto size = getDynamicAsyncContextSize( + *this, layout, i->getOrigCalleeType(), innerContext); + llArgs.add(size); + } + // Lower the parameters in the callee's generic context. { GenericContextScope scope(IGM, diff --git a/test/IRGen/async/partial_apply.sil b/test/IRGen/async/partial_apply.sil new file mode 100644 index 0000000000000..cf41d13636db2 --- /dev/null +++ b/test/IRGen/async/partial_apply.sil @@ -0,0 +1,527 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_struct.swiftmodule -module-name=resilient_struct %S/../../Inputs/resilient_struct.swift +// RUN: %target-swift-frontend -I %t -emit-ir %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: CPU=x86_64 +// REQUIRES: concurrency + +import Builtin +import Swift +import resilient_struct + +class SwiftClass {} +sil_vtable SwiftClass {} +sil @$s13partial_apply10SwiftClassCfD : $@async @convention(method) (SwiftClass) -> () + +sil @partially_applyable_to_class : $@async @convention(thin) (@owned SwiftClass) -> () +sil @partially_applyable_to_two_classes : $@async @convention(thin) (@owned SwiftClass, @owned SwiftClass) -> () + +sil @use_closure : $@async @convention(thin) (@noescape @async @callee_guaranteed () -> ()) -> () + +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_class(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +sil @partial_apply_class : $@async @convention(thin) (SwiftClass) -> @async @callee_owned () -> () { +entry(%c : $SwiftClass): + %f = function_ref @partially_applyable_to_class : $@async @convention(thin) (@owned SwiftClass) -> () + %g = partial_apply %f(%c) : $@async @convention(thin) (@owned SwiftClass) -> () + return %g : $@async @callee_owned () -> () +} + +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_class_on_stack(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +sil @partial_apply_class_on_stack : $@async @convention(thin) (@owned SwiftClass) -> () { +entry(%a : $SwiftClass): + %f = function_ref @partially_applyable_to_class : $@async @convention(thin) (@owned SwiftClass) -> () + %c = partial_apply [callee_guaranteed] [on_stack] %f(%a) : $@async @convention(thin) (@owned SwiftClass) -> () + %use = function_ref @use_closure : $@async @convention(thin) (@noescape @async @callee_guaranteed () -> ()) -> () + apply %use(%c) : $@async @convention(thin) (@noescape @async @callee_guaranteed () -> ()) -> () + dealloc_stack %c : $@noescape @async @callee_guaranteed () ->() + strong_release %a : $SwiftClass + %t = tuple() + return %t : $() +} + +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_two_classes_on_stack(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_two_classes_on_stack : $@async @convention(thin) (@owned SwiftClass, @owned SwiftClass) -> () { +entry(%a : $SwiftClass, %b: $SwiftClass): + %f = function_ref @partially_applyable_to_two_classes : $@async @convention(thin) (@owned SwiftClass, @owned SwiftClass) -> () + %c = partial_apply [callee_guaranteed] [on_stack] %f(%a, %b) : $@async @convention(thin) (@owned SwiftClass, @owned SwiftClass) -> () + %use = function_ref @use_closure : $@async @convention(thin) (@noescape @async @callee_guaranteed () -> ()) -> () + apply %use(%c) : $@async @convention(thin) (@noescape @async @callee_guaranteed () -> ()) -> () + dealloc_stack %c : $@noescape @async @callee_guaranteed () ->() + strong_release %a : $SwiftClass + strong_release %b : $SwiftClass + %t = tuple() + return %t : $() +} +// CHECK-LABEL: define internal swiftcc void @"$s22generic_captured_paramTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { +sil public_external @generic_captured_param : $@async @convention(thin) (Int, @inout T) -> Int + +sil @partial_apply_generic_capture : $@async @convention(thin) (Int) -> @async @callee_owned (Int) -> Int { +entry(%x : $Int): + %a = alloc_stack $Int + store %x to %a : $*Int + %f = function_ref @generic_captured_param : $@async @convention(thin) (Int, @inout T) -> Int + %p = partial_apply %f(%a) : $@async @convention(thin) (Int, @inout T) -> Int + dealloc_stack %a : $*Int + return %p : $@async @callee_owned (Int) -> Int +} + +sil public_external @generic_captured_and_open_param : $@async @convention(thin) (@in T, @inout T) -> @out T + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_open_generic_capture(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +sil @partial_apply_open_generic_capture : $@async @convention(thin) (@inout T) -> @async @callee_owned (@in T) -> @out T { +entry(%a : $*T): + %f = function_ref @generic_captured_and_open_param : $@async @convention(thin) (@in U, @inout U) -> @out U + %p = partial_apply %f(%a) : $@async @convention(thin) (@in U, @inout U) -> @out U + return %p : $@async @callee_owned (@in T) -> @out T +} + +/*****************************************************************************/ +/* Swift-refcounted class captures. Optimizable by using the reference */ +/* as the partial apply context. */ +/*****************************************************************************/ + +sil public_external @guaranteed_captured_class_param : $@async @convention(thin) (Int, @guaranteed SwiftClass) -> Int + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_guaranteed_class_param(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_guaranteed_class_param : $@async @convention(thin) (@owned SwiftClass) -> @async @callee_owned (Int) -> Int { +bb0(%x : $SwiftClass): + %f = function_ref @guaranteed_captured_class_param : $@async @convention(thin) (Int, @guaranteed SwiftClass) -> Int + %p = partial_apply %f(%x) : $@async @convention(thin) (Int, @guaranteed SwiftClass) -> Int + return %p : $@async @callee_owned (Int) -> Int +} + +sil public_external @indirect_guaranteed_captured_class_param : $@async @convention(thin) (Int, @in_guaranteed SwiftClass) -> Int + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_indirect_guaranteed_class_param(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s40indirect_guaranteed_captured_class_paramTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_indirect_guaranteed_class_param : $@async @convention(thin) (@in SwiftClass) -> @async @callee_owned (Int) -> Int { +bb0(%x : $*SwiftClass): + %f = function_ref @indirect_guaranteed_captured_class_param : $@async @convention(thin) (Int, @in_guaranteed SwiftClass) -> Int + %p = partial_apply %f(%x) : $@async @convention(thin) (Int, @in_guaranteed SwiftClass) -> Int + return %p : $@async @callee_owned (Int) -> Int +} + +sil public_external @indirect_consumed_captured_class_param : $@async @convention(thin) (Int, @in SwiftClass) -> Int + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_indirect_consumed_class_param(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s38indirect_consumed_captured_class_paramTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_indirect_consumed_class_param : $@async @convention(thin) (@in SwiftClass) -> @async @callee_owned (Int) -> Int { +bb0(%x : $*SwiftClass): + %f = function_ref @indirect_consumed_captured_class_param : $@async @convention(thin) (Int, @in SwiftClass) -> Int + %p = partial_apply %f(%x) : $@async @convention(thin) (Int, @in SwiftClass) -> Int + return %p : $@async @callee_owned (Int) -> Int +} + +/*****************************************************************************/ +/* A non-trivial capture. Indirect applications can directly reference the */ +/* field from the partial apply context. */ +/*****************************************************************************/ + +struct SwiftClassPair { var x: SwiftClass, y: SwiftClass } + +sil public_external @guaranteed_captured_class_pair_param : $@async @convention(thin) (Int, @guaranteed SwiftClassPair) -> Int + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_guaranteed_class_pair_param(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s36guaranteed_captured_class_pair_paramTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_guaranteed_class_pair_param : $@async @convention(thin) (@owned SwiftClassPair) -> @async @callee_owned (Int) -> Int { +bb0(%x : $SwiftClassPair): + %f = function_ref @guaranteed_captured_class_pair_param : $@async @convention(thin) (Int, @guaranteed SwiftClassPair) -> Int + %p = partial_apply %f(%x) : $@async @convention(thin) (Int, @guaranteed SwiftClassPair) -> Int + return %p : $@async @callee_owned (Int) -> Int +} + +sil public_external @indirect_guaranteed_captured_class_pair_param : $@async @convention(thin) (Int, @in_guaranteed SwiftClassPair) -> Int + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_indirect_guaranteed_class_pair_param(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s45indirect_guaranteed_captured_class_pair_paramTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_indirect_guaranteed_class_pair_param : $@async @convention(thin) (@in SwiftClassPair) -> @async @callee_owned (Int) -> Int { +bb0(%x : $*SwiftClassPair): + %f = function_ref @indirect_guaranteed_captured_class_pair_param : $@async @convention(thin) (Int, @in_guaranteed SwiftClassPair) -> Int + %p = partial_apply %f(%x) : $@async @convention(thin) (Int, @in_guaranteed SwiftClassPair) -> Int + return %p : $@async @callee_owned (Int) -> Int +} + +sil public_external @indirect_consumed_captured_class_pair_param : $@async @convention(thin) (Int, @in SwiftClassPair) -> Int + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_indirect_consumed_class_pair_param(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s43indirect_consumed_captured_class_pair_paramTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_indirect_consumed_class_pair_param : $@async @convention(thin) (@in SwiftClassPair) -> @async @callee_owned (Int) -> Int { +bb0(%x : $*SwiftClassPair): + %f = function_ref @indirect_consumed_captured_class_pair_param : $@async @convention(thin) (Int, @in SwiftClassPair) -> Int + %p = partial_apply %f(%x) : $@async @convention(thin) (Int, @in SwiftClassPair) -> Int + return %p : $@async @callee_owned (Int) -> Int +} + +sil public_external @captured_fixed_and_dependent_params : $@async @convention(thin) (@owned SwiftClass, @in A, Int) -> () + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_indirect_non_fixed_layout(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s35captured_fixed_and_dependent_paramsTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { +sil @partial_apply_indirect_non_fixed_layout : $@async @convention(thin) (@owned SwiftClass, @in T, Int) -> @async @callee_owned () -> () { +bb0(%a : $SwiftClass, %b : $*T, %c : $Int): + %f = function_ref @captured_fixed_and_dependent_params : $@async @convention(thin) (@owned SwiftClass, @in B, Int) -> () + %p = partial_apply %f(%a, %b, %c) : $@async @convention(thin) (@owned SwiftClass, @in C, Int) -> () + return %p : $@async @callee_owned () -> () +} + +sil public_external @captured_dependent_out_param : $@async @convention(thin) (@in A) -> @out A + +sil @partial_apply_with_out_param : $@async @convention(thin) (@in T) -> @async @callee_owned () -> @out T { +bb0(%x : $*T): + %f = function_ref @captured_dependent_out_param : $@async @convention(thin) (@in B) -> @out B + %p = partial_apply %f(%x) : $@async @convention(thin) (@in C) -> @out C + return %p : $@async @callee_owned () -> @out T +} + +// CHECK-LABEL: define internal swiftcc void @"$s28captured_dependent_out_paramTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_dynamic_with_out_param : $@async @convention(thin) (Int32, @owned @async @callee_owned (Int32) -> @out T) -> @async @callee_owned () -> @out T { +bb0(%x : $Int32, %f : $@async @callee_owned (Int32) -> @out T): + %p = partial_apply %f(%x) : $@async @callee_owned (Int32) -> @out T + return %p : $@async @callee_owned () -> @out T +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_dynamic_with_out_param(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +class Base { +} +sil_vtable Base {} + +class Sub : Base { +} + +sil_vtable Sub {} + +sil @parametric_casting_closure : $@async @convention(thin) (@owned Base) -> @owned C { +bb0(%0 : $Base): + %1 = unconditional_checked_cast %0 : $Base to C + return %1 : $C +} + +sil public_external @receive_closure : $@async @convention(thin) (@owned @async @callee_owned () -> (@owned C)) -> () + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test_partial_apply(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +// CHECK-LABEL: define internal swiftcc void @"$s26parametric_casting_closureTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s26parametric_casting_closureTA.{{[0-9]+}}"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil @test_partial_apply : $@async @convention(thin) (@owned Base) -> () { +bb0(%0 : $Base): + %1 = function_ref @parametric_casting_closure : $@async @convention(thin) (@owned Base) -> @owned C + %6 = partial_apply %1() : $@async @convention(thin) (@owned Base) -> @owned C + %2 = partial_apply %1(%0) : $@async @convention(thin) (@owned Base) -> @owned C + %3 = function_ref @receive_closure : $@async @convention(thin) (@owned @async @callee_owned () -> (@owned C)) -> () + %4 = apply %3(%2) : $@async @convention(thin) (@owned @async @callee_owned () -> (@owned C)) -> () + %5 = tuple () + return %5 : $() +} + +sil public_external @partial_empty_box : $@async @convention(thin) (@owned <τ_0_0> { var τ_0_0 } <()>, @inout_aliasable ()) -> () + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @empty_box(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +sil @empty_box : $@async @convention(thin) () -> () { +entry: + // CHECK: [[BOX:%.*]] = call {{.*}}swift_allocEmptyBox + // CHECK: store %swift.refcounted* [[BOX]] + // CHECK: store %swift.opaque* undef + %b = alloc_box $<τ_0_0> { var τ_0_0 } <()> + %ba = project_box %b : $<τ_0_0> { var τ_0_0 } <()>, 0 + %f = function_ref @partial_empty_box : $@async @convention(thin) (@owned <τ_0_0> { var τ_0_0 } <()>, @inout_aliasable ()) -> () + %g = partial_apply %f(%b, %ba) : $@async @convention(thin) (@owned <τ_0_0> { var τ_0_0 } <()>, @inout_aliasable ()) -> () + return undef : $() +} + +protocol P0 {} +protocol P1 { associatedtype X : P0 } +protocol P2 { associatedtype Y : P1 } + +sil hidden_external @complex_generic_function : $@async @convention(thin) (Int) -> () + +sil @partial_apply_complex_generic_function : $@async @convention(thin) (Int) -> () { +bb0(%0 : $Int): + %fn = function_ref @complex_generic_function : $@async @convention(thin) (Int) -> () + %pa = partial_apply %fn (%0) : $@async @convention(thin) (Int) -> () + %result = tuple () + return %result : $() +} +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_complex_generic_function(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s24complex_generic_functionTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +struct ComplexBoundedType {} + +// SR-901: Ensure that a partial_apply which captures bound generic type +// metadata doesn't crash when restoring the generic context. + +sil hidden_external @generic_function : $@async @convention(thin) () -> () +sil @partial_apply_with_generic_type : $@async @convention(thin) () -> () { +bb0: + %fn = function_ref @generic_function : $@async @convention(thin) () -> () + %pa = partial_apply %fn >() : $@async @convention(thin) () -> () + %result = tuple () + return %result : $() +} + +// Crash on partial apply of witness_method without generic signature + +extension Int: P0 {} + +sil hidden_external @concrete_witness_method : $@async @convention(witness_method: P0) (Int, Int) -> () + +sil hidden @partial_apply_witness_method : $@async @convention(thin) (Int) -> () { +bb0(%0 : $Int): + %fn = function_ref @concrete_witness_method : $@async @convention(witness_method: P0) (Int, Int) -> () + %pa = partial_apply %fn (%0) : $@async @convention(witness_method: P0) (Int, Int) -> () + %result = tuple () + return %result : $() +} + + +// Crash on partial apply of a generic enum. +enum GenericEnum { + case X(String) + case Y(T, T, T, T, T) +} +sil public_external @generic_indirect_return : $@async @convention(thin) (Int) -> @owned GenericEnum + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_generic_indirect_return(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s23generic_indirect_returnTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { +sil @partial_apply_generic_indirect_return : $@async @convention(thin) (Int) -> @async @callee_owned () -> @owned GenericEnum { + bb0(%0 : $Int): + %fn = function_ref @generic_indirect_return :$@async @convention(thin) (Int) -> @owned GenericEnum + %pa = partial_apply %fn (%0) : $@async @convention(thin) (Int) -> @owned GenericEnum + return %pa : $@async @callee_owned () -> @owned GenericEnum + +} + +// Crash on partial apply of a generic enum. +enum GenericEnum2 { + case X(String) + case Y(T) +} +sil public_external @generic_indirect_return2 : $@async @convention(thin) (Int) -> @owned GenericEnum2 + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_generic_indirect_return2(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s24generic_indirect_return2TA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { +sil @partial_apply_generic_indirect_return2 : $@async @convention(thin) (Int) -> @async @callee_owned () -> @owned GenericEnum2 { + bb0(%0 : $Int): + %fn = function_ref @generic_indirect_return2 :$@async @convention(thin) (Int) -> @owned GenericEnum2 + %pa = partial_apply %fn (%0) : $@async @convention(thin) (Int) -> @owned GenericEnum2 + return %pa : $@async @callee_owned () -> @owned GenericEnum2 +} + +struct SwiftStruct {} + +sil @fun : $@async @convention(thin) (@thin SwiftStruct.Type, @owned SwiftClass) -> () + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_thin_type(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_thin_type : $@async @convention(thin) (@thin SwiftStruct.Type, @owned SwiftClass) -> @async @callee_owned () -> () { +entry(%0: $@thin SwiftStruct.Type, %1: $SwiftClass): + %fun = function_ref @fun : $@async @convention(thin) (@thin SwiftStruct.Type, @owned SwiftClass) -> () + %closure = partial_apply %fun (%0, %1) : $@async @convention(thin) (@thin SwiftStruct.Type, @owned SwiftClass) -> () + return %closure : $@async @callee_owned () -> () +} + +sil @afun : $@async @convention(thin) (Int) -> @error Error + +// Check that we don't assert on a thin noescape function. +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @convert_thin_test(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +sil @convert_thin_test : $@async @convention(thin) (Int) -> () { +bb(%0 : $Int): + %f = function_ref @afun : $@async @convention(thin) (Int) -> @error Error + %c = convert_function %f : $@async @convention(thin) (Int) -> @error Error to $@async @convention(thin) @noescape (Int) -> @error Error + try_apply %c(%0) : $@async @convention(thin) @noescape (Int) -> @error Error, normal bb2, error bb1 + +bb1(%err: $Error): + %t = tuple () + br bb3(%t: $()) + +bb2(%r : $()): + br bb3(%r : $()) + +bb3(%v : $()): + return %v : $() +} + +struct A1 { + let b: () -> () +} + +struct A2 { + let a: T +} + +class A3 {} + +sil @amethod : $@async @convention(method) (@in_guaranteed A2) -> (@owned A1, @error Error) + +sil @repo : $@async @convention(thin) (@in_guaranteed A2) -> @owned @async @callee_guaranteed () -> (@owned A1, @error Error) { +bb0(%0 : $*A2): + %1 = load %0 : $*A2 + %2 = alloc_stack $A2 + store %1 to %2 : $*A2 + %4 = function_ref @amethod : $@async @convention(method) (@in_guaranteed A2) -> (@owned A1, @error Error) + %5 = partial_apply [callee_guaranteed] %4(%2) : $@async @convention(method) (@in_guaranteed A2) -> (@owned A1, @error Error) + dealloc_stack %2 : $*A2 + return %5 : $@async @callee_guaranteed () -> (@owned A1, @error Error) +} + +sil @capture_class : $@async @convention(thin) (@guaranteed A3) -> () + +// CHECK-LABEL: define{{.*}} swiftcc i8* @partial_apply_stack_in_coroutine(i8* {{.*}} %0, %T13partial_apply2A3C* %1) +sil @partial_apply_stack_in_coroutine : $@yield_once (@owned A3) -> () { +entry(%0: $A3): + %f = function_ref @capture_class : $@async @convention(thin) (@guaranteed A3) -> () + %p = partial_apply [callee_guaranteed] [on_stack] %f(%0) : $@async @convention(thin) (@guaranteed A3) -> () + apply %p() : $@noescape @async @callee_guaranteed () -> () + dealloc_stack %p : $@noescape @async @callee_guaranteed () -> () + %1000 = integer_literal $Builtin.Int32, 1000 + yield (), resume resume, unwind unwind + +resume: + %ret = tuple () + return %ret : $() + +unwind: + unwind +} +sil_vtable A3 {} + + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_callee_guaranteed_indirect_guaranteed_class_pair_param(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_callee_guaranteed_indirect_guaranteed_class_pair_param : $@async @convention(thin) (@in SwiftClassPair) -> @owned @async @callee_guaranteed (Int) -> Int { +bb0(%x : $*SwiftClassPair): + %f = function_ref @indirect_guaranteed_captured_class_pair_param : $@async @convention(thin) (Int, @in_guaranteed SwiftClassPair) -> Int + %p = partial_apply [callee_guaranteed] %f(%x) : $@async @convention(thin) (Int, @in_guaranteed SwiftClassPair) -> Int + return %p : $@async @callee_guaranteed(Int) -> (Int) +} + +sil public_external @use_closure2 : $@async @convention(thin) (@noescape @async @callee_guaranteed (Int) -> Int) -> () + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_stack_callee_guaranteed_indirect_guaranteed_class_pair_param(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s45indirect_guaranteed_captured_class_pair_paramTA.67"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil @partial_apply_stack_callee_guaranteed_indirect_guaranteed_class_pair_param : $@async @convention(thin) (@in_guaranteed SwiftClassPair) -> () { +bb0(%x : $*SwiftClassPair): + %f = function_ref @indirect_guaranteed_captured_class_pair_param : $@async @convention(thin) (Int, @in_guaranteed SwiftClassPair) -> Int + %p = partial_apply [callee_guaranteed] [on_stack] %f(%x) : $@async @convention(thin) (Int, @in_guaranteed SwiftClassPair) -> Int + %u = function_ref @use_closure2 : $@async @convention(thin) (@noescape @async @callee_guaranteed (Int) -> Int) -> () + %r = apply %u(%p) : $@async @convention(thin) (@noescape @async @callee_guaranteed (Int) -> Int) -> () + dealloc_stack %p : $@noescape @async @callee_guaranteed (Int) ->(Int) + %t = tuple() + return %t : $() +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_stack_callee_guaranteed_indirect_in_class_pair_param(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s37indirect_in_captured_class_pair_paramTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil public_external @indirect_in_captured_class_pair_param : $@async @convention(thin) (Int, @in SwiftClassPair) -> Int + +sil @partial_apply_stack_callee_guaranteed_indirect_in_class_pair_param : $@async @convention(thin) (@in SwiftClassPair) -> () { +bb0(%x : $*SwiftClassPair): + %f = function_ref @indirect_in_captured_class_pair_param : $@async @convention(thin) (Int, @in SwiftClassPair) -> Int + %p = partial_apply [callee_guaranteed] [on_stack] %f(%x) : $@async @convention(thin) (Int, @in SwiftClassPair) -> Int + %u = function_ref @use_closure2 : $@async @convention(thin) (@noescape @async @callee_guaranteed (Int) -> Int) -> () + %r = apply %u(%p) : $@async @convention(thin) (@noescape @async @callee_guaranteed (Int) -> Int) -> () + dealloc_stack %p : $@noescape @async @callee_guaranteed (Int) ->(Int) + destroy_addr %x: $*SwiftClassPair + %t = tuple() + return %t : $() +} + + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @partial_apply_stack_callee_guaranteed_indirect_in_constant_class_pair_param(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s46indirect_in_constant_captured_class_pair_paramTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil public_external @indirect_in_constant_captured_class_pair_param : $@async @convention(thin) (Int, @in_constant SwiftClassPair) -> Int + +sil @partial_apply_stack_callee_guaranteed_indirect_in_constant_class_pair_param : $@async @convention(thin) (@in SwiftClassPair) -> () { +bb0(%x : $*SwiftClassPair): + %f = function_ref @indirect_in_constant_captured_class_pair_param : $@async @convention(thin) (Int, @in_constant SwiftClassPair) -> Int + %p = partial_apply [callee_guaranteed] [on_stack] %f(%x) : $@async @convention(thin) (Int, @in_constant SwiftClassPair) -> Int + %u = function_ref @use_closure2 : $@async @convention(thin) (@noescape @async @callee_guaranteed (Int) -> Int) -> () + %r = apply %u(%p) : $@async @convention(thin) (@noescape @async @callee_guaranteed (Int) -> Int) -> () + dealloc_stack %p : $@noescape @async @callee_guaranteed (Int) ->(Int) + destroy_addr %x: $*SwiftClassPair + %t = tuple() + return %t : $() +} + +sil public_external @closure : $@async @convention(thin) (@in_guaranteed ResilientInt, @guaranteed SwiftClass) -> () + +// Make sure that we use the heap header size (16) for the initial offset. +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test_initial_offset(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +sil @test_initial_offset : $@async @convention(thin) (@in_guaranteed ResilientInt, @guaranteed SwiftClass) -> () { +bb0(%x : $*ResilientInt, %y : $SwiftClass): + %f = function_ref @closure : $@async @convention(thin) (@in_guaranteed ResilientInt, @guaranteed SwiftClass) -> () + %p = partial_apply [callee_guaranteed] %f(%x, %y) : $@async @convention(thin) (@in_guaranteed ResilientInt, @guaranteed SwiftClass) -> () + release_value %p : $@async @callee_guaranteed () ->() + %t = tuple() + return %t : $() +} + +protocol Proto1 {} +protocol Proto2 {} +struct EmptyType : Proto1 { } + +struct SomeType : Proto2 { + var d : ResilientInt // some resilient type + var x : Int +} + +sil @foo : $@async @convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : Proto1, τ_0_1 : Proto2> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> () + +// CHECK-64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @empty_followed_by_non_fixed(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +sil @empty_followed_by_non_fixed : $@async @convention(thin) (EmptyType, @in_guaranteed SomeType) -> () { +entry(%0 : $EmptyType, %1: $*SomeType): + %5 = alloc_stack $EmptyType + store %0 to %5 : $*EmptyType + %31 = function_ref @foo : $@async @convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : Proto1, τ_0_1 : Proto2> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> () + %32 = alloc_stack $EmptyType + copy_addr %5 to [initialization] %32 : $*EmptyType + %34 = alloc_stack $SomeType + copy_addr %1 to [initialization] %34 : $*SomeType // id: %35 + %36 = partial_apply [callee_guaranteed] %31(%32, %34) : $@async @convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : Proto1, τ_0_1 : Proto2> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> () + release_value %36: $@async @callee_guaranteed () ->() + dealloc_stack %34 : $*SomeType + dealloc_stack %32 : $*EmptyType + dealloc_stack %5 : $*EmptyType + %40 = tuple() + return %40 : $() +} + +struct FixedType { + var f: Int32 +} +// CHECK-64-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @fixed_followed_by_empty_followed_by_non_fixed(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { + +sil @foo2 : $@async @convention(thin) <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1, @in_guaranteed τ_0_2) -> () +sil @fixed_followed_by_empty_followed_by_non_fixed : $@async @convention(thin) (EmptyType, @in_guaranteed SomeType, FixedType) -> () { +entry(%0 : $EmptyType, %1: $*SomeType, %3: $FixedType): + %5 = alloc_stack $EmptyType + store %0 to %5 : $*EmptyType + %7 = alloc_stack $FixedType + store %3 to %7 : $*FixedType + %31 = function_ref @foo2 : $@async @convention(thin) <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1, @in_guaranteed τ_0_2) -> () + %32 = alloc_stack $EmptyType + copy_addr %5 to [initialization] %32 : $*EmptyType + %34 = alloc_stack $SomeType + copy_addr %1 to [initialization] %34 : $*SomeType // id: %35 + %36 = partial_apply [callee_guaranteed] %31(%7, %32, %34) : $@async @convention(thin) <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1, @in_guaranteed τ_0_2) -> () + release_value %36: $@async @callee_guaranteed () ->() + dealloc_stack %34 : $*SomeType + dealloc_stack %32 : $*EmptyType + dealloc_stack %7 : $*FixedType + dealloc_stack %5 : $*EmptyType + %40 = tuple() + return %40 : $() +} diff --git a/test/IRGen/async/partial_apply_forwarder.sil b/test/IRGen/async/partial_apply_forwarder.sil new file mode 100644 index 0000000000000..e024f9971508e --- /dev/null +++ b/test/IRGen/async/partial_apply_forwarder.sil @@ -0,0 +1,231 @@ +// RUN: %target-swift-frontend -enable-experimental-concurrency -enable-objc-interop -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-objc +// RUN: %target-swift-frontend -enable-experimental-concurrency -disable-objc-interop -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-native + +// REQUIRES: concurrency + +sil_stage canonical + +import Builtin + +public protocol P {} +public class C : P {} +public class D {} +class E {} + +public protocol Observable { + associatedtype Result + func subscribe(o: T) async -> () +} + +public protocol Observer { + associatedtype Result +} + +sil hidden @witness_method : $@async @convention(thin) (@in S) -> @owned @async @callee_owned (@in O) -> () { +bb0(%0 : $*S): + %1 = witness_method $S, #Observable.subscribe : $@async @convention(witness_method: Observable) <τ_0_0 where τ_0_0 : Observable><τ_1_0 where τ_1_0 : Observer, τ_0_0.Result == τ_1_0.Result> (@in τ_1_0, @in_guaranteed τ_0_0) -> () + %2 = partial_apply %1(%0) : $@async @convention(witness_method: Observable) <τ_0_0 where τ_0_0 : Observable><τ_1_0 where τ_1_0 : Observer, τ_0_0.Result == τ_1_0.Result> (@in τ_1_0, @in_guaranteed τ_0_0) -> () + return %2 : $@async @callee_owned (@in O) -> () +} + +// CHECK-LABEL: define internal swiftcc void @"$s23unspecialized_uncurriedTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil hidden @specialized_curried : $@async @convention(thin) (@owned E) -> @owned @async @callee_owned () -> @owned D { +bb0(%0 : $E): + %1 = function_ref @unspecialized_uncurried : $@async @convention(method) <τ_0_0 where τ_0_0 : P> (@guaranteed E) -> @owned D<τ_0_0> + %2 = partial_apply %1(%0) : $@async @convention(method) <τ_0_0 where τ_0_0 : P> (@guaranteed E) -> @owned D<τ_0_0> + return %2 : $@async @callee_owned () -> @owned D +} + +sil hidden_external @unspecialized_uncurried : $@async @convention(method) <τ_0_0 where τ_0_0 : P> (@guaranteed E) -> @owned D<τ_0_0> + + +// CHECK-LABEL: define internal swiftcc void @"$s7takingPTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { +sil hidden_external @takingP : $@async @convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () + +sil hidden @reabstract_context : $@async @convention(thin) (@owned C) -> () { +bb0(%0 : $C): + %6 = alloc_stack $C + store %0 to %6 : $*C + %8 = function_ref @takingP : $@async @convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () + %9 = partial_apply %8(%6) : $@async @convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () + dealloc_stack %6 : $*C + strong_release %9 : $@async @callee_owned() -> () + %10 = tuple () + return %10 : $() +} + +public protocol Q { + associatedtype Update +} + +public struct BaseProducer : Q { + public typealias Update = T +} + +public class WeakBox {} + +public struct EmptyType {} + +sil hidden_external @takingQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () +sil hidden_external @takingQAndEmpty : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>, EmptyType) -> () +sil hidden_external @takingEmptyAndQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (EmptyType, @owned WeakBox<τ_0_0>) -> () + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @bind_polymorphic_param_from_context(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s7takingQTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil public @bind_polymorphic_param_from_context : $@async @convention(thin) <τ_0_1>(@in τ_0_1) -> @owned @async @callee_owned () -> () { +bb0(%0 : $*τ_0_1): + %1 = alloc_ref $WeakBox> + %8 = function_ref @takingQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () + %9 = partial_apply %8>(%1) : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () + return %9 : $@async @callee_owned () -> () +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @bind_polymorphic_param_from_context_2(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +sil public @bind_polymorphic_param_from_context_2 : $@async @convention(thin) <τ_0_1>(@in τ_0_1, EmptyType) -> @owned @async @callee_owned () -> () { +bb0(%0 : $*τ_0_1, %2: $EmptyType): + %1 = alloc_ref $WeakBox> + %8 = function_ref @takingQAndEmpty : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>, EmptyType) -> () + %9 = partial_apply %8>(%1, %2) : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>, EmptyType) -> () + return %9 : $@async @callee_owned () -> () +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @bind_polymorphic_param_from_context_3(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +sil public @bind_polymorphic_param_from_context_3 : $@async @convention(thin) <τ_0_1>(@in τ_0_1, EmptyType) -> @owned @async @callee_owned () -> () { +bb0(%0 : $*τ_0_1, %2: $EmptyType): + %1 = alloc_ref $WeakBox> + %8 = function_ref @takingEmptyAndQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (EmptyType, @owned WeakBox<τ_0_0>) -> () + %9 = partial_apply %8>(%2, %1) : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (EmptyType, @owned WeakBox<τ_0_0>) -> () + return %9 : $@async @callee_owned () -> () +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @bind_polymorphic_param_from_forwarder_parameter(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s7takingQTA.19"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { + +sil public @bind_polymorphic_param_from_forwarder_parameter : $@async @convention(thin) <τ_0_1>(@in τ_0_1) -> () { +bb0(%0 : $*τ_0_1): + %1 = alloc_ref $WeakBox> + %8 = function_ref @takingQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () + %9 = partial_apply %8>() : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () + %10 = tuple () + return %10 : $() +} + +struct S { + var x : Builtin.Int64 +} + +sil hidden_external @takingQAndS : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (S, @owned WeakBox<τ_0_0>) -> () + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @bind_polymorphic_param_from_context_with_layout(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s11takingQAndSTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { +sil public @bind_polymorphic_param_from_context_with_layout : $@async @convention(thin) <τ_0_1>(@in τ_0_1, S) -> @owned @async @callee_owned () -> () { +bb0(%0 : $*τ_0_1, %1: $S): + %2 = alloc_ref $WeakBox> + %8 = function_ref @takingQAndS : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (S, @owned WeakBox<τ_0_0>) -> () + %9 = partial_apply %8>(%1, %2) : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (S, @owned WeakBox<τ_0_0>) -> () + return %9 : $@async @callee_owned () -> () +} + +public class Empty {} + +sil private @inner_closure : $@async @convention(thin) (Empty) -> @owned Empty { +bb0(%0 : $Empty): + return %0 : $Empty +} + +sil hidden @returns_closure : $@async @convention(thin) (Empty) -> (@owned Empty, @async @callee_guaranteed @owned (Empty) -> @owned Empty) { +bb0(%0 : $Empty): + %1 = function_ref @inner_closure : $@async @convention(thin) <τ_0_0> (Empty<τ_0_0>) -> @owned Empty<τ_0_0> + %2 = partial_apply [callee_guaranteed] %1() : $@async @convention(thin) <τ_0_0> (Empty<τ_0_0>) -> @owned Empty<τ_0_0> + %5 = tuple (%0 : $Empty, %2 : $@async @callee_guaranteed (Empty) -> @owned Empty) + return %5 : $(Empty, @async @callee_guaranteed (Empty) -> @owned Empty) +} + +sil hidden @specializes_closure_returning_closure : $@async @convention(thin) () -> @async @callee_guaranteed (Empty) -> (@owned Empty, @owned @async @callee_guaranteed (Empty) -> @owned Empty) { +bb0: + %0 = function_ref @returns_closure : $@async @convention(thin) <τ_0_0> (Empty<τ_0_0>) -> (@owned Empty<τ_0_0>, @owned @async @callee_guaranteed (Empty<τ_0_0>) -> @owned Empty<τ_0_0>) + %1 = partial_apply [callee_guaranteed] %0() : $@async @convention(thin) <τ_0_0> (Empty<τ_0_0>) -> (@owned Empty<τ_0_0>, @owned @async @callee_guaranteed (Empty<τ_0_0>) -> @owned Empty<τ_0_0>) + return %1 : $@async @callee_guaranteed (Empty) -> (@owned Empty, @owned @async @callee_guaranteed (Empty) -> @owned Empty) +} + +// CHECK-LABEL: define hidden swiftcc void @specializes_closure_returning_closure(%swift.context* {{%[0-9]+}}) {{#[0-9]+}} { +// CHECK-LABEL: define internal swiftcc void @"$s15returns_closureTA"(%swift.context* {{%[0-9]+}}, %swift.refcounted* swiftself {{%[0-9]+}}) {{#[0-9]+}} { +protocol MyEquatable { + static func isEqual (lhs: Self, rhs: Self) -> Builtin.Int1 +} + +protocol MyExtended : MyEquatable { + func extended() +} +public struct Inner { + public init() + public init(_ e: Element) +} + +extension Inner : MyEquatable where Element : MyEquatable { + public static func isEqual (lhs: Inner, rhs: Inner) -> Builtin.Int1 +} + +public struct Outer { + init() +} + +extension Outer : MyEquatable where Value : MyEquatable { + public static func isEqual (lhs: Outer, rhs: Outer) -> Builtin.Int1 +} + +public struct Outermost { +} + +sil @$closure : $@async @convention(method) (Outer, Outer, @thin Outer.Type) -> Builtin.Int1 +sil @$closure2 : $@async @convention(method) (Outermost, Outermost, @thin Outermost.Type) -> Builtin.Int1 + +sil @$dont_crash_test_capture_specialized_conditional_conformance : $@async @convention(thin) (Outer>) -> () { +bb0(%0 : $Outer>): + %2 = alloc_stack $Outer> + store %0 to %2 : $*Outer> + %4 = metatype $@thin Outer>.Type + %5 = function_ref @$closure : $@async @convention(method) <τ_0_0 where τ_0_0 : MyEquatable> (Outer<τ_0_0>, Outer<τ_0_0>, @thin Outer<τ_0_0>.Type) -> Builtin.Int1 + %6 = partial_apply [callee_guaranteed] %5>(%4) : $@async @convention(method) <τ_0_0 where τ_0_0 : MyEquatable> (Outer<τ_0_0>, Outer<τ_0_0>, @thin Outer<τ_0_0>.Type) -> Builtin.Int1 + strong_release %6 : $@async @callee_guaranteed (Outer>, Outer>) -> Builtin.Int1 + dealloc_stack %2 : $*Outer> + %15 = tuple () + return %15 : $() +} + +protocol AssocType { + associatedtype A : MyExtended +} + +sil @$dont_crash_test_capture_specialized_conditional_conformance_associated_type : $@async @convention(thin) (Outer>) -> () { +bb0(%0 : $Outer>): + %2 = alloc_stack $Outer> + store %0 to %2 : $*Outer> + %4 = metatype $@thin Outer>.Type + %5 = function_ref @$closure : $@async @convention(method) <τ_0_0 where τ_0_0 : MyEquatable> (Outer<τ_0_0>, Outer<τ_0_0>, @thin Outer<τ_0_0>.Type) -> Builtin.Int1 + %6 = partial_apply [callee_guaranteed] %5>(%4) : $@async @convention(method) <τ_0_0 where τ_0_0 : MyEquatable> (Outer<τ_0_0>, Outer<τ_0_0>, @thin Outer<τ_0_0>.Type) -> Builtin.Int1 + strong_release %6 : $@async @callee_guaranteed (Outer>, Outer>) -> Builtin.Int1 + dealloc_stack %2 : $*Outer> + %15 = tuple () + return %15 : $() +} + +sil @$dont_crash_test_capture_specialized_conditional_conformance_nested : $@async @convention(thin) (Outer>) -> () { +bb0(%0 : $Outer>): + %4 = metatype $@thin Outermost>>.Type + %5 = function_ref @$closure2 : $@async @convention(method) (Outermost, Outermost, @thin Outermost.Type) -> Builtin.Int1 + %6 = partial_apply [callee_guaranteed] %5>>(%4) : $@async @convention(method) (Outermost, Outermost, @thin Outermost.Type) -> Builtin.Int1 + strong_release %6 : $@async @callee_guaranteed (Outermost>>, Outermost>>) -> Builtin.Int1 + %15 = tuple () + return %15 : $() +} + + +sil_vtable WeakBox {} +sil_vtable C {} +sil_vtable D {} +sil_vtable E {} +sil_vtable Empty {} +sil_witness_table C: P module main {} diff --git a/test/IRGen/async/run-partialapply-capture-class-to-void.sil b/test/IRGen/async/run-partialapply-capture-class-to-void.sil new file mode 100644 index 0000000000000..fcffde323e0d0 --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-class-to-void.sil @@ -0,0 +1,93 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + +class S { + deinit + init() +} + +sil hidden [exact_self_class] @S_allocating_init : $@convention(method) (@thick S.Type) -> @owned S { +bb0(%0 : $@thick S.Type): + %1 = alloc_ref $S + %2 = function_ref @$S_init : $@convention(method) (@owned S) -> @owned S + %3 = apply %2(%1) : $@convention(method) (@owned S) -> @owned S + return %3 : $S +} + +sil hidden @$S_init : $@convention(method) (@owned S) -> @owned S { +bb0(%0 : $S): + return %0 : $S +} + +sil hidden @$S_deinit : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject { +bb0(%0 : $S): + %2 = unchecked_ref_cast %0 : $S to $Builtin.NativeObject + return %2 : $Builtin.NativeObject +} + +sil hidden @S_deallocating_deinit : $@convention(method) (@owned S) -> () { +bb0(%0 : $S): + %2 = function_ref @$S_deinit : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject + %3 = apply %2(%0) : $@convention(method) (@guaranteed S) -> @owned Builtin.NativeObject + %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $S + dealloc_ref %4 : $S + %6 = tuple () + return %6 : $() +} + +sil_vtable S { + #S.init!allocator: (S.Type) -> () -> S : @S_allocating_init + #S.deinit!deallocator: @S_deallocating_deinit +} + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @classinstanceSToVoid(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil @classinstanceSToVoid : $@async @convention(thin) (@owned S) -> () { +entry(%c : $S): + %class_addr = alloc_stack $S + store %c to %class_addr : $*S + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %result = apply %printGeneric(%class_addr) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: main.S + dealloc_stack %class_addr : $*S + return %result : $() +} + +sil @partial_apply_class : $@async @convention(thin) (S) -> @async @callee_owned () -> () { +entry(%instance : $S): + %classinstanceSToVoid = function_ref @classinstanceSToVoid : $@async @convention(thin) (@owned S) -> () + %partiallyApplied = partial_apply %classinstanceSToVoid(%instance) : $@async @convention(thin) (@owned S) -> () + return %partiallyApplied : $@async @callee_owned () -> () +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %s_type = metatype $@thick S.Type + %allocating_init = function_ref @S_allocating_init : $@convention(method) (@thick S.Type) -> @owned S + %instance = apply %allocating_init(%s_type) : $@convention(method) (@thick S.Type) -> @owned S + strong_retain %instance : $S + + %partial_apply_class = function_ref @partial_apply_class : $@async @convention(thin) (S) -> @async @callee_owned () -> () + %partiallyApplied = apply %partial_apply_class(%instance) : $@async @convention(thin) (S) -> @async @callee_owned () -> () + %result = apply %partiallyApplied() : $@async @callee_owned () -> () + + strong_release %instance : $S + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/async/run-partialapply-capture-generic_conformer-and-generic-to-void.sil b/test/IRGen/async/run-partialapply-capture-generic_conformer-and-generic-to-void.sil new file mode 100644 index 0000000000000..b84751bf93e70 --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-generic_conformer-and-generic-to-void.sil @@ -0,0 +1,87 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +public protocol Observable { + associatedtype Result + func subscribe(o: T) async -> () +} +class ObservableImpl : Observable { + typealias Result = Void + func subscribe(o: T) async -> () +} +sil_vtable ObservableImpl { +} +// CHECK-LL: define hidden swiftcc void @subscribe_ObservableImpl_Observable(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @subscribe_ObservableImpl_Observable : $@convention(witness_method: Observable) @async <τ_0_0 where τ_0_0 : Observer> (@in_guaranteed τ_0_0, @in_guaranteed ObservableImpl) -> () { +bb0(%observer : $*τ_0_0, %self : $*ObservableImpl): + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %printGeneric_result1 = apply %printGeneric<τ_0_0>(%observer) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: main.ObserverImpl + %printGeneric_result2 = apply %printGeneric(%self) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: main.ObservableImpl + %result = tuple () + return %result : $() +} +sil_witness_table ObservableImpl : Observable module main { + associated_type Result : () + method #Observable.subscribe: (Self) -> (T) async -> () : @subscribe_ObservableImpl_Observable +} + +public protocol Observer { + associatedtype Result +} + +class ObserverImpl : Observer { + typealias Result = Void +} +sil_vtable ObserverImpl {} +sil_witness_table ObserverImpl : Observer module main { + associated_type Result : () +} + +// CHECK-LL: define internal swiftcc void @"$sTA"(%swift.context* {{%[0-9]*}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @witness_method : $@convention(thin) (@in S) -> @owned @async @callee_owned (@in O) -> () { +bb0(%0 : $*S): + %1 = witness_method $S, #Observable.subscribe : $@async @convention(witness_method: Observable) <τ_0_0 where τ_0_0 : Observable><τ_1_0 where τ_1_0 : Observer, τ_0_0.Result == τ_1_0.Result> (@in τ_1_0, @in_guaranteed τ_0_0) -> () + %2 = partial_apply %1(%0) : $@async @convention(witness_method: Observable) <τ_0_0 where τ_0_0 : Observable><τ_1_0 where τ_1_0 : Observer, τ_0_0.Result == τ_1_0.Result> (@in τ_1_0, @in_guaranteed τ_0_0) -> () + return %2 : $@async @callee_owned (@in O) -> () +} + + + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %observableImpl = alloc_ref $ObservableImpl + strong_retain %observableImpl : $ObservableImpl + %observableImpl_addr = alloc_stack $ObservableImpl + store %observableImpl to %observableImpl_addr : $*ObservableImpl + %witness_method = function_ref @witness_method : $@convention(thin) (@in S) -> @owned @async @callee_owned (@in O) -> () + %partiallyApplied = apply %witness_method(%observableImpl_addr) : $@convention(thin) (@in S) -> @owned @async @callee_owned (@in O) -> () + %observerImpl = alloc_ref $ObserverImpl + %observerImpl_addr = alloc_stack $ObserverImpl + store %observerImpl to %observerImpl_addr : $*ObserverImpl + + %result = apply %partiallyApplied(%observerImpl_addr) : $@async @callee_owned (@in ObserverImpl) -> () + + dealloc_stack %observerImpl_addr : $*ObserverImpl + dealloc_stack %observableImpl_addr : $*ObservableImpl + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil b/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil new file mode 100644 index 0000000000000..5e951d9802f3e --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil @@ -0,0 +1,68 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @inGenericAndInoutGenericToGeneric(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +// CHECK-LL: define internal swiftcc void @"$s017inGenericAndInoutb2ToB0TA"(%swift.context* {{%[0-9]*}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +sil @inGenericAndInoutGenericToGeneric : $@async @convention(thin) (@in T, @inout T) -> @out T { +entry(%out : $*T, %in : $*T, %inout : $*T): + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %printGeneric_result1 = apply %printGeneric(%in) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: 876 + %printGeneric_result2 = apply %printGeneric(%inout) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: 678 + copy_addr %inout to [initialization] %out : $*T + %result = tuple () + return %result : $() +} + +sil @partial_apply_open_generic_capture : $@convention(thin) (@inout T) -> @async @callee_owned (@in T) -> @out T { +entry(%a : $*T): + %f = function_ref @inGenericAndInoutGenericToGeneric : $@async @convention(thin) (@in U, @inout U) -> @out U + %p = partial_apply %f(%a) : $@async @convention(thin) (@in U, @inout U) -> @out U + return %p : $@async @callee_owned (@in T) -> @out T +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %in_literal = integer_literal $Builtin.Int64, 876 + %in = struct $Int64 (%in_literal : $Builtin.Int64) + %inout_literal = integer_literal $Builtin.Int64, 678 + %inout = struct $Int64 (%inout_literal : $Builtin.Int64) + + %in_addr = alloc_stack $Int64 + store %in to %in_addr : $*Int64 + %inout_addr = alloc_stack $Int64 + store %inout to %inout_addr : $*Int64 + %result_addr = alloc_stack $Int64 + + %partial_apply_open_generic_capture = function_ref @partial_apply_open_generic_capture : $@convention(thin) (@inout T) -> @async @callee_owned (@in T) -> @out T + %partiallyApplied = apply %partial_apply_open_generic_capture(%inout_addr) : $@convention(thin) (@inout T) -> @async @callee_owned (@in T) -> @out T + %void = apply %partiallyApplied(%result_addr, %in_addr) : $@async @callee_owned (@in Int64) -> @out Int64 + + %result = load %result_addr : $*Int64 + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_result = apply %printInt64(%result) : $@convention(thin) (Int64) -> () // CHECK: 678 + + dealloc_stack %result_addr : $*Int64 + dealloc_stack %inout_addr : $*Int64 + dealloc_stack %in_addr : $*Int64 + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil b/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil new file mode 100644 index 0000000000000..adf8147ddbeb5 --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil @@ -0,0 +1,71 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +sil hidden @createAndInvokeClosure : $@convention(thin) () -> () { +bb0: + %captured_literal = integer_literal $Builtin.Int64, 783247897 + %captured = struct $Int64 (%captured_literal : $Builtin.Int64) + %createPartialApply = function_ref @createPartialApply : $@convention(thin) (Int64) -> @owned @async @callee_guaranteed (Int64) -> Int64 + %partialApply = apply %createPartialApply(%captured) : $@convention(thin) (Int64) -> @owned @async @callee_guaranteed (Int64) -> Int64 + strong_retain %partialApply : $@async @callee_guaranteed (Int64) -> Int64 + %applied_literal = integer_literal $Builtin.Int64, 7823478 + %applied = struct $Int64 (%applied_literal : $Builtin.Int64) + %sum = apply %partialApply(%applied) : $@async @callee_guaranteed (Int64) -> Int64 + strong_release %partialApply : $@async @callee_guaranteed (Int64) -> Int64 + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %result = apply %printInt64(%sum) : $@convention(thin) (Int64) -> () // CHECK: 791071375 + strong_release %partialApply : $@async @callee_guaranteed (Int64) -> Int64 + return %result : $() +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %createAndInvokeClosure = function_ref @createAndInvokeClosure : $@convention(thin) () -> () + %createAndInvokeClosure_result = apply %createAndInvokeClosure() : $@convention(thin) () -> () + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} + +// CHECK-LL: define internal swiftcc void @closure(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} +// CHECK-LL: define internal swiftcc void @"$s7closureTA"(%swift.context* {{%[0-9]*}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} +sil hidden @createPartialApply : $@convention(thin) (Int64) -> @owned @async @callee_guaranteed (Int64) -> Int64 { +bb0(%captured : $Int64): + %closure = function_ref @closure : $@async @convention(thin) (Int64, Int64) -> Int64 + %partialApply = partial_apply [callee_guaranteed] %closure(%captured) : $@async @convention(thin) (Int64, Int64) -> Int64 + return %partialApply : $@async @callee_guaranteed (Int64) -> Int64 +} + +sil private @closure : $@async @convention(thin) (Int64, Int64) -> Int64 { +bb0(%one : $Int64, %two : $Int64): + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_1 = apply %printInt64(%one) : $@convention(thin) (Int64) -> () + %printInt64_2 = apply %printInt64(%two) : $@convention(thin) (Int64) -> () + %one_builtin = struct_extract %one : $Int64, #Int64._value + %two_builtin = struct_extract %two : $Int64, #Int64._value + %flag = integer_literal $Builtin.Int1, -1 + %sumAndOverflowed = builtin "sadd_with_overflow_Int64"(%one_builtin : $Builtin.Int64, %two_builtin : $Builtin.Int64, %flag : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) + %sum_builtin = tuple_extract %sumAndOverflowed : $(Builtin.Int64, Builtin.Int1), 0 + %overflowed = tuple_extract %sumAndOverflowed : $(Builtin.Int64, Builtin.Int1), 1 + cond_fail %overflowed : $Builtin.Int1, "arithmetic overflow" + %sum = struct $Int64 (%sum_builtin : $Builtin.Int64) + return %sum : $Int64 +} + diff --git a/test/IRGen/async/run-partialapply-capture-int64-to-generic.sil b/test/IRGen/async/run-partialapply-capture-int64-to-generic.sil new file mode 100644 index 0000000000000..b50eca7e86b33 --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-int64-to-generic.sil @@ -0,0 +1,64 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt32 : $@convention(thin) (Int32) -> () + +sil @partial_apply_dynamic_with_out_param : $@convention(thin) (Int32, @owned @async @callee_owned (Int32) -> @out T) -> @async @callee_owned () -> @out T { +bb0(%x : $Int32, %f : $@async @callee_owned (Int32) -> @out T): + %p = partial_apply %f(%x) : $@async @callee_owned (Int32) -> @out T + return %p : $@async @callee_owned () -> @out T +} + +// CHECK-LL: define internal swiftcc void @"$sTA"(%swift.context* {{%[0-9]*}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +// CHECK-LL: define internal swiftcc void @"$s6calleeTA"(%swift.context* {{%[0-9]*}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +sil @callee : $@async @convention(thin) (Int32, @in_guaranteed T) -> @out T { +entry(%out_t : $*T, %x : $Int32, %in_t : $*T): + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %printInt32 = function_ref @printInt32 : $@convention(thin) (Int32) -> () + %result = apply %printGeneric(%in_t) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: 1 + %printInt32_result = apply %printInt32(%x) : $@convention(thin) (Int32) -> () // CHECK: 6789 + copy_addr %in_t to [initialization] %out_t : $*T + return %result : $() +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %callee = function_ref @callee : $@async @convention(thin) (Int32, @in_guaranteed T) -> @out T + %first_literal = integer_literal $Builtin.Int32, 1 + %first = struct $Int32 (%first_literal : $Builtin.Int32) + %first_addr = alloc_stack $Int32 + store %first to %first_addr : $*Int32 + %callee1 = partial_apply %callee(%first_addr) : $@async @convention(thin) (Int32, @in_guaranteed T) -> @out T + %partialApplier = function_ref @partial_apply_dynamic_with_out_param : $@convention(thin) (Int32, @owned @async @callee_owned (Int32) -> @out T) -> @async @callee_owned () -> @out T + %second_literal = integer_literal $Builtin.Int32, 6789 + %second = struct $Int32 (%second_literal : $Builtin.Int32) + %callee2 = apply %partialApplier(%second, %callee1) : $@convention(thin) (Int32, @owned @async @callee_owned (Int32) -> @out T) -> @async @callee_owned () -> @out T + + %result_addr = alloc_stack $Int32 + %result = apply %callee2(%result_addr) : $@async @callee_owned () -> @out Int32 + + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %printGeneric_result = apply %printGeneric(%result_addr) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: 1 + + dealloc_stack %result_addr : $*Int32 + dealloc_stack %first_addr : $*Int32 + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/async/run-partialapply-capture-struct_classinstance_classinstance-and-int64-to-int64.sil b/test/IRGen/async/run-partialapply-capture-struct_classinstance_classinstance-and-int64-to-int64.sil new file mode 100644 index 0000000000000..bebd7e2cd57db --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-struct_classinstance_classinstance-and-int64-to-int64.sil @@ -0,0 +1,105 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +class C { + deinit + init() +} + +sil hidden [exact_self_class] @S_allocating_init : $@convention(method) (@thick C.Type) -> @owned C { +bb0(%0 : $@thick C.Type): + %1 = alloc_ref $C + %2 = function_ref @$S_init : $@convention(method) (@owned C) -> @owned C + %3 = apply %2(%1) : $@convention(method) (@owned C) -> @owned C + return %3 : $C +} + +sil hidden @$S_init : $@convention(method) (@owned C) -> @owned C { +bb0(%0 : $C): + return %0 : $C +} + +sil hidden @$S_deinit : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject { +bb0(%0 : $C): + %2 = unchecked_ref_cast %0 : $C to $Builtin.NativeObject + return %2 : $Builtin.NativeObject +} + +sil hidden @S_deallocating_deinit : $@convention(method) (@owned C) -> () { +bb0(%0 : $C): + %2 = function_ref @$S_deinit : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject + %3 = apply %2(%0) : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject + %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $C + dealloc_ref %4 : $C + %6 = tuple () + return %6 : $() +} + +sil_vtable C { + #C.init!allocator: (C.Type) -> () -> C : @S_allocating_init + #C.deinit!deallocator: @S_deallocating_deinit +} + + +struct S { var x: C, y: C } + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @structClassInstanceClassInstanceAndInt64ToInt64(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +// CHECK-LL: define internal swiftcc void @"$s019structClassInstancebc10AndInt64ToE0TA"(%swift.context* {{%[0-9]*}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +sil @structClassInstanceClassInstanceAndInt64ToInt64 : $@async @convention(thin) (Int64, @guaranteed S) -> Int64 { +entry(%in : $Int64, %s : $S): + %s_addr = alloc_stack $S + store %s to %s_addr : $*S + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %printGeneric_result = apply %printGeneric(%s_addr) : $@convention(thin) (@in_guaranteed T) -> () //CHECK: S(x: main.C, y: main.C) + dealloc_stack %s_addr : $*S + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_result = apply %printInt64(%in) : $@convention(thin) (Int64) -> () // CHECK: 9999 + return %in : $Int64 +} + +sil @partial_apply_guaranteed_class_pair_param : $@convention(thin) (@owned S) -> @async @callee_owned (Int64) -> Int64 { +bb0(%x : $S): + %f = function_ref @structClassInstanceClassInstanceAndInt64ToInt64 : $@async @convention(thin) (Int64, @guaranteed S) -> Int64 + %p = partial_apply %f(%x) : $@async @convention(thin) (Int64, @guaranteed S) -> Int64 + return %p : $@async @callee_owned (Int64) -> Int64 +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %s_type = metatype $@thick C.Type + %allocating_init = function_ref @S_allocating_init : $@convention(method) (@thick C.Type) -> @owned C + %instance1 = apply %allocating_init(%s_type) : $@convention(method) (@thick C.Type) -> @owned C + %instance2 = apply %allocating_init(%s_type) : $@convention(method) (@thick C.Type) -> @owned C + strong_retain %instance1 : $C + strong_retain %instance2 : $C + %instance = struct $S (%instance1 : $C, %instance2 : $C) + + %partial_apply_guaranteed_class_pair_param = function_ref @partial_apply_guaranteed_class_pair_param : $@convention(thin) (@owned S) -> @async @callee_owned (Int64) -> Int64 + %partiallyApplied = apply %partial_apply_guaranteed_class_pair_param(%instance) : $@convention(thin) (@owned S) -> @async @callee_owned (Int64) -> Int64 + %int_literal = integer_literal $Builtin.Int64, 9999 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %result = apply %partiallyApplied(%int) : $@async @callee_owned (Int64) -> Int64 + %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () + %printInt64_result = apply %printInt64(%result) : $@convention(thin) (Int64) -> () // CHECK: 9999 + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/async/run-partialapply-capture-structgeneric_classinstance_to_struct_and_error.sil b/test/IRGen/async/run-partialapply-capture-structgeneric_classinstance_to_struct_and_error.sil new file mode 100644 index 0000000000000..e9b8aecc7745d --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-structgeneric_classinstance_to_struct_and_error.sil @@ -0,0 +1,97 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + +struct A1 { + let b: () -> () +} + +struct A2 { + let a: T +} + +class A3 {} +sil_vtable A3 {} + +sil @printA2AtA3 : $@convention(thin) (A2) -> () { +entry(%value : $A2): + %addr = alloc_stack $A2 + store %value to %addr : $*A2 + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %result = apply %printGeneric>(%addr) : $@convention(thin) (@in_guaranteed T) -> () + dealloc_stack %addr : $*A2 + return %result : $() +} + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @amethod(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil @amethod : $@async @convention(method) (@in_guaranteed A2) -> (@owned A1, @error Error) { +entry(%a2_at_a3_addr : $*A2): + %a2_at_a3 = load %a2_at_a3_addr : $*A2 + %printA2AtA3 = function_ref @printA2AtA3 : $@convention(thin) (A2) -> () + %partiallyApplied = partial_apply [callee_guaranteed] %printA2AtA3(%a2_at_a3) : $@convention(thin) (A2) -> () + %result = struct $A1 ( %partiallyApplied : $@callee_guaranteed () -> () ) + return %result : $A1 +} + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @repo(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +// CHECK-LL: define internal swiftcc void @"$s7amethodTA"(%swift.context* {{%[0-9]*}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +sil @repo : $@async @convention(thin) (@in_guaranteed A2) -> @owned @async @callee_guaranteed () -> (@owned A1, @error Error) { +bb0(%0 : $*A2): + %1 = load %0 : $*A2 + %2 = alloc_stack $A2 + store %1 to %2 : $*A2 + %4 = function_ref @amethod : $@async @convention(method) (@in_guaranteed A2) -> (@owned A1, @error Error) + %5 = partial_apply [callee_guaranteed] %4(%2) : $@async @convention(method) (@in_guaranteed A2) -> (@owned A1, @error Error) + dealloc_stack %2 : $*A2 + return %5 : $@async @callee_guaranteed () -> (@owned A1, @error Error) +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %a3 = alloc_ref $A3 + %a2 = struct $A2 (%a3 : $A3) + + %a2_addr = alloc_stack $A2 + store %a2 to %a2_addr : $*A2 + + %callee = function_ref @repo : $@async @convention(thin) (@in_guaranteed A2) -> @owned @async @callee_guaranteed () -> (@owned A1, @error Error) + %partiallyApplied = apply %callee(%a2_addr) : $@async @convention(thin) (@in_guaranteed A2) -> @owned @async @callee_guaranteed () -> (@owned A1, @error Error) + try_apply %partiallyApplied() : $@async @callee_guaranteed () -> (@owned A1, @error Error), normal bb_success, error bb_error + +bb_success(%value : $A1): + %value_addr = alloc_stack $A1 + store %value to %value_addr : $*A1 + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %result = apply %printGeneric(%value_addr) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: A1(b: (Function)) + %closure = struct_extract %value : $A1, #A1.b + %closure_result = apply %closure() : $@callee_guaranteed () -> () // CHECK: A2(a: main.A3) + dealloc_stack %value_addr : $*A1 + + br bb_finish + +bb_error(%error : $Error): + br bb_finish + +bb_finish: + + dealloc_stack %a2_addr : $*A2 + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/async/run-partialapply-capture-structgeneric_polymorphic_constrained-to-void.sil b/test/IRGen/async/run-partialapply-capture-structgeneric_polymorphic_constrained-to-void.sil new file mode 100644 index 0000000000000..5b5a47e58d89c --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-structgeneric_polymorphic_constrained-to-void.sil @@ -0,0 +1,68 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + +public protocol Q { + associatedtype Update +} + +public struct BaseProducer : Q { + public typealias Update = T +} +sil_witness_table BaseProducer : Q module main { + associated_type Update : T +} + +public class WeakBox {} +sil_vtable WeakBox {} + +// CHECK-LL: define internal swiftcc void @"$s7takingQTA"(%swift.context* {{%[0-9]*}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @takingQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () { +entry(%box : $WeakBox<τ_0_0>): + %box_addr = alloc_stack $WeakBox<τ_0_0> + store %box to %box_addr : $*WeakBox<τ_0_0> + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %result = apply %printGeneric>(%box_addr) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: main.WeakBox> + dealloc_stack %box_addr : $*WeakBox<τ_0_0> + return %result : $() +} + +sil public @bind_polymorphic_param_from_context : $@convention(thin) <τ_0_1>(@in τ_0_1) -> @owned @async @callee_owned () -> () { +bb0(%0 : $*τ_0_1): + %1 = alloc_ref $WeakBox> + %8 = function_ref @takingQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () + %9 = partial_apply %8>(%1) : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () + return %9 : $@async @callee_owned () -> () +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %bind_polymorphic_param_from_context = function_ref @bind_polymorphic_param_from_context : $@convention(thin) <τ_0_1>(@in τ_0_1) -> @owned @async @callee_owned () -> () + %int_literal = integer_literal $Builtin.Int64, 9999 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %int_addr = alloc_stack $Int64 + store %int to %int_addr : $*Int64 + %partiallyApplied = apply %bind_polymorphic_param_from_context(%int_addr) : $@convention(thin) <τ_0_1>(@in τ_0_1) -> @owned @async @callee_owned () -> () + dealloc_stack %int_addr : $*Int64 + + %result = apply %partiallyApplied() : $@async @callee_owned () -> () + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/async/run-partialapply-capture-type_structgeneric_polymorphic_constrained-to-void.sil b/test/IRGen/async/run-partialapply-capture-type_structgeneric_polymorphic_constrained-to-void.sil new file mode 100644 index 0000000000000..92fe600aaead5 --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-type_structgeneric_polymorphic_constrained-to-void.sil @@ -0,0 +1,71 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + +public protocol Q { + associatedtype Update +} + +public struct BaseProducer : Q { + public typealias Update = T +} +sil_witness_table BaseProducer : Q module main { + associated_type Update : T +} + +public class WeakBox {} +sil_vtable WeakBox {} + +// CHECK-LL: define hidden swiftcc void @takingQ(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +sil hidden @takingQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () { +entry(%box : $WeakBox<τ_0_0>): + %box_addr = alloc_stack $WeakBox<τ_0_0> + store %box to %box_addr : $*WeakBox<τ_0_0> + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %result = apply %printGeneric>(%box_addr) : $@convention(thin) (@in_guaranteed T) -> () // CHECK: main.WeakBox> + dealloc_stack %box_addr : $*WeakBox<τ_0_0> + return %result : $() +} + + +// CHECK-LL: define internal swiftcc void @"$s7takingQTA"(%swift.context* {{%[0-9]*}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +sil public @bind_polymorphic_param_from_forwarder_parameter : $@convention(thin) <τ_0_1>(@in τ_0_1) -> @callee_owned @async (@owned WeakBox>) -> () { +bb0(%0 : $*τ_0_1): + %1 = alloc_ref $WeakBox> + %8 = function_ref @takingQ : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () + %9 = partial_apply %8>() : $@async @convention(thin) <τ_0_0 where τ_0_0 : Q> (@owned WeakBox<τ_0_0>) -> () + return %9 : $@callee_owned @async (@owned WeakBox>) -> () +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %bind_polymorphic_param_from_forwarder_parameter = function_ref @bind_polymorphic_param_from_forwarder_parameter : $@convention(thin) <τ_0_1>(@in τ_0_1) -> @callee_owned @async (@owned WeakBox>) -> () + %int_literal = integer_literal $Builtin.Int64, 9999 + %int = struct $Int64 (%int_literal : $Builtin.Int64) + %int_addr = alloc_stack $Int64 + store %int to %int_addr : $*Int64 + %partiallyApplied = apply %bind_polymorphic_param_from_forwarder_parameter(%int_addr) : $@convention(thin) <τ_0_1>(@in τ_0_1) -> @callee_owned @async (@owned WeakBox>) -> () + dealloc_stack %int_addr : $*Int64 + + %box = alloc_ref $WeakBox> + %result = apply %partiallyApplied(%box) : $@callee_owned @async (@owned WeakBox>) -> () + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/IRGen/async/run-partialapply-capture-type_thin-and-classinstance-to-void.sil b/test/IRGen/async/run-partialapply-capture-type_thin-and-classinstance-to-void.sil new file mode 100644 index 0000000000000..cc2bcb615c74d --- /dev/null +++ b/test/IRGen/async/run-partialapply-capture-type_thin-and-classinstance-to-void.sil @@ -0,0 +1,102 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule +// RUN: %target-codesign %t/%target-library-name(PrintShims) +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: swift_test_mode_optimize_none +// REQUIRES: concurrency +// UNSUPPORTED: use_oC_stdlib + +import Builtin +import Swift +import PrintShims + +sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () +sil public_external @printInt64 : $@convention(thin) (Int64) -> () + +class C { + deinit + init() +} + +sil hidden [exact_self_class] @C_allocating_init : $@convention(method) (@thick C.Type) -> @owned C { +bb0(%0 : $@thick C.Type): + %1 = alloc_ref $C + %2 = function_ref @$C_init : $@convention(method) (@owned C) -> @owned C + %3 = apply %2(%1) : $@convention(method) (@owned C) -> @owned C + return %3 : $C +} + +sil hidden @$C_init : $@convention(method) (@owned C) -> @owned C { +bb0(%0 : $C): + return %0 : $C +} + +sil hidden @$C_deinit : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject { +bb0(%0 : $C): + %2 = unchecked_ref_cast %0 : $C to $Builtin.NativeObject + return %2 : $Builtin.NativeObject +} + +sil hidden @C_deallocating_deinit : $@convention(method) (@owned C) -> () { +bb0(%0 : $C): + %2 = function_ref @$C_deinit : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject + %3 = apply %2(%0) : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject + %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $C + dealloc_ref %4 : $C + %6 = tuple () + return %6 : $() +} + +sil_vtable C { + #C.init!allocator: (C.Type) -> () -> C : @C_allocating_init + #C.deinit!deallocator: @C_deallocating_deinit +} + +struct S {} + +// CHECK-LL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @structtypeSAndClassinstanceCToVoid(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { +// CHECK-LL: define internal swiftcc void @"$s34structtypeSAndClassinstanceCToVoidTA"(%swift.context* {{%[0-9]*}}, %swift.refcounted* swiftself {{%[0-9]*}}) {{#[0-9]*}} { +sil @structtypeSAndClassinstanceCToVoid : $@async @convention(thin) (@thin S.Type, @owned C) -> () { +entry(%S_type: $@thin S.Type, %C_instance: $C): + %S_type_addr = alloc_stack $@thick S.Type + %S_type_thick = metatype $@thick S.Type + store %S_type_thick to %S_type_addr : $*@thick S.Type + %C_instance_addr = alloc_stack $C + store %C_instance to %C_instance_addr : $*C + %printGeneric = function_ref @printGeneric : $@convention(thin) (@in_guaranteed T) -> () + %printGeneric_result1 = apply %printGeneric(%S_type_addr) : $@convention(thin) (@in_guaranteed T) -> () //CHECK: S + %printGeneric_result2 = apply %printGeneric(%C_instance_addr) : $@convention(thin) (@in_guaranteed T) -> () //CHECK: main.C + dealloc_stack %C_instance_addr : $*C + dealloc_stack %S_type_addr : $*@thick S.Type + return %printGeneric_result2 : $() +} + +sil @partial_apply_thin_type : $@async @convention(thin) (@thin S.Type, @owned C) -> @async @callee_owned () -> () { +entry(%S_type: $@thin S.Type, %C_instance: $C): + %structtypeSAndClassinstanceCToVoid = function_ref @structtypeSAndClassinstanceCToVoid : $@async @convention(thin) (@thin S.Type, @owned C) -> () + %closure = partial_apply %structtypeSAndClassinstanceCToVoid (%S_type, %C_instance) : $@async @convention(thin) (@thin S.Type, @owned C) -> () + return %closure : $@async @callee_owned () -> () +} + +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): + %c_type = metatype $@thick C.Type + %allocating_init = function_ref @C_allocating_init : $@convention(method) (@thick C.Type) -> @owned C + %instance = apply %allocating_init(%c_type) : $@convention(method) (@thick C.Type) -> @owned C + strong_retain %instance : $C + + + %partial_apply_thin_type = function_ref @partial_apply_thin_type : $@async @convention(thin) (@thin S.Type, @owned C) -> @async @callee_owned () -> () + %S_type = metatype $@thin S.Type + %partiallyApplied = apply %partial_apply_thin_type(%S_type, %instance) : $@async @convention(thin) (@thin S.Type, @owned C) -> @async @callee_owned () -> () + %result = apply %partiallyApplied() : $@async @callee_owned () -> () + + %out_literal = integer_literal $Builtin.Int32, 0 + %out = struct $Int32 (%out_literal : $Builtin.Int32) + return %out : $Int32 +} diff --git a/test/Inputs/print-shims.swift b/test/Inputs/print-shims.swift index eebfb80c2c7cb..7a9e569370482 100644 --- a/test/Inputs/print-shims.swift +++ b/test/Inputs/print-shims.swift @@ -3,6 +3,11 @@ public func printInt64(_ int: Int64) { print(int) } +@_silgen_name("printInt32") +public func printInt32(_ int: Int32) { + print(int) +} + @_silgen_name("printGeneric") public func printGeneric(_ t: T) { print(t) From 16104d8045d55c5b6ac2381764a07d91402f80f3 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 22 Oct 2020 13:01:23 -0700 Subject: [PATCH 648/745] [Concurrency] Don't lose name information from completion-handler arguments. When a completion handler parameter has a selector piece that ends with "WithCompletion(Handler)", prepend the text before that suffix to the base name or previous argument label, as appropriate. This ensures that we don't lose information from the name, particularly with delegate names. --- include/swift/Basic/StringExtras.h | 9 +++- lib/Basic/StringExtras.cpp | 54 ++++++++++++++++++- lib/ClangImporter/IAMInference.cpp | 2 +- lib/ClangImporter/ImportName.cpp | 47 ++++++---------- lib/ClangImporter/ImportName.h | 2 +- lib/Sema/MiscDiagnostics.cpp | 5 +- test/ClangImporter/objc_async.swift | 3 ++ .../usr/include/ObjCConcurrency.h | 2 + 8 files changed, 86 insertions(+), 38 deletions(-) diff --git a/include/swift/Basic/StringExtras.h b/include/swift/Basic/StringExtras.h index 4bc4fe7a8a964..4e837605d2c12 100644 --- a/include/swift/Basic/StringExtras.h +++ b/include/swift/Basic/StringExtras.h @@ -442,7 +442,8 @@ class InheritedNameSet { /// /// \param allPropertyNames The set of property names in the enclosing context. /// -/// \param isAsync Whether this is a function imported as 'async'. +/// \param completionHandlerIndex For an 'async' function, the index of the +/// completion handler in argNames. /// /// \param scratch Scratch space that will be used for modifications beyond /// just chopping names. @@ -457,9 +458,13 @@ bool omitNeedlessWords(StringRef &baseName, bool returnsSelf, bool isProperty, const InheritedNameSet *allPropertyNames, - bool isAsync, + Optional completionHandlerIndex, + Optional completionHandlerName, StringScratchSpace &scratch); +/// If the name has a completion-handler suffix, strip off that suffix. +Optional stripWithCompletionHandlerSuffix(StringRef name); + } // end namespace swift #endif // SWIFT_BASIC_STRINGEXTRAS_H diff --git a/lib/Basic/StringExtras.cpp b/lib/Basic/StringExtras.cpp index 51f8970dffcce..3884a8d72deb6 100644 --- a/lib/Basic/StringExtras.cpp +++ b/lib/Basic/StringExtras.cpp @@ -1220,7 +1220,8 @@ bool swift::omitNeedlessWords(StringRef &baseName, bool returnsSelf, bool isProperty, const InheritedNameSet *allPropertyNames, - bool isAsync, + Optional completionHandlerIndex, + Optional completionHandlerName, StringScratchSpace &scratch) { bool anyChanges = false; OmissionTypeName resultType = returnsSelf ? contextType : givenResultType; @@ -1290,6 +1291,7 @@ bool swift::omitNeedlessWords(StringRef &baseName, // If the base name of a method imported as "async" starts with the word // "get", drop the "get". + bool isAsync = completionHandlerIndex.hasValue(); if (isAsync && camel_case::getFirstWord(baseName) == "get" && baseName.size() > 3) { baseName = baseName.substr(3); @@ -1301,6 +1303,15 @@ bool swift::omitNeedlessWords(StringRef &baseName, splitBaseName(baseName, argNames[0], paramTypes[0], firstParamName)) anyChanges = true; + // If this is an asynchronous function where the completion handler is + // the first parameter, strip off WithCompletion(Handler) from the base name. + if (isAsync && *completionHandlerIndex == 0) { + if (auto newBaseName = stripWithCompletionHandlerSuffix(baseName)) { + baseName = *newBaseName; + anyChanges = true; + } + } + // For a method imported as "async", drop the "Asynchronously" suffix from // the base name. It is redundant with 'async'. const StringRef asynchronously = "Asynchronously"; @@ -1310,6 +1321,21 @@ bool swift::omitNeedlessWords(StringRef &baseName, anyChanges = true; } + // If this is an asynchronous function where the completion handler is + // the second parameter, and the corresponding name has some additional + // information prior to WithCompletion(Handler), append that + // additional text to the base name. + if (isAsync && *completionHandlerIndex == 1 && completionHandlerName) { + if (auto extraParamText = stripWithCompletionHandlerSuffix( + *completionHandlerName)) { + SmallString<32> newBaseName; + newBaseName += baseName; + appendSentenceCase(newBaseName, *extraParamText); + baseName = scratch.copyString(newBaseName); + anyChanges = true; + } + } + // Omit needless words based on parameter types. for (unsigned i = 0, n = argNames.size(); i != n; ++i) { // If there is no corresponding parameter, there is nothing to @@ -1329,6 +1355,20 @@ bool swift::omitNeedlessWords(StringRef &baseName, name, paramTypes[i], role, role == NameRole::BaseName ? allPropertyNames : nullptr); + // If this is an asynchronous function where the completion handler is + // past the second parameter and has additional information in the name, + // add that information to the prior argument name. + if (isAsync && completionHandlerName && *completionHandlerIndex > 1 && + *completionHandlerIndex == i + 1) { + if (auto extraParamText = stripWithCompletionHandlerSuffix( + *completionHandlerName)) { + SmallString<32> extendedName; + extendedName += newName; + appendSentenceCase(extendedName, *extraParamText); + newName = scratch.copyString(extendedName); + } + } + if (name == newName) continue; // Record this change. @@ -1342,3 +1382,15 @@ bool swift::omitNeedlessWords(StringRef &baseName, return lowercaseAcronymsForReturn(); } + +Optional swift::stripWithCompletionHandlerSuffix(StringRef name) { + if (name.endswith("WithCompletionHandler")) { + return name.drop_back(strlen("WithCompletionHandler")); + } + + if (name.endswith("WithCompletion")) { + return name.drop_back(strlen("WithCompletion")); + } + + return None; +} diff --git a/lib/ClangImporter/IAMInference.cpp b/lib/ClangImporter/IAMInference.cpp index 6cbf5ce8781fa..cdd88dbb60c08 100644 --- a/lib/ClangImporter/IAMInference.cpp +++ b/lib/ClangImporter/IAMInference.cpp @@ -448,7 +448,7 @@ class IAMInference { baseName = humbleBaseName.userFacingName(); bool didOmit = omitNeedlessWords(baseName, argStrs, "", "", "", paramTypeNames, false, - false, nullptr, false, scratch); + false, nullptr, None, None, scratch); SmallVector argLabels; for (auto str : argStrs) argLabels.push_back(getIdentifier(str)); diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 078d11792dd2f..62ea21f89c339 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -804,7 +804,8 @@ static bool omitNeedlessWordsInFunctionName( ArrayRef params, clang::QualType resultType, const clang::DeclContext *dc, const SmallBitVector &nonNullArgs, Optional errorParamIndex, bool returnsSelf, bool isInstanceMethod, - Optional completionHandlerIndex, NameImporter &nameImporter) { + Optional completionHandlerIndex, + Optional completionHandlerName, NameImporter &nameImporter) { clang::ASTContext &clangCtx = nameImporter.getClangContext(); // Collect the parameter type names. @@ -854,8 +855,8 @@ static bool omitNeedlessWordsInFunctionName( getClangTypeNameForOmission(clangCtx, resultType), getClangTypeNameForOmission(clangCtx, contextType), paramTypes, returnsSelf, /*isProperty=*/false, - allPropertyNames, completionHandlerIndex.hasValue(), - nameImporter.getScratch()); + allPropertyNames, completionHandlerIndex, + completionHandlerName, nameImporter.getScratch()); } /// Prepare global name for importing onto a swift_newtype. @@ -1135,26 +1136,9 @@ Optional NameImporter::considerErrorImport( /// Whether the given parameter name identifies a completion handler. static bool isCompletionHandlerParamName(StringRef paramName) { return paramName == "completionHandler" || paramName == "completion" || - paramName == "withCompletionHandler"; + paramName == "withCompletionHandler" || paramName == "withCompletion"; } -/// Whether the give base name implies that the first parameter is a completion -/// handler. -/// -/// \returns a trimmed base name when it does, \c None others -static Optional isCompletionHandlerInBaseName(StringRef basename) { - if (basename.endswith("WithCompletionHandler")) { - return basename.drop_back(strlen("WithCompletionHandler")); - } - - if (basename.endswith("WithCompletion")) { - return basename.drop_back(strlen("WithCompletion")); - } - - return None; -} - - // Determine whether the given type is a nullable NSError type. static bool isNullableNSErrorType( clang::ASTContext &clangCtx, clang::QualType type) { @@ -1185,7 +1169,7 @@ static bool isNullableNSErrorType( Optional NameImporter::considerAsyncImport( const clang::ObjCMethodDecl *clangDecl, - StringRef &baseName, + StringRef baseName, SmallVectorImpl ¶mNames, ArrayRef params, bool isInitializer, bool hasCustomName, @@ -1207,12 +1191,14 @@ NameImporter::considerAsyncImport( // Determine whether the naming indicates that this is a completion // handler. - Optional newBaseName; if (isCompletionHandlerParamName( - paramNames[completionHandlerParamNameIndex])) { + paramNames[completionHandlerParamNameIndex]) || + (completionHandlerParamNameIndex > 0 && + stripWithCompletionHandlerSuffix( + paramNames[completionHandlerParamNameIndex]))) { // The argument label itself has an appropriate name. } else if (!hasCustomName && completionHandlerParamIndex == 0 && - (newBaseName = isCompletionHandlerInBaseName(baseName))) { + stripWithCompletionHandlerSuffix(baseName)) { // The base name implies that the first parameter is a completion handler. } else if (isCompletionHandlerParamName( params[completionHandlerParamIndex]->getName())) { @@ -1301,10 +1287,6 @@ NameImporter::considerAsyncImport( // Drop the completion handler parameter name. paramNames.erase(paramNames.begin() + completionHandlerParamNameIndex); - // Update the base name, if needed. - if (newBaseName && !hasCustomName) - baseName = *newBaseName; - return ForeignAsyncConvention::Info( completionHandlerParamIndex, completionHandlerErrorParamIndex); } @@ -1954,7 +1936,7 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, (void)omitNeedlessWords(baseName, {}, "", propertyTypeName, contextTypeName, {}, /*returnsSelf=*/false, /*isProperty=*/true, allPropertyNames, - /*isAsync=*/false, scratch); + None, None, scratch); } } @@ -1971,6 +1953,11 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, [](const ForeignAsyncConvention::Info &info) { return info.CompletionHandlerParamIndex; }), + result.getAsyncInfo().map( + [&](const ForeignAsyncConvention::Info &info) { + return method->getDeclName().getObjCSelector().getNameForSlot( + info.CompletionHandlerParamIndex); + }), *this); } diff --git a/lib/ClangImporter/ImportName.h b/lib/ClangImporter/ImportName.h index 5f5d9a027343e..a35fb6760e6ba 100644 --- a/lib/ClangImporter/ImportName.h +++ b/lib/ClangImporter/ImportName.h @@ -455,7 +455,7 @@ class NameImporter { Optional considerAsyncImport(const clang::ObjCMethodDecl *clangDecl, - StringRef &baseName, + StringRef baseName, SmallVectorImpl ¶mNames, ArrayRef params, bool isInitializer, bool hasCustomName, diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index deb27a20ba1a4..ac3930d93841e 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -4868,7 +4868,7 @@ Optional TypeChecker::omitNeedlessWords(AbstractFunctionDecl *afd) { getTypeNameForOmission(contextType), paramTypes, returnsSelf, false, /*allPropertyNames=*/nullptr, - afd->hasAsync(), scratch)) + None, None, scratch)) return None; /// Retrieve a replacement identifier. @@ -4923,8 +4923,7 @@ Optional TypeChecker::omitNeedlessWords(VarDecl *var) { OmissionTypeName contextTypeName = getTypeNameForOmission(contextType); if (::omitNeedlessWords(name, { }, "", typeName, contextTypeName, { }, /*returnsSelf=*/false, true, - /*allPropertyNames=*/nullptr, /*isAsync=*/false, - scratch)) { + /*allPropertyNames=*/nullptr, None, None, scratch)) { return Context.getIdentifier(name); } diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift index 8d2e691d49857..8d80d1a0b6cfc 100644 --- a/test/ClangImporter/objc_async.swift +++ b/test/ClangImporter/objc_async.swift @@ -21,6 +21,9 @@ func testSlowServer(slowServer: SlowServer) async throws { let _: String? = await try slowServer.fortune() let _: Int = await try slowServer.magicNumber(withSeed: 42) + + await slowServer.serverRestart("localhost") + await slowServer.server("localhost", atPriorityRestart: 0.8) } func testSlowServerSynchronous(slowServer: SlowServer) { diff --git a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h index b968a5561e7fe..01a62d25ea599 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h @@ -16,6 +16,8 @@ -(void)doSomethingConflicted:(NSString *)operation completionHandler:(void (^)(NSInteger))handler; -(NSInteger)doSomethingConflicted:(NSString *)operation; +-(void)server:(NSString *)name restartWithCompletionHandler:(void (^)(void))block; +-(void)server:(NSString *)name atPriority:(double)priority restartWithCompletionHandler:(void (^)(void))block; @end @protocol RefrigeratorDelegate From 223d7b9ea3d92ceac17c8c80d2177fc26514b450 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 22 Oct 2020 17:26:51 -0400 Subject: [PATCH 649/745] Fix isStartOfSwiftDecl to allow qualified types and generic arguments in attributes. Fixes rdar://70158735 --- lib/Parse/ParseDecl.cpp | 45 ++++++++++++++++++++++----------- test/Parse/result-builder.swift | 40 +++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 15 deletions(-) create mode 100644 test/Parse/result-builder.swift diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 362a88740cbc7..35d585ab8bffb 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3616,6 +3616,30 @@ static bool isParenthesizedUnowned(Parser &P) { (P.Tok.getText() == "safe" || P.Tok.getText() == "unsafe"); } +static void skipAttribute(Parser &P) { + // Consider unexpected tokens to be incomplete attributes. + + // Parse the attribute name, which can be qualified, have + // generic arguments, and so on. + do { + if (!P.consumeIf(tok::identifier) && !P.consumeIf(tok::code_complete)) + return; + + if (P.startsWithLess(P.Tok)) { + P.consumeStartingLess(); + P.skipUntilGreaterInTypeList(); + } + } while (P.consumeIf(tok::period)); + + // Skip an argument clause after the attribute name. + if (P.consumeIf(tok::l_paren)) { + while (P.Tok.isNot(tok::r_brace, tok::eof, tok::pound_endif)) { + if (P.consumeIf(tok::r_paren)) break; + P.skipSingle(); + } + } +} + bool Parser::isStartOfSwiftDecl() { // If this is obviously not the start of a decl, then we're done. if (!isKeywordPossibleDeclStart(Tok)) return false; @@ -3645,23 +3669,14 @@ bool Parser::isStartOfSwiftDecl() { if (Tok.is(tok::kw_try)) return peekToken().isAny(tok::kw_let, tok::kw_var); - // Look through attribute list, because it may be an *type* attribute list. + // Skip an attribute, since it might be a type attribute. This can't + // happen at the top level of a scope, but we do use isStartOfSwiftDecl() + // in positions like generic argument lists. if (Tok.is(tok::at_sign)) { BacktrackingScope backtrack(*this); - while (consumeIf(tok::at_sign)) { - // If not identifier or code complete token, consider '@' as an incomplete - // attribute. - if (Tok.isNot(tok::identifier, tok::code_complete)) - continue; - consumeToken(); - // Eat paren after attribute name; e.g. @foo(x) - if (consumeIf(tok::l_paren)) { - while (Tok.isNot(tok::r_brace, tok::eof, tok::pound_endif)) { - if (consumeIf(tok::r_paren)) break; - skipSingle(); - } - } - } + while (consumeIf(tok::at_sign)) + skipAttribute(*this); + // If this attribute is the last element in the block, // consider it is a start of incomplete decl. if (Tok.isAny(tok::r_brace, tok::eof, tok::pound_endif)) diff --git a/test/Parse/result-builder.swift b/test/Parse/result-builder.swift new file mode 100644 index 0000000000000..30e178e07c550 --- /dev/null +++ b/test/Parse/result-builder.swift @@ -0,0 +1,40 @@ +// RUN: %target-typecheck-verify-swift + +// rdar://70158735 + +@resultBuilder +struct A { + static func buildBlock(_ values: Int...) -> Int { return 0 } +} + +struct B {} + +extension B { + @resultBuilder + struct Generic { + static func buildBlock(_ values: Int...) -> Int { return 0 } + } + + @resultBuilder + struct NonGeneric { + static func buildBlock(_ values: Int...) -> Int { return 0 } + } +} + +@A var test0: Int { + 1 + 2 + 3 +} + +@B.NonGeneric var test1: Int { + 1 + 2 + 3 +} + +@B.Generic var test2: Int { + 1 + 2 + 3 +} From f21a306ae55d1f616d00dd8645d7d0ca9472b95c Mon Sep 17 00:00:00 2001 From: Azoy Date: Sat, 23 May 2020 11:07:38 -0400 Subject: [PATCH 650/745] [AST] Introduce BuiltinProtocolConformance --- include/swift/AST/ASTContext.h | 6 ++ include/swift/AST/ProtocolConformance.h | 114 +++++++++++++++++++++- include/swift/SIL/SILCloner.h | 5 +- lib/AST/ASTContext.cpp | 24 +++++ lib/AST/ASTDumper.cpp | 6 +- lib/AST/ASTMangler.cpp | 27 +++-- lib/AST/ASTPrinter.cpp | 8 +- lib/AST/ProtocolConformance.cpp | 52 +++++++++- lib/SIL/IR/SILModule.cpp | 7 +- lib/SILGen/SILGenLazyConformance.cpp | 4 +- lib/Sema/CSSimplify.cpp | 3 +- lib/Serialization/DeclTypeRecordNodes.def | 4 +- lib/Serialization/Deserialization.cpp | 30 +++++- lib/Serialization/ModuleFormat.h | 12 ++- lib/Serialization/Serialization.cpp | 16 ++- lib/Serialization/SerializeSIL.cpp | 3 +- 16 files changed, 294 insertions(+), 27 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index c9f3fbe06e187..b41dcfbc5fee1 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -103,6 +103,7 @@ namespace swift { class InheritedProtocolConformance; class SelfProtocolConformance; class SpecializedProtocolConformance; + class BuiltinProtocolConformance; enum class ProtocolConformanceState; class Pattern; enum PointerTypeKind : unsigned; @@ -948,6 +949,11 @@ class ASTContext final { SelfProtocolConformance * getSelfConformance(ProtocolDecl *protocol); + /// Produce the builtin conformance for some structural type to some protocol. + BuiltinProtocolConformance * + getBuiltinConformance(Type type, ProtocolDecl *protocol, + ArrayRef conformances); + /// A callback used to produce a diagnostic for an ill-formed protocol /// conformance that was type-checked before we're actually walking the /// conformance itself, along with a bit indicating whether this diagnostic diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index 1f61cdf438175..31fda612c717f 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -66,7 +66,10 @@ enum class ProtocolConformanceKind { Specialized, /// Conformance of a generic class type projected through one of its /// superclass's conformances. - Inherited + Inherited, + /// Builtin conformances are special conformaces that the runtime handles + /// and isn't implemented directly in Swift. + Builtin }; /// Describes the state of a protocol conformance, which may be complete, @@ -329,7 +332,9 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance { /// - the type is directly declared to conform to the protocol (a /// normal conformance) or /// - the protocol's existential type is known to conform to itself (a -/// self-conformance). +/// self-conformance) or +/// - the type's conformance is declared within the runtime (a builtin +/// conformance). class RootProtocolConformance : public ProtocolConformance { protected: RootProtocolConformance(ProtocolConformanceKind kind, Type conformingType) @@ -380,7 +385,8 @@ class RootProtocolConformance : public ProtocolConformance { static bool classof(const ProtocolConformance *conformance) { return conformance->getKind() == ProtocolConformanceKind::Normal || - conformance->getKind() == ProtocolConformanceKind::Self; + conformance->getKind() == ProtocolConformanceKind::Self || + conformance->getKind() == ProtocolConformanceKind::Builtin; } }; @@ -1014,6 +1020,106 @@ class InheritedProtocolConformance : public ProtocolConformance, } }; +/// A builtin conformance appears when a special non-nominal type has a runtime +/// declared conformance. E.g. the runtime implements Equatable for tuples. +class BuiltinProtocolConformance final : public RootProtocolConformance, + private llvm::TrailingObjects { + friend ASTContext; + friend TrailingObjects; + + ProtocolDecl *protocol = nullptr; + size_t numConformances; + + mutable Optional> conditionalConformances = None; + + BuiltinProtocolConformance(Type conformingType, ProtocolDecl *protocol, + ArrayRef conformances); + + size_t numTrailingObjects(OverloadToken) const { + return numConformances; + } + +public: + /// Get the protocol being conformed to. + ProtocolDecl *getProtocol() const { + return protocol; + } + + /// Get the trailing conformances that this builtin conformance needs. + MutableArrayRef getConformances() { + return {getTrailingObjects(), numConformances}; + } + + /// Get the trailing conformances that this builtin conformance needs. + ArrayRef getConformances() const { + return {getTrailingObjects(), numConformances}; + } + + /// Get any requirements that must be satisfied for this conformance to apply. + Optional> + getConditionalRequirementsIfAvailable() const { + return ArrayRef(); + } + + /// Get any requirements that must be satisfied for this conformance to apply. + ArrayRef getConditionalRequirements() const; + + /// Get the declaration context that contains the nominal type declaration. + DeclContext *getDeclContext() const { + return getProtocol(); + } + + /// Retrieve the state of this conformance. + ProtocolConformanceState getState() const { + return ProtocolConformanceState::Complete; + } + + /// Get the kind of source from which this conformance comes. + ConformanceEntryKind getSourceKind() const { + return ConformanceEntryKind::Synthesized; + } + /// Get the protocol conformance which implied this implied conformance. + NormalProtocolConformance *getImplyingConformance() const { + return nullptr; + } + + bool hasTypeWitness(AssociatedTypeDecl *assocType) const { + llvm_unreachable("builtin-conformances never have associated types"); + } + + /// Retrieve the type witness and type decl (if one exists) + /// for the given associated type. + TypeWitnessAndDecl + getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, + SubstOptions options=None) const { + llvm_unreachable("builtin-conformances never have associated types"); + } + + /// Given that the requirement signature of the protocol directly states + /// that the given dependent type must conform to the given protocol, + /// return its associated conformance. + ProtocolConformanceRef + getAssociatedConformance(Type assocType, ProtocolDecl *protocol) const { + llvm_unreachable("builtin-conformances never have associated types"); + } + + /// Retrieve the witness corresponding to the given value requirement. + ConcreteDeclRef getWitnessDeclRef(ValueDecl *requirement) const { + return ConcreteDeclRef(requirement); + } + + /// Determine whether the witness for the given requirement + /// is either the default definition or was otherwise deduced. + bool usesDefaultDefinition(AssociatedTypeDecl *requirement) const { + llvm_unreachable("builtin-conformances never have associated types"); + } + + static bool classof(const ProtocolConformance *conformance) { + return conformance->getKind() == ProtocolConformanceKind::Builtin; + } +}; + inline bool ProtocolConformance::isInvalid() const { return getRootConformance()->isInvalid(); } diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 8e0a0110561d7..c51d0b7831019 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -2093,7 +2093,8 @@ SILCloner::visitWitnessMethodInst(WitnessMethodInst *Inst) { if (conformance.isConcrete()) { CanType Ty = conformance.getConcrete()->getType()->getCanonicalType(); - if (Ty != newLookupType) { + if (Ty != newLookupType && + !isa(conformance.getConcrete())) { assert( (Ty->isExactSuperclassOf(newLookupType) || getBuilder().getModule().Types.getLoweredRValueType( diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 7cf42afa2d904..212e923c1f136 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -406,6 +406,10 @@ struct ASTContext::Implementation { /// The set of inherited protocol conformances. llvm::FoldingSet InheritedConformances; + /// The set of builtin protocol conformances. + llvm::DenseMap, + BuiltinProtocolConformance *> BuiltinConformances; + /// The set of substitution maps (uniqued by their storage). llvm::FoldingSet SubstitutionMaps; @@ -2014,6 +2018,24 @@ ASTContext::getSelfConformance(ProtocolDecl *protocol) { return entry; } +/// Produce the builtin conformance for some non-nominal to some protocol. +BuiltinProtocolConformance * +ASTContext::getBuiltinConformance(Type type, ProtocolDecl *protocol, + ArrayRef conformances) { + auto key = std::make_pair(type, protocol); + auto &builtinConformances = + getImpl().getArena(AllocationArena::Permanent).BuiltinConformances; + auto &entry = builtinConformances[key]; + if (!entry) { + auto size = BuiltinProtocolConformance:: + totalSizeToAlloc(conformances.size()); + auto mem = this->Allocate(size, alignof(BuiltinProtocolConformance), + AllocationArena::Permanent); + entry = new (mem) BuiltinProtocolConformance(type, protocol, conformances); + } + return entry; +} + /// If one of the ancestor conformances already has a matching type, use /// that instead. static ProtocolConformance *collapseSpecializedConformance( @@ -2030,6 +2052,7 @@ static ProtocolConformance *collapseSpecializedConformance( case ProtocolConformanceKind::Normal: case ProtocolConformanceKind::Inherited: case ProtocolConformanceKind::Self: + case ProtocolConformanceKind::Builtin: // If the conformance matches, return it. if (conformance->getType()->isEqual(type)) { for (auto subConformance : substitutions.getConformances()) @@ -2252,6 +2275,7 @@ size_t ASTContext::Implementation::Arena::getTotalMemory() const { // NormalConformances ? // SpecializedConformances ? // InheritedConformances ? + // BuiltinConformances ? } void AbstractFunctionDecl::setForeignErrorConvention( diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 7cc169599a95c..9228cfb175652 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -3310,6 +3310,10 @@ static void dumpProtocolConformanceRec( visited); break; } + + case ProtocolConformanceKind::Builtin: { + printCommon("builtin"); + } } PrintWithColorRAII(out, ParenthesisColor) << ')'; diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 490385afbe1a1..cc9d60109fec2 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -200,9 +200,11 @@ std::string ASTMangler::mangleWitnessTable(const RootProtocolConformance *C) { if (isa(C)) { appendProtocolConformance(C); appendOperator("WP"); - } else { + } else if (isa(C)) { appendProtocolName(cast(C)->getProtocol()); appendOperator("WS"); + } else { + llvm_unreachable("mangling unknown conformance kind"); } return finalize(); } @@ -226,7 +228,11 @@ std::string ASTMangler::mangleWitnessThunk( } if (Conformance) { - appendOperator(isa(Conformance) ? "TS" : "TW"); + if (isa(Conformance)) { + appendOperator("TS"); + } else { + appendOperator("TW"); + } } return finalize(); } @@ -1462,7 +1468,8 @@ void ASTMangler::appendBoundGenericArgs(Type type, bool &isFirstArgList) { static bool conformanceHasIdentity(const RootProtocolConformance *root) { auto conformance = dyn_cast(root); if (!conformance) { - assert(isa(root)); + assert(isa(root) || + isa(root)); return true; } @@ -1483,8 +1490,9 @@ static bool conformanceHasIdentity(const RootProtocolConformance *root) { static bool isRetroactiveConformance(const RootProtocolConformance *root) { auto conformance = dyn_cast(root); if (!conformance) { - assert(isa(root)); - return false; // self-conformances are never retroactive. + assert(isa(root) || + isa(root)); + return false; // self-conformances are never retroactive. nor are builtin. } return conformance->isRetroactive(); @@ -2910,6 +2918,10 @@ ASTMangler::appendProtocolConformance(const ProtocolConformance *conformance) { appendModule(Mod, DC->getAsDecl()->getAlternateModuleName()); } + // If this is a non-nominal type, we're done. + if (!conformingType->getAnyNominal()) + return; + contextSig = conformingType->getAnyNominal()->getGenericSignatureOfContext(); @@ -2934,6 +2946,9 @@ void ASTMangler::appendProtocolConformanceRef( assert(DC->getAsDecl()); appendModule(conformance->getDeclContext()->getParentModule(), DC->getAsDecl()->getAlternateModuleName()); + // Builtin conformances are always from the Swift module. + } else if (isa(conformance)) { + appendOperator("HP"); } else if (conformance->getDeclContext()->getParentModule() == conformance->getType()->getAnyNominal()->getParentModule()) { appendOperator("HP"); diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 5ebaa4d44d22d..3764a7677dc99 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -5035,6 +5035,12 @@ void ProtocolConformance::printName(llvm::raw_ostream &os, os << ")"; break; } + case ProtocolConformanceKind::Builtin: { + auto builtin = cast(this); + os << builtin->getProtocol()->getName() + << " type " << builtin->getType(); + break; + } } } diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 62b4791e29c88..f890f5bdbc9ea 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -202,6 +202,11 @@ switch (getKind()) { \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Inherited: \ return cast(this)->Method Args; \ + case ProtocolConformanceKind::Builtin: \ + static_assert(&ProtocolConformance::Method != \ + &BuiltinProtocolConformance::Method, \ + "Must override BuiltinProtocolConformance::" #Method); \ + return cast(this)->Method Args; \ } \ llvm_unreachable("bad ProtocolConformanceKind"); @@ -213,6 +218,7 @@ switch (getKind()) { \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Specialized: \ case ProtocolConformanceKind::Inherited: \ + case ProtocolConformanceKind::Builtin: \ llvm_unreachable("not a root conformance"); \ } \ llvm_unreachable("bad ProtocolConformanceKind"); @@ -275,6 +281,8 @@ ValueDecl *ProtocolConformance::getWitnessDecl(ValueDecl *requirement) const { case ProtocolConformanceKind::Specialized: return cast(this) ->getGenericConformance()->getWitnessDecl(requirement); + case ProtocolConformanceKind::Builtin: + return requirement; } llvm_unreachable("unhandled kind"); } @@ -296,6 +304,7 @@ GenericEnvironment *ProtocolConformance::getGenericEnvironment() const { return getDeclContext()->getGenericEnvironmentOfContext(); case ProtocolConformanceKind::Specialized: + case ProtocolConformanceKind::Builtin: // If we have a specialized protocol conformance, since we do not support // currently partial specialization, we know that it cannot have any open // type variables. @@ -317,6 +326,7 @@ GenericSignature ProtocolConformance::getGenericSignature() const { return getDeclContext()->getGenericSignatureOfContext(); case ProtocolConformanceKind::Specialized: + case ProtocolConformanceKind::Builtin: // If we have a specialized protocol conformance, since we do not support // currently partial specialization, we know that it cannot have any open // type variables. @@ -334,6 +344,7 @@ SubstitutionMap ProtocolConformance::getSubstitutions(ModuleDecl *M) const { switch (parent->getKind()) { case ProtocolConformanceKind::Normal: case ProtocolConformanceKind::Self: + case ProtocolConformanceKind::Builtin: llvm_unreachable("should have exited the loop?!"); case ProtocolConformanceKind::Inherited: parent = @@ -1110,6 +1121,7 @@ ProtocolConformance::getRootConformance() const { switch (C->getKind()) { case ProtocolConformanceKind::Normal: case ProtocolConformanceKind::Self: + case ProtocolConformanceKind::Builtin: return cast(C); case ProtocolConformanceKind::Inherited: C = cast(C) @@ -1159,6 +1171,7 @@ ProtocolConformance::subst(TypeSubstitutionFn subs, subMap); } case ProtocolConformanceKind::Self: + case ProtocolConformanceKind::Builtin: return const_cast(this); case ProtocolConformanceKind::Inherited: { // Substitute the base. @@ -1418,7 +1431,8 @@ bool ProtocolConformance::isCanonical() const { switch (getKind()) { case ProtocolConformanceKind::Self: - case ProtocolConformanceKind::Normal: { + case ProtocolConformanceKind::Normal: + case ProtocolConformanceKind::Builtin: { return true; } case ProtocolConformanceKind::Inherited: { @@ -1447,7 +1461,8 @@ ProtocolConformance *ProtocolConformance::getCanonicalConformance() { switch (getKind()) { case ProtocolConformanceKind::Self: - case ProtocolConformanceKind::Normal: { + case ProtocolConformanceKind::Normal: + case ProtocolConformanceKind::Builtin: { // Root conformances are always canonical by construction. return this; } @@ -1489,6 +1504,37 @@ ProtocolConformanceRef::getCanonicalConformanceRef() const { return ProtocolConformanceRef(getConcrete()->getCanonicalConformance()); } +BuiltinProtocolConformance::BuiltinProtocolConformance( + Type conformingType, ProtocolDecl *protocol, + ArrayRef conformances) : + RootProtocolConformance(ProtocolConformanceKind::Builtin, conformingType), + protocol(protocol), numConformances(conformances.size()) { + std::uninitialized_copy(conformances.begin(), conformances.end(), + getTrailingObjects()); +} + +ArrayRef +BuiltinProtocolConformance::getConditionalRequirements() const { + if (conditionalConformances == None) { + // Right now only tuples are builtin and have conditional conformances. + if (auto tuple = getType()->getAs()) { + SmallVector requirements; + + for (size_t i = 0; i != getConformances().size(); i += 1) { + auto req = Requirement(RequirementKind::Conformance, + tuple->getElement(i).getType(), + getProtocol()->getDeclaredType()); + requirements.push_back(req); + } + + conditionalConformances = getProtocol()->getASTContext() + .AllocateCopy(requirements); + } + } + + return *conditionalConformances; +} + // See swift/Basic/Statistic.h for declaration: this enables tracing // ProtocolConformances, is defined here to avoid too much layering violation / // circular linkage dependency. diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index cc30fc7f5d07b..8d35fb3750a33 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -220,6 +220,11 @@ SILModule::lookUpWitnessTable(const ProtocolConformance *C, SILWitnessTable *wtable; auto rootC = C->getRootConformance(); + + // Builtin conformances don't have witness tables in SIL. + if (isa(rootC)) + return nullptr; + // Attempt to lookup the witness table from the table. auto found = WitnessTableMap.find(rootC); if (found == WitnessTableMap.end()) { diff --git a/lib/SILGen/SILGenLazyConformance.cpp b/lib/SILGen/SILGenLazyConformance.cpp index 791ae643c7f4c..f568f26dfe0dc 100644 --- a/lib/SILGen/SILGenLazyConformance.cpp +++ b/lib/SILGen/SILGenLazyConformance.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -31,7 +31,7 @@ void SILGenModule::useConformance(ProtocolConformanceRef conformanceRef) { if (auto *inherited = dyn_cast(conformance)) conformance = inherited->getInheritedConformance(); - // Get the normal conformance. If we don't have one, this is a self + // Get the normal conformance. If we don't have one, this is a self or builtin // conformance, which we can ignore. auto normal = dyn_cast( conformance->getRootConformance()); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 3acc4aaeff9c4..1c491b1cf2c4b 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -5628,7 +5628,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( // This conformance may be conditional, in which case we need to consider // those requirements as constraints too. - if (conformance.isConcrete()) { + if (conformance.isConcrete() && + !isa(conformance.getConcrete())) { unsigned index = 0; for (const auto &req : conformance.getConditionalRequirements()) { addConstraint(req, diff --git a/lib/Serialization/DeclTypeRecordNodes.def b/lib/Serialization/DeclTypeRecordNodes.def index 2ab062799f926..783a1da57d4af 100644 --- a/lib/Serialization/DeclTypeRecordNodes.def +++ b/lib/Serialization/DeclTypeRecordNodes.def @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -194,6 +194,8 @@ OTHER(CLANG_TYPE, 253) OTHER(DERIVATIVE_FUNCTION_CONFIGURATION, 254) +OTHER(BUILTIN_PROTOCOL_CONFORMANCE, 255) + #undef RECORD #undef DECLTYPERECORDNODES_HAS_RECORD_VAL #undef RECORD_VAL diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index e9a2b2d030b14..077124df3dc5c 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -562,6 +562,34 @@ ModuleFile::readConformanceChecked(llvm::BitstreamCursor &Cursor, return ProtocolConformanceRef(conformance); } + case BUILTIN_PROTOCOL_CONFORMANCE: { + TypeID conformingTypeID; + DeclID protoID; + size_t numConformances; + BuiltinProtocolConformanceLayout::readRecord(scratch, conformingTypeID, + protoID, numConformances); + + Type conformingType = getType(conformingTypeID); + + auto decl = getDeclChecked(protoID); + if (!decl) + return decl.takeError(); + + auto proto = cast(decl.get()); + + // Read the conformances. + SmallVector conformances; + conformances.reserve(numConformances); + for (unsigned i : range(numConformances)) { + (void)i; + conformances.push_back(readConformance(Cursor)); + } + + auto conformance = getContext().getBuiltinConformance(conformingType, proto, + conformances); + return ProtocolConformanceRef(conformance); + } + case NORMAL_PROTOCOL_CONFORMANCE_ID: { NormalConformanceID conformanceID; NormalProtocolConformanceIdLayout::readRecord(scratch, conformanceID); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index d7257e3f490d4..eb6f35d1a66fe 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 583; // @actorIndependent safe/unsafe attribute +const uint16_t SWIFTMODULE_VERSION_MINOR = 584; // builtin protocol conformances /// A standard hash seed used for all string hashes in a serialized module. /// @@ -1628,6 +1628,14 @@ namespace decls_block { TypeIDField // the conforming type >; + using BuiltinProtocolConformanceLayout = BCRecordLayout< + BUILTIN_PROTOCOL_CONFORMANCE, + TypeIDField, // the conforming type + DeclIDField, // the protocol + BCVBR<5> // the number of element conformances + // the (optional) element conformances follow + >; + // Refers to a normal protocol conformance in the given module via its id. using NormalProtocolConformanceIdLayout = BCRecordLayout< NORMAL_PROTOCOL_CONFORMANCE_ID, diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index dc881ddb61732..0a4b6c9fee616 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -845,6 +845,8 @@ void Serializer::writeBlockInfoBlock() { decls_block::SPECIALIZED_PROTOCOL_CONFORMANCE); BLOCK_RECORD_WITH_NAMESPACE(sil_block, decls_block::INHERITED_PROTOCOL_CONFORMANCE); + BLOCK_RECORD_WITH_NAMESPACE(sil_block, + decls_block::BUILTIN_PROTOCOL_CONFORMANCE); BLOCK_RECORD_WITH_NAMESPACE(sil_block, decls_block::NORMAL_PROTOCOL_CONFORMANCE_ID); BLOCK_RECORD_WITH_NAMESPACE(sil_block, @@ -1506,6 +1508,17 @@ Serializer::writeConformance(ProtocolConformanceRef conformanceRef, writeConformance(conf->getInheritedConformance(), abbrCodes, genericEnv); break; } + case ProtocolConformanceKind::Builtin: + auto builtin = cast(conformance); + unsigned abbrCode = abbrCodes[BuiltinProtocolConformanceLayout::Code]; + auto typeID = addTypeRef(builtin->getType()); + auto protocolID = addDeclRef(builtin->getProtocol()); + BuiltinProtocolConformanceLayout::emitRecord(Out, ScratchRecord, abbrCode, + typeID, protocolID, + builtin->getConformances().size()); + + writeConformances(builtin->getConformances(), abbrCodes); + break; } } @@ -4588,6 +4601,7 @@ void Serializer::writeAllDeclsAndTypes() { registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); + registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 70fa8597c5470..1f3d1aae2051c 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -2732,6 +2732,7 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) { registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); + registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); From 4b71d6776ea4b4c47c28889f9543fbb047eb3241 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sat, 23 May 2020 11:10:14 -0400 Subject: [PATCH 651/745] [ABI] Introduce MetadataKind TypeRef --- include/swift/ABI/Metadata.h | 21 ++++++++++++++++++- include/swift/ABI/MetadataValues.h | 8 +++++-- include/swift/Remote/MetadataReader.h | 5 ++++- stdlib/public/runtime/Metadata.cpp | 5 ++++- stdlib/public/runtime/ProtocolConformance.cpp | 11 ++++++++-- .../Compatibility51/ProtocolConformance.cpp | 3 +++ 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 9fbb260dc66ad..4bfcc243aebe8 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -2325,6 +2325,7 @@ struct TargetTypeMetadataRecord { // in the future) are just never used in these lists. case TypeReferenceKind::DirectObjCClassName: case TypeReferenceKind::IndirectObjCClass: + case TypeReferenceKind::MetadataKind: return nullptr; } @@ -2415,6 +2416,9 @@ struct TargetTypeReference { /// A direct reference to an Objective-C class name. RelativeDirectPointer DirectObjCClassName; + + /// A "reference" to some metadata kind, e.g. tuple. + MetadataKind MetadataKind; }; const TargetContextDescriptor * @@ -2428,12 +2432,18 @@ struct TargetTypeReference { case TypeReferenceKind::DirectObjCClassName: case TypeReferenceKind::IndirectObjCClass: + case TypeReferenceKind::MetadataKind: return nullptr; } return nullptr; } + enum MetadataKind getMetadataKind(TypeReferenceKind kind) const { + assert(kind == TypeReferenceKind::MetadataKind); + return MetadataKind; + } + #if SWIFT_OBJC_INTEROP /// If this type reference is one of the kinds that supports ObjC /// references, @@ -2519,6 +2529,10 @@ struct TargetProtocolConformanceDescriptor final return Flags.getTypeReferenceKind(); } + enum MetadataKind getMetadataKind() const { + return TypeRef.getMetadataKind(getTypeKind()); + } + const char *getDirectObjCClassName() const { return TypeRef.getDirectObjCClassName(getTypeKind()); } @@ -2546,6 +2560,11 @@ struct TargetProtocolConformanceDescriptor final TargetRelativeContextPointer>(); } + /// Whether this conformance is builtin by the compiler + runtime. + bool isBuiltin() const { + return getTypeKind() == TypeReferenceKind::MetadataKind; + } + /// Whether this conformance is non-unique because it has been synthesized /// for a foreign type. bool isSynthesizedNonUnique() const { diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index 76c5fd75a3845..00adcbdba61d4 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -384,10 +384,14 @@ enum class TypeReferenceKind : unsigned { /// unused. IndirectObjCClass = 0x03, + /// The conformance is for a non-nominal type whose metadata kind we recorded; + /// getMetadataKind() returns the kind. + MetadataKind = 0x04, + // We only reserve three bits for this in the various places we store it. First_Kind = DirectTypeDescriptor, - Last_Kind = IndirectObjCClass, + Last_Kind = MetadataKind, }; /// Flag that indicates whether an existential type is class-constrained or not. diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index 589bef52d8a68..bee120e64e0bb 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -1414,6 +1414,9 @@ class MetadataReader { return metadataFn(metadata); } + case TypeReferenceKind::MetadataKind: { + return None; + } } return None; diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index a4c941cd0f3a3..96d5325fa737e 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -193,6 +193,9 @@ computeMetadataBoundsForSuperclass(const void *ref, break; #endif } + // Type metadata type ref is unsupported here. + case TypeReferenceKind::MetadataKind: + break; } swift_unreachable("unsupported superclass reference kind"); } diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index f3fa79c578a72..bc03e0633dd11 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -83,11 +83,14 @@ template<> void ProtocolConformanceDescriptor::dump() const { case TypeReferenceKind::IndirectTypeDescriptor: printf("unique nominal type descriptor %s", symbolName(getTypeDescriptor())); break; + case TypeReferenceKind::MetadataKind: + printf("metadata kind %i", getMetadataKind()); + break; } printf(" => "); - printf("witness table %pattern s\n", symbolName(getWitnessTablePattern())); + printf("witness table pattern %p\n", symbolName(getWitnessTablePattern())); } #endif @@ -113,6 +116,7 @@ const ClassMetadata *TypeReference::getObjCClass(TypeReferenceKind kind) const { case TypeReferenceKind::DirectTypeDescriptor: case TypeReferenceKind::IndirectTypeDescriptor: + case TypeReferenceKind::MetadataKind: return nullptr; } @@ -153,6 +157,9 @@ ProtocolConformanceDescriptor::getCanonicalTypeMetadata() const { return nullptr; } + case TypeReferenceKind::MetadataKind: { + return nullptr; + } } swift_unreachable("Unhandled TypeReferenceKind in switch."); diff --git a/stdlib/toolchain/Compatibility51/ProtocolConformance.cpp b/stdlib/toolchain/Compatibility51/ProtocolConformance.cpp index 09bd46ea02700..dd96167686be5 100644 --- a/stdlib/toolchain/Compatibility51/ProtocolConformance.cpp +++ b/stdlib/toolchain/Compatibility51/ProtocolConformance.cpp @@ -229,6 +229,9 @@ override_getCanonicalTypeMetadata(const ProtocolConformanceDescriptor *conf) { return nullptr; } + case TypeReferenceKind::MetadataKind: { + return nullptr; + } } swift_unreachable("Unhandled TypeReferenceKind in switch."); From e60ef84bd278d5cd4b5799bad21f9871169122d5 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sat, 30 May 2020 16:11:46 -0400 Subject: [PATCH 652/745] Implement Tuple Equatable Conformance --- .../Runtime/BuiltinProtocolConformances.h | 51 ++++++ lib/AST/Module.cpp | 24 ++- lib/IRGen/GenProto.cpp | 33 +++- lib/IRGen/IRGenMangler.cpp | 22 ++- lib/IRGen/IRGenModule.cpp | 7 +- lib/SIL/IR/SIL.cpp | 7 +- .../runtime/BuiltinProtocolConformances.cpp | 146 ++++++++++++++++++ stdlib/public/runtime/CMakeLists.txt | 1 + stdlib/public/runtime/Metadata.cpp | 34 +++- stdlib/public/runtime/ProtocolConformance.cpp | 43 ++++++ test/IDE/complete_expr_tuple.swift | 14 +- test/IRGen/tuple_equatable_conformance.swift | 28 ++++ .../tuple_equatable_conformance.swift | 70 +++++++++ test/Misc/misc_diagnostics.swift | 2 - test/SILOptimizer/pound_assert.swift | 5 + 15 files changed, 464 insertions(+), 23 deletions(-) create mode 100644 include/swift/Runtime/BuiltinProtocolConformances.h create mode 100644 stdlib/public/runtime/BuiltinProtocolConformances.cpp create mode 100644 test/IRGen/tuple_equatable_conformance.swift create mode 100644 test/Interpreter/tuple_equatable_conformance.swift diff --git a/include/swift/Runtime/BuiltinProtocolConformances.h b/include/swift/Runtime/BuiltinProtocolConformances.h new file mode 100644 index 0000000000000..f6d540e1bf599 --- /dev/null +++ b/include/swift/Runtime/BuiltinProtocolConformances.h @@ -0,0 +1,51 @@ +//===--- BuiltinProtocolWitnessTable.h --------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Swift runtime support for builtin protocol witnesses and related items. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RUNTIME_BUILTINPROTOCOLCONFORMANCES_H +#define SWIFT_RUNTIME_BUILTINPROTOCOLCONFORMANCES_H + +#include "swift/ABI/Metadata.h" + +namespace swift { + +#define STR(a) #a +#define XSTR(a) STR(a) +#define SYMBOL(name) XSTR(__USER_LABEL_PREFIX__) name + +// public protocol Equatable {} +#define SWIFT_EQUATABLE_MANGLING SQ + +#define PROTOCOL_DESCRIPTOR_MANGLING Mp + +#define PROTOCOL_DESCRIPTOR_SYM(Proto) \ + MANGLE_SYM(MANGLING_CONCAT2(Proto, PROTOCOL_DESCRIPTOR_MANGLING)) + +#define EQUATABLE_PROTOCOL_DESCRIPTOR \ + PROTOCOL_DESCRIPTOR_SYM(SWIFT_EQUATABLE_MANGLING) + +#define TUPLE_EQUATABLE_CONF SYMBOL("_swift_tupleEquatable_conf") +#define TUPLE_EQUATABLE_EQUALS SYMBOL("_swift_tupleEquatable_equals") + +/// The protocol witness for static Swift.Equatable.== infix(A, A) -> Swift.Bool +/// in conformance (A...): Swift.Equatable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool _swift_tupleEquatable_equals(OpaqueValue *tuple1, OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable); + +} // end namespace swift + +#endif diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 6b4a60c42d24f..7ceca1f6a62af 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -1013,6 +1013,28 @@ LookupConformanceInModuleRequest::evaluate( if (type->is()) return ProtocolConformanceRef(protocol); + // Tuples have builtin conformances implemented within the runtime. + // These conformances so far consist of Equatable. + if (auto tuple = type->getAs()) { + if (protocol == ctx.getProtocol(KnownProtocolKind::Equatable)) { + SmallVector elementConformances; + + // Ensure that every element in this tuple conforms to Equatable. + for (auto eltTy : tuple->getElementTypes()) { + auto conformance = mod->lookupConformance(eltTy, protocol); + + if (conformance.isInvalid()) + return ProtocolConformanceRef::forInvalid(); + + elementConformances.push_back(conformance); + } + + auto conformance = ctx.getBuiltinConformance(tuple, protocol, + elementConformances); + return ProtocolConformanceRef(conformance); + } + } + auto nominal = type->getAnyNominal(); // If we don't have a nominal type, there are no conformances. diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 15899ea44be2f..724387162f787 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -906,6 +906,16 @@ static bool isDependentConformance( IRGenModule &IGM, const RootProtocolConformance *rootConformance, llvm::SmallPtrSet &visited){ + // Some Builtin conformances are dependent. + if (auto builtin = dyn_cast(rootConformance)) { + // Tuples are conditionally conformed to any builtin conformance. + if (builtin->getType()->is()) + return true; + + // Otherwise, this builtin conformance is not dependant. + return false; + } + // Self-conformances are never dependent. auto conformance = dyn_cast(rootConformance); if (!conformance) @@ -971,12 +981,10 @@ static bool isSynthesizedNonUnique(const RootProtocolConformance *conformance) { static llvm::Value * emitConditionalConformancesBuffer(IRGenFunction &IGF, const ProtocolConformance *substConformance) { - auto rootConformance = - dyn_cast(substConformance->getRootConformance()); + auto rootConformance = substConformance->getRootConformance(); - // Not a normal conformance means no conditional requirements means no need - // for a buffer. - if (!rootConformance) + if (!isa(rootConformance) && + !isa(rootConformance)) return llvm::UndefValue::get(IGF.IGM.WitnessTablePtrPtrTy); // Pointers to the witness tables, in the right order, which will be included @@ -986,9 +994,18 @@ emitConditionalConformancesBuffer(IRGenFunction &IGF, auto subMap = substConformance->getSubstitutions(IGF.IGM.getSwiftModule()); SILWitnessTable::enumerateWitnessTableConditionalConformances( - rootConformance, [&](unsigned, CanType type, ProtocolDecl *proto) { + rootConformance, [&](unsigned i, CanType type, ProtocolDecl *proto) { auto substType = type.subst(subMap)->getCanonicalType(); auto reqConformance = subMap.lookupConformance(type, proto); + + // Builtin conformances don't have a substitution list, so accomodate + // for that here. + if (auto builtin + = dyn_cast(rootConformance)) { + substType = type->getCanonicalType(); + reqConformance = builtin->getConformances()[i]; + } + assert(reqConformance && "conditional conformance must be valid"); tables.push_back(emitWitnessTableRef(IGF, substType, reqConformance)); @@ -1137,8 +1154,10 @@ class AccessorConformanceInfo : public ConformanceInfo { llvm::Value *getTable(IRGenFunction &IGF, llvm::Value **typeMetadataCache) const override { // If we're looking up a dependent type, we can't cache the result. + // If the conformance is builtin, go ahead and emit an accessor call. if (Conformance->getType()->hasArchetype() || - Conformance->getType()->hasDynamicSelfType()) { + Conformance->getType()->hasDynamicSelfType() || + isa(Conformance)) { return emitWitnessTableAccessorCall(IGF, Conformance, typeMetadataCache); } diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index 720a85ecc9887..972c9bf991af0 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -147,6 +147,26 @@ IRGenMangler::mangleTypeForReflection(IRGenModule &IGM, std::string IRGenMangler::mangleProtocolConformanceDescriptor( const RootProtocolConformance *conformance) { + // Builtin conformances are different because they don't use a mangled name + // for their conformance descriptors. For now, we use predefined symbol names + // just in case in the future we have some conflict between actual + // conformances and these builtin ones. + if (isa(conformance)) { + auto &ctx = conformance->getType()->getASTContext(); + + if (conformance->getType()->is()) { + auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable); + + if (conformance->getProtocol() == equatable) { + return "_swift_tupleEquatable_conf"; + } + + llvm_unreachable("mangling unknown tuple witness table protocol"); + } + + llvm_unreachable("mangling unknown builtin witness table type"); + } + beginMangling(); if (isa(conformance)) { appendProtocolConformance(conformance); diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 5df5d617e02ae..a765911fe124a 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -972,6 +972,11 @@ bool IRGenerator::canEmitWitnessTableLazily(SILWitnessTable *wt) { if (wt->getLinkage() == SILLinkage::Shared) return true; + // If we happen to see a builtin witness table here, we can't emit those. + // The runtime has those for us. + if (isa(wt->getConformance())) + return false; + NominalTypeDecl *ConformingTy = wt->getConformingType()->getNominalOrBoundGenericNominal(); diff --git a/lib/SIL/IR/SIL.cpp b/lib/SIL/IR/SIL.cpp index e9c739af20805..ef497ac51eb9b 100644 --- a/lib/SIL/IR/SIL.cpp +++ b/lib/SIL/IR/SIL.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -80,6 +80,11 @@ swift::getLinkageForProtocolConformance(const RootProtocolConformance *C, if (isa(C->getDeclContext()->getModuleScopeContext())) return SILLinkage::Shared; + // If the conforming type is a non-nomianl, give it public linkage. + // These conformances are implemented within the runtime. + if (!C->getType()->getAnyNominal()) + return definition ? SILLinkage::Public : SILLinkage::PublicExternal; + auto typeDecl = C->getType()->getNominalOrBoundGenericNominal(); AccessLevel access = std::min(C->getProtocol()->getEffectiveAccess(), typeDecl->getEffectiveAccess()); diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp new file mode 100644 index 0000000000000..ef8177dfeb357 --- /dev/null +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -0,0 +1,146 @@ +//===--- BuiltinProtocolConformances.cpp ----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Definitions of some builtin protocol witnesses. +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/BuiltinProtocolConformances.h" +#include "swift/Runtime/Casting.h" +#include "swift/Runtime/Debug.h" +#include "swift/Runtime/Metadata.h" + +using namespace swift; + +extern const ProtocolDescriptor +PROTOCOL_DESCRIPTOR_SYM(SWIFT_EQUATABLE_MANGLING); + +#if defined(__ELF__) +// Create a GOT equivalent for the Equatable reference. +__asm( + " .type got.$sSQMp, @object\n" + " .section .data.rel.ro\n" + " .p2align 3\n" + "got.$sSQMp:\n" + " .quad ($sSQMp)\n" + " .size got.$sSQMp, 8\n" +); + +// Create a GOT equivalent for the Equatable.== method descriptor. +__asm( + " .type got.$sSQ2eeoiySbx_xtFZTq, @object\n" + " .p2align 3\n" + "got.$sSQ2eeoiySbx_xtFZTq:\n" + " .quad ($sSQ2eeoiySbx_xtFZTq)\n" + " .size got.$sSQ2eeoiySbx_xtFZTq, 8\n" +); +#endif + +// Define the conformance descriptor for tuple Equatable. We do this in +// assembly to work around relative reference issues. +__asm( + #if defined(__ELF__) + " .type __swift_tupleEquatable_private, @object\n" + " .local __swift_tupleEquatable_private\n" + " .comm __swift_tupleEquatable_private, 128, 16\n" + " .protected " TUPLE_EQUATABLE_CONF "\n" + " .type " TUPLE_EQUATABLE_CONF ", @object\n" + " .section .rodata\n" + #elif defined(__MACH__) + " .zerofill __DATA, __bss, __swift_tupleEquatable_private, 128, 4\n" + " .section __TEXT, __const\n" + #endif + " .globl " TUPLE_EQUATABLE_CONF "\n" + " .p2align 2\n" + TUPLE_EQUATABLE_CONF ":\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Equatable protocol descriptor, hence why we add 1 to indicate indirect. + " .long (got.$sSQMp - (" TUPLE_EQUATABLE_CONF ")) + 1\n" + #elif defined(__MACH__) + " .long _$sSQMp@GOTPCREL + 5\n" + #endif + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This indicates that we have no witness table pattern. We use a generic + // witness table for builtin conformances. + " .long 0\n" + // 196640 are the ConformanceFlags with the type reference bit set to + // MetadataKind, the has resilient witness bit, and the generic witness table + // bit. + " .long 196640\n" + // This 1 is the ResilientWitnessesHeader indicating we have 1 resilient + // witness. + " .long 1\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Equatable.== method descriptor, hence why we add 1 to indicate indirect. + " .long ((got.$sSQ2eeoiySbx_xtFZTq - (" TUPLE_EQUATABLE_CONF ")) - 20) + 1\n" + #elif defined(__MACH__) + " .long _$sSQ2eeoiySbx_xtFZTq@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the equals witness defined below. + " .long ((" TUPLE_EQUATABLE_EQUALS ") - (" TUPLE_EQUATABLE_CONF ")) - 24\n" + // The witness table size in words. + " .short 0\n" + // The witness table private size in words & requires instantiation. + " .short 1\n" + // The witness table instantiator function. + " .long 0\n" + // This is a direct relative reference to the private data for the + // conformance. + " .long (__swift_tupleEquatable_private - (" TUPLE_EQUATABLE_CONF ")) - 36\n" + #if defined(__ELF__) + " .size " TUPLE_EQUATABLE_CONF ", 40\n" + #endif +); + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable) { + auto tuple = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // Grab the specific witness for this element type. + auto equatableTable = reinterpret_cast(conformance); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + using Fn = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, + SWIFT_CONTEXT const Metadata *, + const Metadata *, const WitnessTable *); + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function + auto result = equals(value1, value2, elt.Type, elt.Type, conformance); + + // If the values aren't equal, this tuple isn't equal. :) + if (!result) + return false; + } + + // Otherwise this tuple has value equality with all elements. + return true; +} diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index 56d5110ef94d8..0250f90024630 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -29,6 +29,7 @@ set(swift_runtime_sources AnyHashableSupport.cpp Array.cpp BackDeployment.cpp + BuiltinProtocolConformances.cpp Casting.cpp CompatibilityOverride.cpp CygwinPort.cpp diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 96d5325fa737e..77c930c06b9df 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -20,6 +20,7 @@ #include "swift/Basic/Range.h" #include "swift/Demangling/Demangler.h" #include "swift/ABI/TypeIdentity.h" +#include "swift/Runtime/BuiltinProtocolConformances.h" #include "swift/Runtime/Casting.h" #include "swift/Runtime/EnvironmentVariables.h" #include "swift/Runtime/ExistentialContainer.h" @@ -4477,18 +4478,27 @@ class WitnessTableCacheEntry : const Metadata *type, const ProtocolConformanceDescriptor *conformance, const void * const *instantiationArgs) { - return getWitnessTableSize(conformance); + return getWitnessTableSize(type, conformance); } size_t getExtraAllocationSize() const { - return getWitnessTableSize(Conformance); + return getWitnessTableSize(Type, Conformance); } - static size_t getWitnessTableSize( + static size_t getWitnessTableSize(const Metadata *type, const ProtocolConformanceDescriptor *conformance) { auto protocol = conformance->getProtocol(); auto genericTable = conformance->getGenericWitnessTable(); size_t numPrivateWords = genericTable->getWitnessTablePrivateSizeInWords(); + + // Builtin conformance descriptors, namely tuple conformances at the moment, + // have their private size in words be determined via the number of elements + // the type has. + if (conformance->isBuiltin()) { + auto tuple = cast(type); + numPrivateWords = tuple->NumElements; + } + size_t numRequirementWords = WitnessTableFirstRequirementOffset + protocol->NumRequirements; return (numPrivateWords + numRequirementWords) * sizeof(void*); @@ -4744,6 +4754,14 @@ instantiateWitnessTable(const Metadata *Type, // Number of bytes for any private storage used by the conformance itself. size_t privateSizeInWords = genericTable->getWitnessTablePrivateSizeInWords(); + // Builtin conformance descriptors, namely tuple conformances at the moment, + // have their private size in words be determined via the number of elements + // the type has. + if (conformance->isBuiltin()) { + auto tuple = cast(Type); + privateSizeInWords = tuple->NumElements; + } + // Advance the address point; the private storage area is accessed via // negative offsets. auto table = fullTable + privateSizeInWords; @@ -4786,6 +4804,16 @@ instantiateWitnessTable(const Metadata *Type, if (conditionalRequirement.Flags.hasExtraArgument()) copyNextInstantiationArg(); } + + // Builtin conformance descriptors, namely tuple conformances at the moment, + // have instantiation arguments equal to the number of element conformances. + if (conformance->isBuiltin()) { + auto tuple = cast(Type); + + for (size_t i = 0; i != tuple->NumElements; i += 1) { + copyNextInstantiationArg(); + } + } } // Fill in any default requirements. diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index bc03e0633dd11..d14412f9ff2e2 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -16,6 +16,7 @@ #include "swift/Basic/Lazy.h" #include "swift/Demangling/Demangle.h" +#include "swift/Runtime/BuiltinProtocolConformances.h" #include "swift/Runtime/Casting.h" #include "swift/Runtime/Concurrent.h" #include "swift/Runtime/HeapObject.h" @@ -375,6 +376,38 @@ searchInConformanceCache(const Metadata *type, return {false, nullptr}; } +extern const ProtocolDescriptor EQUATABLE_PROTOCOL_DESCRIPTOR; + +static bool tupleConformsToProtocol(const Metadata *type, + const ProtocolDescriptor *protocol) { + auto tuple = cast(type); + + // At the moment, tuples can only conform to Equatable, so reject all other + // protocols. + auto equatable = &EQUATABLE_PROTOCOL_DESCRIPTOR; + if (protocol != equatable) + return false; + + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + if (!swift_conformsToProtocol(elt.Type, protocol)) + return false; + } + + return true; +} + +extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; + +static const ProtocolConformanceDescriptor *getTupleConformanceDescriptor( + const ProtocolDescriptor *protocol) { + if (protocol == &EQUATABLE_PROTOCOL_DESCRIPTOR) { + return &_swift_tupleEquatable_conf; + } + + return nullptr; +} + namespace { /// Describes a protocol conformance "candidate" that can be checked /// against a type metadata. @@ -494,6 +527,16 @@ swift_conformsToProtocolImpl(const Metadata *const type, } } + // If we're asking if a tuple conforms to a protocol, handle that here for + // builtin conformances. + if (auto tuple = dyn_cast(type)) { + if (tupleConformsToProtocol(tuple, protocol)) { + auto descriptor = getTupleConformanceDescriptor(protocol); + C.cacheResult(type, protocol, descriptor->getWitnessTable(type), + /*always cache*/ 0); + } + } + // Try the search again to look for the most specific cached conformance. found = searchInConformanceCache(type, protocol); diff --git a/test/IDE/complete_expr_tuple.swift b/test/IDE/complete_expr_tuple.swift index 0f2295f8b99c2..7d863a856ba05 100644 --- a/test/IDE/complete_expr_tuple.swift +++ b/test/IDE/complete_expr_tuple.swift @@ -27,7 +27,7 @@ func testTupleNoDot1() { var t = (1, 2.0) t#^TUPLE_NO_DOT_1^# } -// TUPLE_NO_DOT_1: Begin completions, 10 items +// TUPLE_NO_DOT_1: Begin completions, 11 items // TUPLE_NO_DOT_1-DAG: Pattern/CurrNominal: .0[#Int#]{{; name=.+$}} // TUPLE_NO_DOT_1-DAG: Pattern/CurrNominal: .1[#Double#]{{; name=.+$}} // TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(Int, Double)#}[#Bool#]{{; name=.+$}} @@ -44,14 +44,14 @@ func testTupleNoDot2() { var t = (foo: 1, bar: 2.0) t#^TUPLE_NO_DOT_2^# } -// TUPLE_NO_DOT_2: Begin completions, 10 items +// TUPLE_NO_DOT_2: Begin completions, 11 items // TUPLE_NO_DOT_2-DAG: Pattern/CurrNominal: .foo[#Int#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Pattern/CurrNominal: .bar[#Double#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: BuiltinOperator/None: = {#(foo: Int, bar: Double)#}[#Void#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Keyword[self]/CurrNominal: .self[#(foo: Int, bar: Double)#]; name=self @@ -61,14 +61,14 @@ func testTupleNoDot3() { var t = (foo: 1, 2.0) t#^TUPLE_NO_DOT_3^# } -// TUPLE_NO_DOT_3: Begin completions, 10 items +// TUPLE_NO_DOT_3: Begin completions, 11 items // TUPLE_NO_DOT_3-DAG: Pattern/CurrNominal: .foo[#Int#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Pattern/CurrNominal: .1[#Double#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: BuiltinOperator/None: = {#(foo: Int, Double)#}[#Void#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Keyword[self]/CurrNominal: .self[#(foo: Int, Double)#]; name=self diff --git a/test/IRGen/tuple_equatable_conformance.swift b/test/IRGen/tuple_equatable_conformance.swift new file mode 100644 index 0000000000000..8242fa8139184 --- /dev/null +++ b/test/IRGen/tuple_equatable_conformance.swift @@ -0,0 +1,28 @@ +// RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s + +// CHECK-LABEL: @_swift_tupleEquatable_conf = external global %swift.protocol_conformance_descriptor + +struct Wrapper { + let value: T +} + +extension Wrapper: Equatable where T: Equatable {} + +public func equals(_ lhs: T, _ rhs: T) -> Bool { + lhs == rhs +} + +public func use(_ thing: T) -> Bool { + // CHECK: [[USE_WT:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleEquatable_conf, %swift.type* {{%.*}}, i8*** {{%.*}}) + // CHECK-NEXT: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_WT]]) + equals((thing, thing), (thing, thing)) +} + +public func test() { + // CHECK: [[TEST_WT1:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleEquatable_conf, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8*** undef) + // CHECK: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture undef, %swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_WT1]]) + let _ = equals((), ()) + + // CHECK: {{%.*}} = call swiftcc i1 {{.*}}({{%.*}}.0* noalias nocapture undef, {{%.*}}.0* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_WT1]]) + let _ = Wrapper(value: ()) == Wrapper(value: ()) +} diff --git a/test/Interpreter/tuple_equatable_conformance.swift b/test/Interpreter/tuple_equatable_conformance.swift new file mode 100644 index 0000000000000..64992461e55dd --- /dev/null +++ b/test/Interpreter/tuple_equatable_conformance.swift @@ -0,0 +1,70 @@ +// RUN: %target-run-simple-swift | %FileCheck %s +// REQUIRES: executable_test + +func equals(_ lhs: T, _ rhs: T) -> Bool { + lhs == rhs +} + +// CHECK: true +print(equals((), ())) + +// CHECK: true +print(equals((128, 316), (128, 316))) + +// CHECK: false +print(equals((128, 316), (316, 128))) + +// CHECK: true +print(equals(((1, 2), 3), ((1, 2), 3))) + +// CHECK: false +print(equals(((1, 2), 3), ((1, 2), 4))) + +@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) +func opaqueEquatableValue() -> some Equatable { + (1, 2, 3, 4, 5) +} + +if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) { + print(opaqueEquatableValue() == opaqueEquatableValue()) +} + +struct Wrapper { + let value: T +} + +extension Wrapper: Equatable where T: Equatable {} + +// CHECK: true +print(Wrapper(value: ()) == Wrapper(value: ())) + +// CHECK: true +print(Wrapper(value: (128, 316)) == Wrapper(value: (128, 316))) + +// CHECK: false +print(Wrapper(value: (128, 316)) == Wrapper(value: (316, 128))) + +func use(_ thing: T) -> Bool { + equals((thing, thing), (thing, thing)) +} + +// CHECK: true +print(use(128)) + +class Foo: Equatable { + var age: Int + + init(age: Int) { + self.age = age + } + + static func == (lhs: Foo, rhs: Foo) -> Bool { + lhs.age == rhs.age + } +} + +// CHECK: true +print(equals((Foo(age: 128), false, 0), (Foo(age: 128), false, 0))) + +// CHECK: false +print(equals((Foo(age: 128), false, 0), (Foo(age: 316), false, 0))) diff --git a/test/Misc/misc_diagnostics.swift b/test/Misc/misc_diagnostics.swift index 6bf146ba0ff36..944e145477d8d 100644 --- a/test/Misc/misc_diagnostics.swift +++ b/test/Misc/misc_diagnostics.swift @@ -143,8 +143,6 @@ func test17875634() { func test20770032() { if case let 1...10 = (1, 1) { // expected-warning{{'let' pattern has no effect; sub-pattern didn't bind any variables}} {{11-15=}} // expected-error@-1 {{expression pattern of type 'ClosedRange' cannot match values of type '(Int, Int)'}} - // expected-error@-2 {{type '(Int, Int)' cannot conform to 'Equatable'; only concrete types such as structs, enums and classes can conform to protocols}} - // expected-note@-3 {{required by operator function '~=' where 'T' = '(Int, Int)'}} } } diff --git a/test/SILOptimizer/pound_assert.swift b/test/SILOptimizer/pound_assert.swift index d593786a3afa0..f73a45dd5bb01 100644 --- a/test/SILOptimizer/pound_assert.swift +++ b/test/SILOptimizer/pound_assert.swift @@ -228,12 +228,17 @@ func test_genericAdd() { #assert(genericAdd(1, 1) == 2) } +// FIXME: The following is disabled for now because tuples conform to Equatable +// now, but the work needed for SIL to optimize away the witness method is not +// there yet. +/* func test_tupleAsGeneric() { func identity(_ t: T) -> T { return t } #assert(identity((1, 2)) == (1, 2)) } +*/ //===----------------------------------------------------------------------===// // Reduced testcase propagating substitutions around. From df9778e2e8b068c6b1df9fe012d8a598605c8ff1 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sat, 6 Jun 2020 18:45:40 -0400 Subject: [PATCH 653/745] [Compatibility53] Add compatibility library for 5.3 and backport tuple Equatable conformance Fix some comments Unnecessary cast --- include/swift/Frontend/BackDeploymentLibs.def | 1 + lib/Basic/Platform.cpp | 12 +- lib/Driver/DarwinToolChains.cpp | 17 +- lib/Frontend/CompilerInvocation.cpp | 2 + stdlib/toolchain/CMakeLists.txt | 1 + .../Compatibility50/ProtocolConformance.cpp | 8 + .../toolchain/Compatibility51/Overrides.cpp | 6 + .../BuiltinProtocolConformances.cpp | 137 +++++++++++ .../toolchain/Compatibility53/CMakeLists.txt | 34 +++ .../Compatibility53/CompatibilityOverride.def | 226 ++++++++++++++++++ .../Compatibility53/CompatibilityOverride.h | 61 +++++ .../toolchain/Compatibility53/Overrides.cpp | 38 +++ stdlib/toolchain/Compatibility53/Overrides.h | 29 +++ .../Compatibility53/ProtocolConformance.cpp | 92 +++++++ .../autolink-runtime-compatibility.swift | 16 ++ test/stdlib/Compatibility50Linking.c | 2 +- 16 files changed, 669 insertions(+), 13 deletions(-) create mode 100644 stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp create mode 100644 stdlib/toolchain/Compatibility53/CMakeLists.txt create mode 100644 stdlib/toolchain/Compatibility53/CompatibilityOverride.def create mode 100644 stdlib/toolchain/Compatibility53/CompatibilityOverride.h create mode 100644 stdlib/toolchain/Compatibility53/Overrides.cpp create mode 100644 stdlib/toolchain/Compatibility53/Overrides.h create mode 100644 stdlib/toolchain/Compatibility53/ProtocolConformance.cpp diff --git a/include/swift/Frontend/BackDeploymentLibs.def b/include/swift/Frontend/BackDeploymentLibs.def index b2d9f8a84acd9..7a2ba2d638c86 100644 --- a/include/swift/Frontend/BackDeploymentLibs.def +++ b/include/swift/Frontend/BackDeploymentLibs.def @@ -26,6 +26,7 @@ BACK_DEPLOYMENT_LIB((5, 0), all, "swiftCompatibility50") BACK_DEPLOYMENT_LIB((5, 1), all, "swiftCompatibility51") +BACK_DEPLOYMENT_LIB((5, 3), all, "swiftCompatibility53") BACK_DEPLOYMENT_LIB((5, 0), executable, "swiftCompatibilityDynamicReplacements") #undef BACK_DEPLOYMENT_LIB diff --git a/lib/Basic/Platform.cpp b/lib/Basic/Platform.cpp index ad1db82c717cf..ebd24e0fd7062 100644 --- a/lib/Basic/Platform.cpp +++ b/lib/Basic/Platform.cpp @@ -378,8 +378,10 @@ swift::getSwiftRuntimeCompatibilityVersionForTarget( } else if (Minor <= 15) { if (Micro <= 3) { return llvm::VersionTuple(5, 1); - } else { + } else if (Micro <= 4) { return llvm::VersionTuple(5, 2); + } else { + return llvm::VersionTuple(5, 3); } } } else if (Major == 11) { @@ -399,8 +401,10 @@ swift::getSwiftRuntimeCompatibilityVersionForTarget( } else if (Major <= 13) { if (Minor <= 3) { return llvm::VersionTuple(5, 1); - } else { + } else if (Minor <= 4) { return llvm::VersionTuple(5, 2); + } else { + return llvm::VersionTuple(5, 3); } } } else if (Triple.isWatchOS()) { @@ -410,8 +414,10 @@ swift::getSwiftRuntimeCompatibilityVersionForTarget( } else if (Major <= 6) { if (Minor <= 1) { return llvm::VersionTuple(5, 1); - } else { + } else if (Minor <= 2) { return llvm::VersionTuple(5, 2); + } else { + return llvm::VersionTuple(5, 3); } } } diff --git a/lib/Driver/DarwinToolChains.cpp b/lib/Driver/DarwinToolChains.cpp index 95accad29f2bd..6a828f0e7d071 100644 --- a/lib/Driver/DarwinToolChains.cpp +++ b/lib/Driver/DarwinToolChains.cpp @@ -378,19 +378,18 @@ toolchains::Darwin::addArgsToLinkStdlib(ArgStringList &Arguments, // have an older Swift runtime. SmallString<128> SharedResourceDirPath; getResourceDirPath(SharedResourceDirPath, context.Args, /*Shared=*/true); - Optional runtimeCompatibilityVersion; + Optional runtimeCompatibilityVersion + = llvm::VersionTuple(); if (context.Args.hasArg(options::OPT_runtime_compatibility_version)) { auto value = context.Args.getLastArgValue( options::OPT_runtime_compatibility_version); - if (value.equals("5.0")) { - runtimeCompatibilityVersion = llvm::VersionTuple(5, 0); - } else if (value.equals("5.1")) { - runtimeCompatibilityVersion = llvm::VersionTuple(5, 1); - } else if (value.equals("none")) { - runtimeCompatibilityVersion = None; - } else { - // TODO: diagnose unknown runtime compatibility version? + if (runtimeCompatibilityVersion->tryParse(value)) { + if (value.equals("none")) { + runtimeCompatibilityVersion = None; + } else { + // TODO: diagnose unknown runtime compatibility version? + } } } else if (job.getKind() == LinkKind::Executable) { runtimeCompatibilityVersion diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 568c7a6aa7e65..aacb59c859485 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1598,6 +1598,8 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, runtimeCompatibilityVersion = llvm::VersionTuple(5, 0); } else if (version.equals("5.1")) { runtimeCompatibilityVersion = llvm::VersionTuple(5, 1); + } else if (version.equals("5.3")) { + runtimeCompatibilityVersion = llvm::VersionTuple(5, 3); } else { Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, versionArg->getAsString(Args), version); diff --git a/stdlib/toolchain/CMakeLists.txt b/stdlib/toolchain/CMakeLists.txt index 9cda30ee3e91b..fa5782ef212e8 100644 --- a/stdlib/toolchain/CMakeLists.txt +++ b/stdlib/toolchain/CMakeLists.txt @@ -53,4 +53,5 @@ set(COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_WATCHOS "2.0") add_subdirectory(legacy_layouts) add_subdirectory(Compatibility50) add_subdirectory(Compatibility51) +add_subdirectory(Compatibility53) add_subdirectory(CompatibilityDynamicReplacements) diff --git a/stdlib/toolchain/Compatibility50/ProtocolConformance.cpp b/stdlib/toolchain/Compatibility50/ProtocolConformance.cpp index a48e801a5a43a..a0e7309cee65c 100644 --- a/stdlib/toolchain/Compatibility50/ProtocolConformance.cpp +++ b/stdlib/toolchain/Compatibility50/ProtocolConformance.cpp @@ -18,6 +18,7 @@ //===----------------------------------------------------------------------===// #include "Overrides.h" +#include "../Compatibility53/Overrides.h" #include "../../public/runtime/Private.h" #include "swift/Basic/Lazy.h" #include @@ -94,6 +95,13 @@ swift::swift50override_conformsToProtocol(const Metadata *type, static OnceToken_t token; SWIFT_ONCE_F(token, registerAddImageCallback, nullptr); + // The Swift 5.4 runtime added support for builtin conformances. Call 5.3's + // backported implementation to handle that here. + if (auto result = + swift53override_conformsToProtocol(type, protocol, + original_conformsToProtocol)) + return result; + // The implementation of swift_conformsToProtocol in Swift 5.0 would return // a false negative answer when asking whether a subclass conforms using // a conformance from a superclass. Work around this by walking up the diff --git a/stdlib/toolchain/Compatibility51/Overrides.cpp b/stdlib/toolchain/Compatibility51/Overrides.cpp index f9a89ec39e115..df6c3efb5d61d 100644 --- a/stdlib/toolchain/Compatibility51/Overrides.cpp +++ b/stdlib/toolchain/Compatibility51/Overrides.cpp @@ -16,6 +16,7 @@ #include "CompatibilityOverride.h" #include "Overrides.h" +#include "../Compatibility53/Overrides.h" #include #include @@ -33,6 +34,11 @@ struct OverrideSection { OverrideSection Swift51Overrides __attribute__((used, section("__DATA,__swift51_hooks"))) = { .version = 0, + // We use the same hook for conformsToProtocol as we do for a 5.3 + // runtime, so reference the override from the Compatibility53 library. + // If we're back deploying to Swift 5.1, we also have to support 5.3, so + // the Compatibility53 library is always linked when the 51 library is. + .conformsToProtocol = swift53override_conformsToProtocol, .conformsToSwiftProtocol = swift51override_conformsToSwiftProtocol, }; diff --git a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp new file mode 100644 index 0000000000000..1d39c759c458e --- /dev/null +++ b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp @@ -0,0 +1,137 @@ +//===--- BuiltinProtocolConformances.cpp ----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Definitions of some builtin protocol witnesses. +// +//===----------------------------------------------------------------------===// + +#include "Overrides.h" +#include "swift/Basic/Lazy.h" +#include "swift/Runtime/BuiltinProtocolConformances.h" +#include "swift/Runtime/Casting.h" +#include +#include + +using namespace swift; + +static const ProtocolDescriptor *getEquatableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSQMp"))); + return descriptor; +} + +static const WitnessTable *conformsToProtocol(const Metadata *type, + const ProtocolDescriptor *protocol) { + using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *); + auto func = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "swift_conformsToProtocol"))); + return func(type, protocol); +} + +#define TUPLE_EQUATABLE_WT SYMBOL("_swift_tupleEquatable_wt") + +// Define the conformance descriptor for tuple Equatable. We do this in +// assembly to work around relative reference issues. +__asm( + " .section __DATA,__data\n" + " .globl " TUPLE_EQUATABLE_CONF "\n" + " .p2align 2\n" + TUPLE_EQUATABLE_CONF ":\n" + // This is an indirectable relative reference to the Equatable protocol + // descriptor. However, this is 0 here because the compatibility libraries + // can't have a dependency on libswiftCore (which is where Equatable lives). + " .long 0\n" + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This is a direct relative reference to the witness table defined below. + " .long ((" TUPLE_EQUATABLE_WT ") - (" TUPLE_EQUATABLE_CONF ")) - 8\n" + // 32 are the ConformanceFlags with the type reference bit set to MetadataKind. + " .long 32\n" +); + +extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; + +// Due to the fact that the compatibility libraries can't have a hard +// dependency to libswiftCore (which is where the Equatable protocol desciptor +// lives), we have to manually implant this before calling any user code. +__attribute__((constructor)) +void _emplaceEquatableDescriptor() { + auto tupleEquatableConf = const_cast( + reinterpret_cast(&_swift_tupleEquatable_conf)); + auto equatable = getEquatableDescriptor(); + + // This is an indirectable pointer. + *tupleEquatableConf = intptr_t(equatable) - intptr_t(tupleEquatableConf); +} + +template +struct _WitnessTable { + const ProtocolConformanceDescriptor *Conformance; + const void *Witnesses[NumWitnesses]; +}; + +SWIFT_RUNTIME_EXPORT +const _WitnessTable<1> _swift_tupleEquatable_wt = { + &_swift_tupleEquatable_conf, + { + reinterpret_cast(_swift_tupleEquatable_equals) + } +}; + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable) { + auto tuple = cast(Self); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Ensure we actually have a conformance to Equatable for this element type. + auto equatable = getEquatableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, equatable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Equatable...?? + if (!conformance) + swift_unreachable("Tuple equality requires that all elements be \ + Equatable."); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // Grab the specific witness for this element type. + auto equatableTable = reinterpret_cast(conformance); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + using Fn = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, + SWIFT_CONTEXT const Metadata *, + const Metadata *, const WitnessTable *); + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto result = equals(value1, value2, elt.Type, elt.Type, conformance); + + // If the values aren't equal, this tuple isn't equal. :) + if (!result) + return false; + } + + // Otherwise this tuple has value equality with all elements. + return true; +} diff --git a/stdlib/toolchain/Compatibility53/CMakeLists.txt b/stdlib/toolchain/Compatibility53/CMakeLists.txt new file mode 100644 index 0000000000000..61da832cf6d46 --- /dev/null +++ b/stdlib/toolchain/Compatibility53/CMakeLists.txt @@ -0,0 +1,34 @@ +set(library_name "swiftCompatibility53") + +add_swift_target_library("${library_name}" STATIC + BuiltinProtocolConformances.cpp + Overrides.cpp + ProtocolConformance.cpp + + TARGET_SDKS ${SWIFT_APPLE_PLATFORMS} + + C_COMPILE_FLAGS ${CXX_COMPILE_FLAGS} + LINK_FLAGS ${CXX_LINK_FLAGS} + SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + DEPLOYMENT_VERSION_OSX ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_OSX} + DEPLOYMENT_VERSION_IOS ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_IOS} + DEPLOYMENT_VERSION_TVOS ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_TVOS} + DEPLOYMENT_VERSION_WATCHOS ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_WATCHOS} + + INSTALL_IN_COMPONENT compiler + INSTALL_WITH_SHARED) + + +# FIXME: We need a more flexible mechanism to add lipo targets generated by +# add_swift_target_library to the ALL target. Until then this hack is necessary +# to ensure these libraries build. +foreach(sdk ${SWIFT_SDKS}) + set(target_name "${library_name}-${SWIFT_SDK_${sdk}_LIB_SUBDIR}") + if(NOT TARGET "${target_name}") + continue() + endif() + + set_target_properties("${target_name}" + PROPERTIES + EXCLUDE_FROM_ALL FALSE) +endforeach() diff --git a/stdlib/toolchain/Compatibility53/CompatibilityOverride.def b/stdlib/toolchain/Compatibility53/CompatibilityOverride.def new file mode 100644 index 0000000000000..a7a34a8852100 --- /dev/null +++ b/stdlib/toolchain/Compatibility53/CompatibilityOverride.def @@ -0,0 +1,226 @@ +//===--- CompatibilityOverrides.def - Compatibility Overrides Database -*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines x-macros used for metaprogramming with the set of +// compatibility override functions. +// +//===----------------------------------------------------------------------===// + +/// #define OVERRIDE(name, ret, attrs, namespace, typedArgs, namedArgs) +/// Provides information about an overridable function. +/// - name is the name of the function, without any leading swift_ or +/// namespace. +/// - ret is the return type of the function. +/// - attrs is the attributes, if any, applied to the function definition. +/// - namespace is the namespace, if any, the function is in, including a +/// trailing :: +/// - typedArgs is the argument list, including types, surrounded by +/// parentheses +/// - namedArgs is the list of argument names, with no types, surrounded by +/// parentheses +/// +/// The entries are organized by group. A user may define OVERRIDE to get all +/// entries, or define one or more of OVERRIDE_METADATALOOKUP, OVERRIDE_CASTING, +/// OVERRIDE_OBJC, OVERRIDE_FOREIGN, OVERRIDE_PROTOCOLCONFORMANCE, +/// and OVERRIDE_KEYPATH to get only those entries. + +// NOTE: this file is used to build the definition of OverrideSection in +// CompatibilityOverride.cpp, which is part of the ABI. Do not move or remove entries +// in this file after ABI stability. Additional entries can be added to the end. + +#ifdef OVERRIDE +# define OVERRIDE_METADATALOOKUP OVERRIDE +# define OVERRIDE_CASTING OVERRIDE +# define OVERRIDE_OBJC OVERRIDE +# define OVERRIDE_FOREIGN OVERRIDE +# define OVERRIDE_PROTOCOLCONFORMANCE OVERRIDE +# define OVERRIDE_KEYPATH OVERRIDE +# define OVERRIDE_WITNESSTABLE OVERRIDE +#else +# ifndef OVERRIDE_METADATALOOKUP +# define OVERRIDE_METADATALOOKUP(...) +# endif +# ifndef OVERRIDE_CASTING +# define OVERRIDE_CASTING(...) +# endif +# ifndef OVERRIDE_OBJC +# define OVERRIDE_OBJC(...) +# endif +# ifndef OVERRIDE_FOREIGN +# define OVERRIDE_FOREIGN(...) +# endif +# ifndef OVERRIDE_PROTOCOLCONFORMANCE +# define OVERRIDE_PROTOCOLCONFORMANCE(...) +# endif +# ifndef OVERRIDE_KEYPATH +# define OVERRIDE_KEYPATH(...) +# endif +# ifndef OVERRIDE_WITNESSTABLE +# define OVERRIDE_WITNESSTABLE(...) +# endif +#endif + +OVERRIDE_CASTING(dynamicCast, bool, , , swift::, + (OpaqueValue *dest, OpaqueValue *src, + const Metadata *srcType, + const Metadata *targetType, + DynamicCastFlags flags), + (dest, src, srcType, targetType, flags)) + + +OVERRIDE_CASTING(dynamicCastClass, const void *, , , swift::, + (const void *object, + const ClassMetadata *targetType), + (object, targetType)) + + +OVERRIDE_CASTING(dynamicCastClassUnconditional, const void *, , , swift::, + (const void *object, + const ClassMetadata *targetType, + const char *file, unsigned line, unsigned column), + (object, targetType, file, line, column)) + + + +OVERRIDE_CASTING(dynamicCastUnknownClass, const void *, , , swift::, + (const void *object, const Metadata *targetType), + (object, targetType)) + + +OVERRIDE_CASTING(dynamicCastUnknownClassUnconditional, const void *, , , swift::, + (const void *object, const Metadata *targetType, + const char *file, unsigned line, unsigned column), + (object, targetType, file, line, column)) + + +OVERRIDE_CASTING(dynamicCastMetatype, const Metadata *, , , swift::, + (const Metadata *sourceType, + const Metadata *targetType), + (sourceType, targetType)) + + +OVERRIDE_CASTING(dynamicCastMetatypeUnconditional, const Metadata *, , , swift::, + (const Metadata *sourceType, + const Metadata *targetType, + const char *file, unsigned line, unsigned column), + (sourceType, targetType, file, line, column)) + + +OVERRIDE_FOREIGN(dynamicCastForeignClassMetatype, const ClassMetadata *, , , swift::, + (const ClassMetadata *sourceType, + const ClassMetadata *targetType), + (sourceType, targetType)) + + +OVERRIDE_FOREIGN(dynamicCastForeignClassMetatypeUnconditional, + const ClassMetadata *, , , swift::, + (const ClassMetadata *sourceType, + const ClassMetadata *targetType, + const char *file, unsigned line, unsigned column), + (sourceType, targetType, file, line, column)) + + +OVERRIDE_PROTOCOLCONFORMANCE(conformsToProtocol, const WitnessTable *, , , swift::, + (const Metadata * const type, + const ProtocolDescriptor *protocol), + (type, protocol)) + +OVERRIDE_PROTOCOLCONFORMANCE(conformsToSwiftProtocol, + const ProtocolConformanceDescriptor *, , , swift::, + (const Metadata * const type, + const ProtocolDescriptor *protocol, + StringRef moduleName), + (type, protocol, moduleName)) + +OVERRIDE_KEYPATH(getKeyPath, const HeapObject *, , , swift::, + (const void *pattern, const void *arguments), + (pattern, arguments)) + +OVERRIDE_METADATALOOKUP(getTypeByMangledNode, TypeInfo, , SWIFT_CC(swift), swift::, + (MetadataRequest request, + Demangler &demangler, + Demangle::NodePointer node, + const void * const *arguments, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable), + (request, demangler, node, arguments, substGenericParam, substWitnessTable)) +OVERRIDE_METADATALOOKUP(getTypeByMangledName, TypeInfo, , SWIFT_CC(swift), swift::, + (MetadataRequest request, + StringRef typeName, + const void * const *arguments, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable), + (request, typeName, arguments, substGenericParam, substWitnessTable)) + +OVERRIDE_WITNESSTABLE(getAssociatedTypeWitnessSlow, MetadataResponse, + SWIFT_RUNTIME_STDLIB_INTERNAL, SWIFT_CC(swift), swift::, + (MetadataRequest request, WitnessTable *wtable, + const Metadata *conformingType, + const ProtocolRequirement *reqBase, + const ProtocolRequirement *assocType), + (request, wtable, conformingType, reqBase, assocType)) + +OVERRIDE_WITNESSTABLE(getAssociatedConformanceWitnessSlow, const WitnessTable *, + SWIFT_RUNTIME_STDLIB_INTERNAL, SWIFT_CC(swift), swift::, + (WitnessTable *wtable, const Metadata *conformingType, + const Metadata *assocType, + const ProtocolRequirement *reqBase, + const ProtocolRequirement *assocConformance), + (wtable, conformingType, assocType, reqBase, + assocConformance)) +#if SWIFT_OBJC_INTEROP + +OVERRIDE_OBJC(dynamicCastObjCClass, const void *, , , swift::, + (const void *object, + const ClassMetadata *targetType), + (object, targetType)) + + +OVERRIDE_OBJC(dynamicCastObjCClassUnconditional, const void *, , , swift::, + (const void *object, + const ClassMetadata *targetType, + const char *file, unsigned line, unsigned column), + (object, targetType, file, line, column)) + +OVERRIDE_OBJC(dynamicCastObjCClassMetatype, const ClassMetadata *, , , swift::, + (const ClassMetadata *sourceType, + const ClassMetadata *targetType), + (sourceType, targetType)) + + +OVERRIDE_OBJC(dynamicCastObjCClassMetatypeUnconditional, const ClassMetadata *, , , swift::, + (const ClassMetadata *sourceType, const ClassMetadata *targetType, + const char *file, unsigned line, unsigned column), + (sourceType, targetType, file, line, column)) + + +OVERRIDE_FOREIGN(dynamicCastForeignClass, const void *, , , swift::, + (const void *object, + const ForeignClassMetadata *targetType), + (object, targetType)) + + +OVERRIDE_FOREIGN(dynamicCastForeignClassUnconditional, const void *, , , swift::, + (const void *object, const ForeignClassMetadata *targetType, + const char *file, unsigned line, unsigned column), + (object, targetType, file, line, column)) + +#endif + +#undef OVERRIDE +#undef OVERRIDE_METADATALOOKUP +#undef OVERRIDE_CASTING +#undef OVERRIDE_OBJC +#undef OVERRIDE_FOREIGN +#undef OVERRIDE_PROTOCOLCONFORMANCE +#undef OVERRIDE_KEYPATH +#undef OVERRIDE_WITNESSTABLE diff --git a/stdlib/toolchain/Compatibility53/CompatibilityOverride.h b/stdlib/toolchain/Compatibility53/CompatibilityOverride.h new file mode 100644 index 0000000000000..4c7c5b3c47d6b --- /dev/null +++ b/stdlib/toolchain/Compatibility53/CompatibilityOverride.h @@ -0,0 +1,61 @@ +//===--- CompatibiltyOverride.h - Back-deploying compatibility fixes --*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Support back-deploying compatibility fixes for newer apps running on older runtimes. +// +//===----------------------------------------------------------------------===// + +#ifndef COMPATIBILITY_OVERRIDE_H +#define COMPATIBILITY_OVERRIDE_H + +#include "../../public/runtime/Private.h" +#include "swift/Runtime/Metadata.h" +#include "swift/Runtime/Once.h" +#include + +namespace swift { + +#define COMPATIBILITY_UNPAREN(...) __VA_ARGS__ + +#define OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + ccAttrs typedef ret (*Original_ ## name) typedArgs; +#include "CompatibilityOverride.def" + +#define OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + ccAttrs typedef ret (*Override_ ## name)(COMPATIBILITY_UNPAREN typedArgs, \ + Original_ ## name originalImpl); +#include "CompatibilityOverride.def" + +#define OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + Override_ ## name getOverride_ ## name(); +#include "CompatibilityOverride.def" + + +/// Used to define an override point. The override point #defines the appropriate +/// OVERRIDE macro from CompatibilityOverride.def to this macro, then includes +/// the file to generate the override points. The original implementation of the +/// functionality must be available as swift_funcNameHereImpl. +#define COMPATIBILITY_OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + attrs ccAttrs ret namespace swift_ ## name typedArgs { \ + static Override_ ## name Override; \ + static swift_once_t Predicate; \ + swift_once(&Predicate, [](void *) { \ + Override = getOverride_ ## name(); \ + }, nullptr); \ + if (Override != nullptr) \ + return Override(COMPATIBILITY_UNPAREN namedArgs, swift_ ## name ## Impl); \ + return swift_ ## name ## Impl namedArgs; \ + } + +} /* end namespace swift */ + +#endif /* COMPATIBILITY_OVERRIDE_H */ diff --git a/stdlib/toolchain/Compatibility53/Overrides.cpp b/stdlib/toolchain/Compatibility53/Overrides.cpp new file mode 100644 index 0000000000000..b88014d009df6 --- /dev/null +++ b/stdlib/toolchain/Compatibility53/Overrides.cpp @@ -0,0 +1,38 @@ +//===--- Overrides.cpp - Compat override table for Swift 5.3 runtime ------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file provides compatibility override hooks for Swift 5.3 runtimes. +// +//===----------------------------------------------------------------------===// + +#include "CompatibilityOverride.h" +#include "Overrides.h" + +using namespace swift; + +struct OverrideSection { + uintptr_t version; +#define OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + Override_ ## name name; +#include "../../public/runtime/CompatibilityOverride.def" +}; + +OverrideSection Swift53Overrides +__attribute__((used, section("__DATA,__swift53_hooks"))) = { + .version = 0, + .conformsToProtocol = swift53override_conformsToProtocol, +}; + +// Allow this library to get force-loaded by autolinking +__attribute__((weak, visibility("hidden"))) +extern "C" +char _swift_FORCE_LOAD_$_swiftCompatibility53 = 0; diff --git a/stdlib/toolchain/Compatibility53/Overrides.h b/stdlib/toolchain/Compatibility53/Overrides.h new file mode 100644 index 0000000000000..baba40f72e7b6 --- /dev/null +++ b/stdlib/toolchain/Compatibility53/Overrides.h @@ -0,0 +1,29 @@ +//===--- Overrides.cpp - Compat overrides for Swift 5.3 runtime ----s------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file provides compatibility override hooks for Swift 5.3 runtimes. +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/Metadata.h" + +namespace swift { + +using ConformsToProtocol_t = + const WitnessTable *(const Metadata *, const ProtocolDescriptor *); + +const WitnessTable * +swift53override_conformsToProtocol(const Metadata * const type, + const ProtocolDescriptor *protocol, + ConformsToProtocol_t *original); + +} // end namespace swift diff --git a/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp b/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp new file mode 100644 index 0000000000000..8520769271543 --- /dev/null +++ b/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp @@ -0,0 +1,92 @@ +//===--- ProtocolConformance.cpp - Swift protocol conformance checking ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Checking of Swift protocol conformances. +// +// This implementation is intended to be backward-deployed into Swift 5.3 and +// later runtimes. +// +//===----------------------------------------------------------------------===// + +#include "Overrides.h" +#include "swift/Basic/Lazy.h" +#include "swift/Runtime/BuiltinProtocolConformances.h" +#include +#include + +using namespace swift; + +static const ProtocolDescriptor *getEquatableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSQMp"))); + return descriptor; +} + +static const WitnessTable *conformsToProtocol(const Metadata *type, + const ProtocolDescriptor *protocol) { + using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *); + auto func = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "swift_conformsToProtocol"))); + return func(type, protocol); +} + +static bool tupleConformsToProtocol(const Metadata *type, + const ProtocolDescriptor *protocol) { + auto tuple = cast(type); + + // At the moment, tuples can only conform to Equatable, so reject all other + // protocols. + if (protocol != getEquatableDescriptor()) + return false; + + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + if (!conformsToProtocol(elt.Type, protocol)) + return false; + } + + return true; +} + +extern const WitnessTable _swift_tupleEquatable_wt; + +static const WitnessTable *getTupleConformanceWitnessTable( + const ProtocolDescriptor *protocol) { + if (protocol == getEquatableDescriptor()) { + return &_swift_tupleEquatable_wt; + } + + return nullptr; +} + +const WitnessTable * +swift::swift53override_conformsToProtocol(const Metadata *type, + const ProtocolDescriptor *protocol, + ConformsToProtocol_t *original_conformsToProtocol) +{ + // Swift 5.4 introduces tuple Equatable conformance, so ensure that Swift 5.3 + // and later runtimes can handle this as well. + if (auto tuple = dyn_cast(type)) { + if (!tupleConformsToProtocol(type, protocol)) + return nullptr; + + return getTupleConformanceWitnessTable(protocol); + } + + auto result = original_conformsToProtocol(type, protocol); + if (result) + return result; + + return nullptr; +} diff --git a/test/IRGen/autolink-runtime-compatibility.swift b/test/IRGen/autolink-runtime-compatibility.swift index f917f2115481a..a06fc8bb4457d 100644 --- a/test/IRGen/autolink-runtime-compatibility.swift +++ b/test/IRGen/autolink-runtime-compatibility.swift @@ -16,14 +16,17 @@ // Autolinks because compatibility library was explicitly asked for // RUN: %target-swift-frontend -runtime-compatibility-version 5.0 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD %s // RUN: %target-swift-frontend -runtime-compatibility-version 5.1 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD-51 %s +// RUN: %target-swift-frontend -runtime-compatibility-version 5.3 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD-53 %s // RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.24 -runtime-compatibility-version 5.0 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD %s // RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.24 -runtime-compatibility-version 5.1 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD-51 %s +// RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.24 -runtime-compatibility-version 5.3 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD-53 %s public func foo() {} // NO-FORCE-LOAD-NOT: FORCE_LOAD // NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibility50"} // NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibility51"} +// NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibility53"} // NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibilityDynamicReplacements"} // FORCE-LOAD: declare {{.*}} @"_swift_FORCE_LOAD_$_swiftCompatibility50" @@ -42,3 +45,16 @@ public func foo() {} // FORCE-LOAD-51-DAG: !llvm.linker.options = !{{{.*}}[[AUTOLINK_SWIFT_COMPAT]]{{[,}]}} // FORCE-LOAD-51-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" // FORCE-LOAD-51-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" + +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility51" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" +// FORCE-LOAD-53: declare {{.*}} @"_swift_FORCE_LOAD_$_swiftCompatibility53" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility51" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" +// FORCE-LOAD-53-DAG: [[AUTOLINK_SWIFT_COMPAT:![0-9]+]] = !{!"-lswiftCompatibility53"} +// FORCE-LOAD-53-DAG: !llvm.linker.options = !{{{.*}}[[AUTOLINK_SWIFT_COMPAT]]{{[,}]}} +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility51" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" diff --git a/test/stdlib/Compatibility50Linking.c b/test/stdlib/Compatibility50Linking.c index deb98344323b7..8963a1b2b6e92 100644 --- a/test/stdlib/Compatibility50Linking.c +++ b/test/stdlib/Compatibility50Linking.c @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-clang %s -all_load %test-resource-dir/%target-sdk-name/libswiftCompatibility50.a %test-resource-dir/%target-sdk-name/libswiftCompatibility51.a -lobjc -o %t/main +// RUN: %target-clang %s -all_load %test-resource-dir/%target-sdk-name/libswiftCompatibility50.a %test-resource-dir/%target-sdk-name/libswiftCompatibility51.a %test-resource-dir/%target-sdk-name/libswiftCompatibility53.a -lobjc -o %t/main // RUN: %target-codesign %t/main // RUN: %target-run %t/main // REQUIRES: objc_interop From fd950ebbf35d93991417cc91d8b7f6d757c63d11 Mon Sep 17 00:00:00 2001 From: Azoy Date: Fri, 17 Jul 2020 20:18:17 -0400 Subject: [PATCH 654/745] Implement Tuple Comparable Conformance Add protocol witnesses for all Comparable requirements --- .../Runtime/BuiltinProtocolConformances.h | 76 ++- lib/AST/Module.cpp | 30 +- lib/IRGen/IRGenMangler.cpp | 10 +- .../runtime/BuiltinProtocolConformances.cpp | 499 +++++++++++++++++- stdlib/public/runtime/ProtocolConformance.cpp | 19 +- ...re_clause_contextually_generic_decls.swift | 2 +- test/IDE/complete_expr_tuple.swift | 22 +- test/IRGen/builtin_conformances.swift | 62 +++ test/IRGen/tuple_equatable_conformance.swift | 28 - test/Interpreter/builtin_conformances.swift | 220 ++++++++ .../tuple_equatable_conformance.swift | 70 --- 11 files changed, 884 insertions(+), 154 deletions(-) create mode 100644 test/IRGen/builtin_conformances.swift delete mode 100644 test/IRGen/tuple_equatable_conformance.swift create mode 100644 test/Interpreter/builtin_conformances.swift delete mode 100644 test/Interpreter/tuple_equatable_conformance.swift diff --git a/include/swift/Runtime/BuiltinProtocolConformances.h b/include/swift/Runtime/BuiltinProtocolConformances.h index f6d540e1bf599..5b2dd9ef3db36 100644 --- a/include/swift/Runtime/BuiltinProtocolConformances.h +++ b/include/swift/Runtime/BuiltinProtocolConformances.h @@ -25,16 +25,22 @@ namespace swift { #define XSTR(a) STR(a) #define SYMBOL(name) XSTR(__USER_LABEL_PREFIX__) name -// public protocol Equatable {} -#define SWIFT_EQUATABLE_MANGLING SQ - #define PROTOCOL_DESCRIPTOR_MANGLING Mp #define PROTOCOL_DESCRIPTOR_SYM(Proto) \ MANGLE_SYM(MANGLING_CONCAT2(Proto, PROTOCOL_DESCRIPTOR_MANGLING)) -#define EQUATABLE_PROTOCOL_DESCRIPTOR \ - PROTOCOL_DESCRIPTOR_SYM(SWIFT_EQUATABLE_MANGLING) +//===----------------------------------------------------------------------===// +// Tuple Equatable Conformance +//===----------------------------------------------------------------------===// + +// public protocol Equatable {} +#define SWIFT_EQUATABLE_MANGLING SQ + +#define EQUATABLE_DESCRIPTOR PROTOCOL_DESCRIPTOR_SYM(SWIFT_EQUATABLE_MANGLING) + +#define EQUATABLE_DESCRIPTOR_SYMBOL SYMBOL("$sSQMp") +#define EQUATABLE_EE_METHOD_DESCRIPTOR SYMBOL("$sSQ2eeoiySbx_xtFZTq") #define TUPLE_EQUATABLE_CONF SYMBOL("_swift_tupleEquatable_conf") #define TUPLE_EQUATABLE_EQUALS SYMBOL("_swift_tupleEquatable_equals") @@ -46,6 +52,66 @@ bool _swift_tupleEquatable_equals(OpaqueValue *tuple1, OpaqueValue *tuple2, SWIFT_CONTEXT Metadata *swiftSelf, Metadata *Self, void *witnessTable); +//===----------------------------------------------------------------------===// +// Tuple Comparable Conformance +//===----------------------------------------------------------------------===// + +// public protocol Comparable {} +#define SWIFT_COMPARABLE_MANGLING SL + +#define COMPARABLE_DESCRIPTOR PROTOCOL_DESCRIPTOR_SYM(SWIFT_COMPARABLE_MANGLING) + +#define COMPARABLE_DESCRIPTOR_SYMBOL SYMBOL("$sSLMp") + +#define COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR SYMBOL("$sSLSQTb") +#define COMPARABLE_LT_METHOD_DESCRIPTOR SYMBOL("$sSL1loiySbx_xtFZTq") +#define COMPARBALE_LTE_METHOD_DESCRIPTOR SYMBOL("$sSL2leoiySbx_xtFZTq") +#define COMPARABLE_GTE_METHOD_DESCRIPTOR SYMBOL("$sSL2geoiySbx_xtFZTq") +#define COMPARABLE_GT_METHOD_DESCRIPTOR SYMBOL("$sSL1goiySbx_xtFZTq") + +#define TUPLE_COMPARABLE_CONF SYMBOL("_swift_tupleComparable_conf") +#define TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE \ + SYMBOL("associated conformance _swift_tupleComparable") +#define TUPLE_COMPARABLE_BASEACCESSOREQUATABLE \ + SYMBOL("_swift_tupleComparable_baseAccessorEquatable") +#define TUPLE_COMPARABLE_LESSTHAN SYMBOL("_swift_tupleComparable_lessThan") +#define TUPLE_COMPARABLE_LESSTHANOREQUAL \ + SYMBOL("_swift_tupleComparable_lessThanOrEqual") +#define TUPLE_COMPARABLE_GREATERTHANOREQUAL \ + SYMBOL("_swift_tupleComparable_greaterThanOrEqual") +#define TUPLE_COMPARABLE_GREATERTHAN \ + SYMBOL("_swift_tupleComparable_greaterThan") + +/// The protocol witness for static Swift.Comparable.< infix(A, A) -> Swift.Bool +/// in conformance (A...): Swift.Comparable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool _swift_tupleComparable_lessThan(OpaqueValue *tuple1, OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable); + +/// The protocol witness for static Swift.Comparable.<= infix(A, A) -> Swift.Bool +/// in conformance (A...): Swift.Comparable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool _swift_tupleComparable_lessThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable); + +/// The protocol witness for static Swift.Comparable.>= infix(A, A) -> Swift.Bool +/// in conformance (A...): Swift.Comparable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool _swift_tupleComparable_greaterThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable); + +/// The protocol witness for static Swift.Comparable.> infix(A, A) -> Swift.Bool +/// in conformance (A...): Swift.Comparable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool _swift_tupleComparable_greaterThan(OpaqueValue *tuple1, OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable); + } // end namespace swift #endif diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 7ceca1f6a62af..bd7c57e6bebe1 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -1014,25 +1014,29 @@ LookupConformanceInModuleRequest::evaluate( return ProtocolConformanceRef(protocol); // Tuples have builtin conformances implemented within the runtime. - // These conformances so far consist of Equatable. + // These conformances so far consist of Equatable and Comparable. if (auto tuple = type->getAs()) { - if (protocol == ctx.getProtocol(KnownProtocolKind::Equatable)) { - SmallVector elementConformances; + auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable); + auto comparable = ctx.getProtocol(KnownProtocolKind::Comparable); - // Ensure that every element in this tuple conforms to Equatable. - for (auto eltTy : tuple->getElementTypes()) { - auto conformance = mod->lookupConformance(eltTy, protocol); + if (protocol != equatable && protocol != comparable) + return ProtocolConformanceRef::forInvalid(); - if (conformance.isInvalid()) - return ProtocolConformanceRef::forInvalid(); + SmallVector elementConformances; - elementConformances.push_back(conformance); - } + // Ensure that every element in this tuple conforms to said protocol. + for (auto eltTy : tuple->getElementTypes()) { + auto conformance = mod->lookupConformance(eltTy, protocol); + + if (conformance.isInvalid()) + return ProtocolConformanceRef::forInvalid(); - auto conformance = ctx.getBuiltinConformance(tuple, protocol, - elementConformances); - return ProtocolConformanceRef(conformance); + elementConformances.push_back(conformance); } + + auto conformance = ctx.getBuiltinConformance(tuple, protocol, + elementConformances); + return ProtocolConformanceRef(conformance); } auto nominal = type->getAnyNominal(); diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index 972c9bf991af0..7eb01dbb90a13 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -156,15 +156,21 @@ std::string IRGenMangler::mangleProtocolConformanceDescriptor( if (conformance->getType()->is()) { auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable); + auto comparable = ctx.getProtocol(KnownProtocolKind::Comparable); if (conformance->getProtocol() == equatable) { return "_swift_tupleEquatable_conf"; } - llvm_unreachable("mangling unknown tuple witness table protocol"); + if (conformance->getProtocol() == comparable) { + return "_swift_tupleComparable_conf"; + } + + llvm_unreachable("mangling conformance descriptor for unknown tuple \ + protocol"); } - llvm_unreachable("mangling unknown builtin witness table type"); + llvm_unreachable("mangling conformance descriptor for unknown builtin type"); } beginMangling(); diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp index ef8177dfeb357..a136916b4e8d5 100644 --- a/stdlib/public/runtime/BuiltinProtocolConformances.cpp +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -19,29 +19,37 @@ #include "swift/Runtime/Debug.h" #include "swift/Runtime/Metadata.h" +#include + using namespace swift; -extern const ProtocolDescriptor -PROTOCOL_DESCRIPTOR_SYM(SWIFT_EQUATABLE_MANGLING); +using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, + SWIFT_CONTEXT const Metadata *, + const Metadata *, + const WitnessTable *); + +//===----------------------------------------------------------------------===// +// Tuple Equatable Conformance +//===----------------------------------------------------------------------===// #if defined(__ELF__) // Create a GOT equivalent for the Equatable reference. __asm( - " .type got.$sSQMp, @object\n" + " .type got." EQUATABLE_DESCRIPTOR_SYMBOL ", @object\n" " .section .data.rel.ro\n" " .p2align 3\n" - "got.$sSQMp:\n" - " .quad ($sSQMp)\n" - " .size got.$sSQMp, 8\n" + "got." EQUATABLE_DESCRIPTOR_SYMBOL ":\n" + " .quad (" EQUATABLE_DESCRIPTOR_SYMBOL ")\n" + " .size got." EQUATABLE_DESCRIPTOR_SYMBOL ", 8\n" ); // Create a GOT equivalent for the Equatable.== method descriptor. __asm( - " .type got.$sSQ2eeoiySbx_xtFZTq, @object\n" + " .type got." EQUATABLE_EE_METHOD_DESCRIPTOR ", @object\n" " .p2align 3\n" - "got.$sSQ2eeoiySbx_xtFZTq:\n" - " .quad ($sSQ2eeoiySbx_xtFZTq)\n" - " .size got.$sSQ2eeoiySbx_xtFZTq, 8\n" + "got." EQUATABLE_EE_METHOD_DESCRIPTOR ":\n" + " .quad (" EQUATABLE_EE_METHOD_DESCRIPTOR ")\n" + " .size got." EQUATABLE_EE_METHOD_DESCRIPTOR ", 8\n" ); #endif @@ -65,9 +73,10 @@ __asm( #if defined(__ELF__) // This is an indirectable relative reference to the GOT equivalent for the // Equatable protocol descriptor, hence why we add 1 to indicate indirect. - " .long (got.$sSQMp - (" TUPLE_EQUATABLE_CONF ")) + 1\n" + " .long (got." EQUATABLE_DESCRIPTOR_SYMBOL " - \ + (" TUPLE_EQUATABLE_CONF ")) + 1\n" #elif defined(__MACH__) - " .long _$sSQMp@GOTPCREL + 5\n" + " .long " EQUATABLE_DESCRIPTOR_SYMBOL "@GOTPCREL + 5\n" #endif // 769 is the MetadataKind::Tuple " .long 769\n" @@ -84,9 +93,10 @@ __asm( #if defined(__ELF__) // This is an indirectable relative reference to the GOT equivalent for the // Equatable.== method descriptor, hence why we add 1 to indicate indirect. - " .long ((got.$sSQ2eeoiySbx_xtFZTq - (" TUPLE_EQUATABLE_CONF ")) - 20) + 1\n" + " .long ((got." EQUATABLE_EE_METHOD_DESCRIPTOR " - \ + (" TUPLE_EQUATABLE_CONF ")) - 20) + 1\n" #elif defined(__MACH__) - " .long _$sSQ2eeoiySbx_xtFZTq@GOTPCREL + 5\n" + " .long " EQUATABLE_EE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" #endif // This is a direct relative reference to the equals witness defined below. " .long ((" TUPLE_EQUATABLE_EQUALS ") - (" TUPLE_EQUATABLE_CONF ")) - 24\n" @@ -104,6 +114,8 @@ __asm( #endif ); +extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; + SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, OpaqueValue *tuple2, @@ -128,10 +140,7 @@ bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, // Grab the specific witness for this element type. auto equatableTable = reinterpret_cast(conformance); auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; - using Fn = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, - SWIFT_CONTEXT const Metadata *, - const Metadata *, const WitnessTable *); - auto equals = reinterpret_cast(equalsWitness); + auto equals = reinterpret_cast(equalsWitness); // Call the equal function auto result = equals(value1, value2, elt.Type, elt.Type, conformance); @@ -144,3 +153,457 @@ bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, // Otherwise this tuple has value equality with all elements. return true; } + +//===----------------------------------------------------------------------===// +// Tuple Comparable Conformance +//===----------------------------------------------------------------------===// + +#if defined(__ELF__) +// Create a GOT equivalent for the Comparable reference. +__asm( + " .type got." COMPARABLE_DESCRIPTOR_SYMBOL ", @object\n" + " .section .data.rel.ro\n" + " .p2align 3\n" + "got." COMPARABLE_DESCRIPTOR_SYMBOL ":\n" + " .quad (" COMPARABLE_DESCRIPTOR_SYMBOL ")\n" + " .size got." COMPARABLE_DESCRIPTOR_SYMBOL ", 8\n" +); + +// Create a GOT equivalent for the Comparable base conformance to Equatable. +__asm( + " .type got." COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR ":\n" + " .quad (" COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR ")\n" + " .size got." COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR ", 8\n" +); + +// Create a GOT equivalent for the Comparable.< method descriptor. +__asm( + " .type got." COMPARABLE_LT_METHOD_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." COMPARABLE_LT_METHOD_DESCRIPTOR ":\n" + " .quad (" COMPARABLE_LT_METHOD_DESCRIPTOR ")\n" + " .size got." COMPARABLE_LT_METHOD_DESCRIPTOR ", 8\n" +); + +// Create a GOT equivalent for the Comparable.<= method descriptor. +__asm( + " .type got." COMPARBALE_LTE_METHOD_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." COMPARBALE_LTE_METHOD_DESCRIPTOR ":\n" + " .quad (" COMPARBALE_LTE_METHOD_DESCRIPTOR ")\n" + " .size got." COMPARBALE_LTE_METHOD_DESCRIPTOR ", 8\n" +); + +// Create a GOT equivalent for the Comparable.>= method descriptor. +__asm( + " .type got." COMPARABLE_GTE_METHOD_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." COMPARABLE_GTE_METHOD_DESCRIPTOR ":\n" + " .quad (" COMPARABLE_GTE_METHOD_DESCRIPTOR ")\n" + " .size got." COMPARABLE_GTE_METHOD_DESCRIPTOR ", 8\n" +); + +// Create a GOT equivalent for the Comparable.> method descriptor. +__asm( + " .type got." COMPARABLE_GT_METHOD_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." COMPARABLE_GT_METHOD_DESCRIPTOR ":\n" + " .quad (" COMPARABLE_GT_METHOD_DESCRIPTOR ")\n" + " .size got." COMPARABLE_GT_METHOD_DESCRIPTOR ", 8\n" +); +#endif + +// Define the associated conformance structure for tuple Comparable. We do this +// in assembly to work around relative reference issues. +__asm( + #if defined(__ELF__) + " .hidden \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + " .type \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\", @object\n" + " .section swift5_typeref, \"a\"" + " .weak \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"" + #elif defined(__MACH__) + " .private_extern \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + " .section __TEXT, __swift5_typeref\n" + " .globl \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + " .weak_definition \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + #endif + " .p2align 1\n" + "\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\":\n" + " .byte 255\n" + " .byte 7\n" + // This is a direct relative reference to the base accessor for Equatable + // defined below. + " .long ((" TUPLE_COMPARABLE_BASEACCESSOREQUATABLE ") - \ + (\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\")) - 2\n" + // This 0 is our null terminator. + " .byte 0\n" + #if defined (__ELF__) + " .size \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\", 7\n" + #endif +); + +// Define the conformance descriptor for tuple Comparable. We do this in +// assembly to work around relative reference issues. +__asm( + #if defined(__ELF__) + " .type __swift_tupleComparable_private, @object\n" + " .local __swift_tupleComparable_private\n" + " .comm __swift_tupleComparable_private, 128, 16\n" + " .protected " TUPLE_COMPARABLE_CONF "\n" + " .type " TUPLE_COMPARABLE_CONF ", @object\n" + " .section .rodata\n" + #elif defined(__MACH__) + " .zerofill __DATA, __bss, __swift_tupleComparable_private, 128, 4\n" + " .section __TEXT, __const\n" + #endif + " .globl " TUPLE_COMPARABLE_CONF "\n" + " .p2align 2\n" + TUPLE_COMPARABLE_CONF ":\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Comparable protocol descriptor, hence why we add 1 to indicate indirect. + " .long (got." COMPARABLE_DESCRIPTOR_SYMBOL " - \ + (" TUPLE_COMPARABLE_CONF ")) + 1\n" + #elif defined(__MACH__) + " .long " COMPARABLE_DESCRIPTOR_SYMBOL "@GOTPCREL + 5\n" + #endif + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This indicates that we have no witness table pattern. We use a generic + // witness table for builtin conformances. + " .long 0\n" + // 196640 are the ConformanceFlags with the type reference bit set to + // MetadataKind, the has resilient witness bit, and the generic witness table + // bit. + " .long 196640\n" + // This 5 is the ResilientWitnessesHeader indicating we have 5 resilient + // witnesses. + " .long 5\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Comparable base conformance for Equatable, hence why we add 1 to indicate + // indirect. + " .long ((got." COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR " - \ + (" TUPLE_COMPARABLE_CONF ")) - 20) + 1\n" + #elif defined(__MACH__) + " .long " COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the associated conformance for + // Equatable defined above in assembly. + " .long ((\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - \ + (" TUPLE_COMPARABLE_CONF ")) - 24\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Comparable.< method descriptor, hence why we add 1 to indicate indirect. + " .long ((got." COMPARABLE_LT_METHOD_DESCRIPTOR " - \ + (" TUPLE_COMPARABLE_CONF ")) - 28) + 1\n" + #elif defined(__MACH__) + " .long " COMPARABLE_LT_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the less than witness defined below. + " .long ((" TUPLE_COMPARABLE_LESSTHAN ") - (" TUPLE_COMPARABLE_CONF ")) - 32\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Comparable.<= method descriptor, hence why we add 1 to indicate + // indirect. + " .long ((got." COMPARBALE_LTE_METHOD_DESCRIPTOR " - \ + (" TUPLE_COMPARABLE_CONF ")) - 36) + 1\n" + #elif defined(__MACH__) + " .long " COMPARBALE_LTE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the less than or equal witness + // defined below. + " .long ((" TUPLE_COMPARABLE_LESSTHANOREQUAL ") - \ + (" TUPLE_COMPARABLE_CONF ")) - 40\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Comparable.>= method descriptor, hence why we add 1 to indicate + // indirect. + " .long ((got." COMPARABLE_GTE_METHOD_DESCRIPTOR " - \ + (" TUPLE_COMPARABLE_CONF ")) - 44) + 1\n" + #elif defined(__MACH__) + " .long " COMPARABLE_GTE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the greater than or equal witness + // defined below. + " .long ((" TUPLE_COMPARABLE_GREATERTHANOREQUAL ") - \ + (" TUPLE_COMPARABLE_CONF ")) - 48\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Comparable.> method descriptor, hence why we add 1 to indicate + // indirect. + " .long ((got." COMPARABLE_GT_METHOD_DESCRIPTOR " - \ + (" TUPLE_COMPARABLE_CONF ")) - 52) + 1\n" + #elif defined(__MACH__) + " .long " COMPARABLE_GT_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the greater than witness defined + // below. + " .long ((" TUPLE_COMPARABLE_GREATERTHAN ") - \ + (" TUPLE_COMPARABLE_CONF ")) - 56\n" + // The witness table size in words. + " .short 0\n" + // The witness table private size in words & requires instantiation. + " .short 1\n" + // The witness table instantiator function. + " .long 0\n" + // This is a direct relative reference to the private data for the + // conformance. + " .long (__swift_tupleComparable_private - (" TUPLE_COMPARABLE_CONF ")) - 68\n" + #if defined(__ELF__) + " .size " TUPLE_COMPARABLE_CONF ", 72\n" + #endif +); + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +const WitnessTable * +_swift_tupleComparable_baseAccessorEquatable(Metadata *assocType, + Metadata *conformingType, + void **witnessTable) { + auto tuple = cast(assocType); + std::vector instantiationArgs; + + // Fill the instantiationArgs with the element Equatable tables. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + // Get the element's Comparable table. + auto comparableTable = reinterpret_cast(witnessTable[-1 - i]); + + // The Equatable table is the first requirement, thus it'll be right after + // the conformance descriptor. + auto equatableTable = comparableTable[WitnessTableFirstRequirementOffset]; + + instantiationArgs.push_back(equatableTable); + } + + // Finally, call getWitnessTable to realize the tuple's Equatable table. + auto equatableTable = swift_getWitnessTable(&_swift_tupleEquatable_conf, + assocType, + instantiationArgs.data()); + + return equatableTable; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_lessThan(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their less than function + // and return the result. + auto lessThanWitness = comparableTable[WitnessTableFirstRequirementOffset + 1]; + auto lessThan = reinterpret_cast(lessThanWitness); + + // Call the less than function. + auto isLessThan = lessThan(value1, value2, elt.Type, elt.Type, conformance); + + return isLessThan; + } + + // Otherwise these tuples are completely equal, thus they are not less than + // each other. + return false; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_lessThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their less than or equal + // function and return the result. + auto lessThanOrEqualWitness = + comparableTable[WitnessTableFirstRequirementOffset + 2]; + auto lessThanOrEqual = + reinterpret_cast(lessThanOrEqualWitness); + + // Call the less than function. + auto isLessThanOrEqual = lessThanOrEqual(value1, value2, elt.Type, elt.Type, + conformance); + + return isLessThanOrEqual; + } + + // Otherwise these tuples are completely equal. + return true; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_greaterThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their greater than or + // equal function and return the result. + auto greaterThanOrEqualWitness = + comparableTable[WitnessTableFirstRequirementOffset + 3]; + auto greaterThanOrEqual = + reinterpret_cast(greaterThanOrEqualWitness); + + // Call the greater than or equal function. + auto isGreaterThanOrEqual = greaterThanOrEqual(value1, value2, elt.Type, + elt.Type, conformance); + + return isGreaterThanOrEqual; + } + + // Otherwise these tuples are completely equal. + return true; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their greater than + // function and return the result. + auto greaterThanWitness = + comparableTable[WitnessTableFirstRequirementOffset + 4]; + auto greaterThan = + reinterpret_cast(greaterThanWitness); + + // Call the greater than function. + auto isGreaterThan = greaterThan(value1, value2, elt.Type, elt.Type, + conformance); + + return isGreaterThan; + } + + // Otherwise these tuples are completely equal, thus they are not greater than + // each other. + return false; +} diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index d14412f9ff2e2..6f281e0178156 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -376,16 +376,18 @@ searchInConformanceCache(const Metadata *type, return {false, nullptr}; } -extern const ProtocolDescriptor EQUATABLE_PROTOCOL_DESCRIPTOR; +extern const ProtocolDescriptor EQUATABLE_DESCRIPTOR; +extern const ProtocolDescriptor COMPARABLE_DESCRIPTOR; static bool tupleConformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { auto tuple = cast(type); - // At the moment, tuples can only conform to Equatable, so reject all other - // protocols. - auto equatable = &EQUATABLE_PROTOCOL_DESCRIPTOR; - if (protocol != equatable) + // At the moment, tuples can only conform to Equatable and Comparable, so + // reject all other protocols. + auto equatable = &EQUATABLE_DESCRIPTOR; + auto comparable = &COMPARABLE_DESCRIPTOR; + if (protocol != equatable && protocol != comparable) return false; for (size_t i = 0; i != tuple->NumElements; i += 1) { @@ -398,13 +400,18 @@ static bool tupleConformsToProtocol(const Metadata *type, } extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; +extern const ProtocolConformanceDescriptor _swift_tupleComparable_conf; static const ProtocolConformanceDescriptor *getTupleConformanceDescriptor( const ProtocolDescriptor *protocol) { - if (protocol == &EQUATABLE_PROTOCOL_DESCRIPTOR) { + if (protocol == &EQUATABLE_DESCRIPTOR) { return &_swift_tupleEquatable_conf; } + if (protocol == &COMPARABLE_DESCRIPTOR) { + return &_swift_tupleComparable_conf; + } + return nullptr; } diff --git a/test/Generics/where_clause_contextually_generic_decls.swift b/test/Generics/where_clause_contextually_generic_decls.swift index 27819a167a03f..87afc552198c7 100644 --- a/test/Generics/where_clause_contextually_generic_decls.swift +++ b/test/Generics/where_clause_contextually_generic_decls.swift @@ -147,7 +147,7 @@ extension Container.NestedStruct3 { _ = Container.NestedAlias2.self // expected-error {{type 'String' does not conform to protocol 'FixedWidthInteger'}} _ = Container>.NestedClass.self // expected-error {{type 'Container' does not conform to protocol 'Equatable'}} _ = Container.NestedStruct.self // expected-error {{type 'Void' does not conform to protocol 'Sequence'}} -_ = Container>.NestedStruct2.self // expected-error {{type 'Void' does not conform to protocol 'Comparable'}} +_ = Container>.NestedStruct2.self _ = Container.NestedStruct2.NestedEnum.self // expected-error {{'Container.NestedStruct2.NestedEnum' requires the types 'String.Element' (aka 'Character') and 'Double' be equivalent}} _ = Container.NestedAlias2.self _ = Container.NestedClass.self diff --git a/test/IDE/complete_expr_tuple.swift b/test/IDE/complete_expr_tuple.swift index 7d863a856ba05..b3cb669f4b1be 100644 --- a/test/IDE/complete_expr_tuple.swift +++ b/test/IDE/complete_expr_tuple.swift @@ -27,7 +27,7 @@ func testTupleNoDot1() { var t = (1, 2.0) t#^TUPLE_NO_DOT_1^# } -// TUPLE_NO_DOT_1: Begin completions, 11 items +// TUPLE_NO_DOT_1: Begin completions, 14 items // TUPLE_NO_DOT_1-DAG: Pattern/CurrNominal: .0[#Int#]{{; name=.+$}} // TUPLE_NO_DOT_1-DAG: Pattern/CurrNominal: .1[#Double#]{{; name=.+$}} // TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(Int, Double)#}[#Bool#]{{; name=.+$}} @@ -44,15 +44,15 @@ func testTupleNoDot2() { var t = (foo: 1, bar: 2.0) t#^TUPLE_NO_DOT_2^# } -// TUPLE_NO_DOT_2: Begin completions, 11 items +// TUPLE_NO_DOT_2: Begin completions, 14 items // TUPLE_NO_DOT_2-DAG: Pattern/CurrNominal: .foo[#Int#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Pattern/CurrNominal: .bar[#Double#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: BuiltinOperator/None: = {#(foo: Int, bar: Double)#}[#Void#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Keyword[self]/CurrNominal: .self[#(foo: Int, bar: Double)#]; name=self // TUPLE_NO_DOT_2-NEXT: End completions @@ -61,15 +61,15 @@ func testTupleNoDot3() { var t = (foo: 1, 2.0) t#^TUPLE_NO_DOT_3^# } -// TUPLE_NO_DOT_3: Begin completions, 11 items +// TUPLE_NO_DOT_3: Begin completions, 14 items // TUPLE_NO_DOT_3-DAG: Pattern/CurrNominal: .foo[#Int#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Pattern/CurrNominal: .1[#Double#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: BuiltinOperator/None: = {#(foo: Int, Double)#}[#Void#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Keyword[self]/CurrNominal: .self[#(foo: Int, Double)#]; name=self // TUPLE_NO_DOT_3-NEXT: End completions diff --git a/test/IRGen/builtin_conformances.swift b/test/IRGen/builtin_conformances.swift new file mode 100644 index 0000000000000..2053d69457881 --- /dev/null +++ b/test/IRGen/builtin_conformances.swift @@ -0,0 +1,62 @@ +// RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s + +// CHECK-LABEL: @_swift_tupleEquatable_conf = external global %swift.protocol_conformance_descriptor +// CHECK-LABEL: @_swift_tupleComparable_conf = external global %swift.protocol_conformance_descriptor + +struct Wrapper { + let value: T +} + +//===----------------------------------------------------------------------===// +// Tuple Equatable conformance +//===----------------------------------------------------------------------===// + +extension Wrapper: Equatable where T: Equatable {} + +public func equals(_ lhs: T, _ rhs: T) -> Bool { + lhs == rhs +} + +public func useEquatable(_ thing: T) -> Bool { + // CHECK: [[USE_EQUATABLE_WT:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleEquatable_conf, %swift.type* {{%.*}}, i8*** {{%.*}}) + // CHECK-NEXT: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_EQUATABLE_WT]]) + equals((thing, thing), (thing, thing)) +} + +public func testTupleEquatable() { + // CHECK: [[TEST_TUPLE_EQUATABLE_WT1:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleEquatable_conf, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8*** undef) + // CHECK: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture undef, %swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_EQUATABLE_WT1]]) + let _ = equals((), ()) + + // CHECK: {{%.*}} = call swiftcc i1 {{.*}}({{%.*}}.0* noalias nocapture undef, {{%.*}}.0* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_EQUATABLE_WT1]]) + let _ = Wrapper(value: ()) == Wrapper(value: ()) +} + +//===----------------------------------------------------------------------===// +// Tuple Comparable conformance +//===----------------------------------------------------------------------===// + +extension Wrapper: Comparable where T: Comparable { + static func <(lhs: Wrapper, rhs: Wrapper) -> Bool { + return lhs.value < rhs.value + } +} + +public func compare(_ lhs: T, _ rhs: T) -> Bool { + lhs < rhs +} + +public func useComparable(_ thing: T) -> Bool { + // CHECK: [[USE_COMPARABLE_WT:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleComparable_conf, %swift.type* {{%.*}}, i8*** {{%.*}}) + // CHECK-NEXT: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_COMPARABLE_WT]]) + compare((thing, thing), (thing, thing)) +} + +public func testTupleComparable() { + // CHECK: [[TEST_TUPLE_COMPARABLE_WT1:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleComparable_conf, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8*** undef) + // CHECK: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture undef, %swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_COMPARABLE_WT1]]) + let _ = compare((), ()) + + // CHECK: {{%.*}} = call swiftcc i1 {{.*}}({{%.*}}.1* noalias nocapture undef, {{%.*}}.1* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_COMPARABLE_WT1]]) + let _ = Wrapper(value: ()) < Wrapper(value: ()) +} diff --git a/test/IRGen/tuple_equatable_conformance.swift b/test/IRGen/tuple_equatable_conformance.swift deleted file mode 100644 index 8242fa8139184..0000000000000 --- a/test/IRGen/tuple_equatable_conformance.swift +++ /dev/null @@ -1,28 +0,0 @@ -// RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s - -// CHECK-LABEL: @_swift_tupleEquatable_conf = external global %swift.protocol_conformance_descriptor - -struct Wrapper { - let value: T -} - -extension Wrapper: Equatable where T: Equatable {} - -public func equals(_ lhs: T, _ rhs: T) -> Bool { - lhs == rhs -} - -public func use(_ thing: T) -> Bool { - // CHECK: [[USE_WT:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleEquatable_conf, %swift.type* {{%.*}}, i8*** {{%.*}}) - // CHECK-NEXT: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_WT]]) - equals((thing, thing), (thing, thing)) -} - -public func test() { - // CHECK: [[TEST_WT1:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleEquatable_conf, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8*** undef) - // CHECK: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture undef, %swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_WT1]]) - let _ = equals((), ()) - - // CHECK: {{%.*}} = call swiftcc i1 {{.*}}({{%.*}}.0* noalias nocapture undef, {{%.*}}.0* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_WT1]]) - let _ = Wrapper(value: ()) == Wrapper(value: ()) -} diff --git a/test/Interpreter/builtin_conformances.swift b/test/Interpreter/builtin_conformances.swift new file mode 100644 index 0000000000000..182cb85904608 --- /dev/null +++ b/test/Interpreter/builtin_conformances.swift @@ -0,0 +1,220 @@ +// RUN: %target-run-simple-swift | %FileCheck %s +// REQUIRES: executable_test + +struct Wrapper { + let value: T +} + +extension Wrapper: Equatable where T: Equatable {} + +extension Wrapper: Comparable where T: Comparable { + static func <(lhs: Wrapper, rhs: Wrapper) -> Bool { + lhs.value < rhs.value + } +} + +class Foo { + var age: Int + + init(age: Int) { + self.age = age + } +} + +extension Foo: Equatable { + static func ==(lhs: Foo, rhs: Foo) -> Bool { + lhs.age == rhs.age + } +} + +extension Foo: Comparable { + static func <(lhs: Foo, rhs: Foo) -> Bool { + lhs.age < rhs.age + } +} + +//===----------------------------------------------------------------------===// +// Tuple Equatable Conformance +//===----------------------------------------------------------------------===// + +func equals(_ lhs: T, _ rhs: T) -> Bool { + lhs == rhs +} + +// CHECK: true +print(equals((), ())) + +// CHECK: true +print(equals((128, 316), (128, 316))) + +// CHECK: false +print(equals((128, 316), (316, 128))) + +// CHECK: true +print(equals(((1, 2), 3), ((1, 2), 3))) + +// CHECK: false +print(equals(((1, 2), 3), ((1, 2), 4))) + +@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) +func opaqueTupleEquatableValue() -> some Equatable { + (1, 2, 3, 4, 5) +} + +if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) { + _ = opaqueTupleEquatableValue() == opaqueTupleEquatableValue() +} + +// CHECK: true +print(Wrapper(value: ()) == Wrapper(value: ())) + +// CHECK: true +print(Wrapper(value: (128, 316)) == Wrapper(value: (128, 316))) + +// CHECK: false +print(Wrapper(value: (128, 316)) == Wrapper(value: (316, 128))) + +func useEquatable(_ thing: T) -> Bool { + equals((thing, thing), (thing, thing)) +} + +// CHECK: true +print(useEquatable(128)) + +// CHECK: true +print(equals((Foo(age: 128), false, 0), (Foo(age: 128), false, 0))) + +// CHECK: false +print(equals((Foo(age: 128), false, 0), (Foo(age: 316), false, 0))) + +//===----------------------------------------------------------------------===// +// Tuple Comparable Conformance +//===----------------------------------------------------------------------===// + +func compareLT(_ lhs: T, _ rhs: T) -> Bool { + lhs < rhs +} + +func compareLTE(_ lhs: T, _ rhs: T) -> Bool { + lhs <= rhs +} + +func compareGTE(_ lhs: T, _ rhs: T) -> Bool { + lhs >= rhs +} + +func compareGT(_ lhs: T, _ rhs: T) -> Bool { + lhs > rhs +} + +// false +print(compareLT((), ())) +// true +print(compareLTE((), ())) +// true +print(compareGTE((), ())) +// false +print(compareGT((), ())) + +// false +print(compareLT((1, 2), (1, 2))) +// true +print(compareLTE((1, 2), (1, 2))) +// true +print(compareGTE((1, 2), (1, 2))) +// false +print(compareGT((1, 2), (1, 2))) + + +// true +print(compareLT((1, 2), (2, 1))) +// true +print(compareLTE((1, 2), (2, 1))) +// false +print(compareGTE((1, 2), (2, 1))) +// false +print(compareGT((1, 2), (2, 1))) + + +// false +print(compareLT(((1, 2), 3), ((1, 2), 3))) +// true +print(compareLTE(((1, 2), 3), ((1, 2), 3))) +// true +print(compareGTE(((1, 2), 3), ((1, 2), 3))) +// false +print(compareGT(((1, 2), 3), ((1, 2), 3))) + + +// true +print(compareLT(((1, 2), 3), ((1, 2), 4))) +// true +print(compareLTE(((1, 2), 3), ((1, 2), 4))) +// false +print(compareGTE(((1, 2), 3), ((1, 2), 4))) +// false +print(compareGT(((1, 2), 3), ((1, 2), 4))) + +@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) +func opaqueTupleComparableValue() -> some Comparable { + (1, 2, 3, 4, 5) +} + +if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) { + _ = opaqueTupleComparableValue() < opaqueTupleComparableValue() + _ = opaqueTupleComparableValue() <= opaqueTupleComparableValue() + _ = opaqueTupleComparableValue() >= opaqueTupleComparableValue() + _ = opaqueTupleComparableValue() > opaqueTupleComparableValue() +} + +// CHECK: false +print(Wrapper(value: ()) < Wrapper(value: ())) +// CHECK: true +print(Wrapper(value: ()) <= Wrapper(value: ())) +// CHECK: true +print(Wrapper(value: ()) >= Wrapper(value: ())) +// CHECK: false +print(Wrapper(value: ()) > Wrapper(value: ())) + +// CHECK: false +print(Wrapper(value: (128, 316)) < Wrapper(value: (128, 316))) +// CHECK: true +print(Wrapper(value: (128, 316)) <= Wrapper(value: (128, 316))) +// CHECK: true +print(Wrapper(value: (128, 316)) >= Wrapper(value: (128, 316))) +// CHECK: false +print(Wrapper(value: (128, 316)) > Wrapper(value: (128, 316))) + +// CHECK: true +print(Wrapper(value: (128, 316)) < Wrapper(value: (316, 128))) +// CHECK: true +print(Wrapper(value: (128, 316)) <= Wrapper(value: (316, 128))) +// CHECK: false +print(Wrapper(value: (128, 316)) >= Wrapper(value: (316, 128))) +// CHECK: false +print(Wrapper(value: (128, 316)) > Wrapper(value: (316, 128))) + +func useComparable(_ thing: T) -> Bool { + compareLT((thing, thing), (thing, thing)) +} + +// CHECK: false +print(useComparable(128)) + +// CHECK: false +print(compareLT((Foo(age: 128), 0), (Foo(age: 128), 0))) +// CHECK: true +print(compareLTE((Foo(age: 128), 0), (Foo(age: 128), 0))) +// CHECK: true +print(compareGTE((Foo(age: 128), 0), (Foo(age: 128), 0))) +// CHECK: false +print(compareGT((Foo(age: 128), 0), (Foo(age: 128), 0))) + +// CHECK: true +print(compareLT((Foo(age: 128), 0), (Foo(age: 734), 0))) +// CHECK: true +print(compareLTE((Foo(age: 128), 0), (Foo(age: 734), 0))) +// CHECK: false +print(compareGTE((Foo(age: 128), 0), (Foo(age: 734), 0))) +// CHECK: false +print(compareGT((Foo(age: 128), 0), (Foo(age: 734), 0))) diff --git a/test/Interpreter/tuple_equatable_conformance.swift b/test/Interpreter/tuple_equatable_conformance.swift deleted file mode 100644 index 64992461e55dd..0000000000000 --- a/test/Interpreter/tuple_equatable_conformance.swift +++ /dev/null @@ -1,70 +0,0 @@ -// RUN: %target-run-simple-swift | %FileCheck %s -// REQUIRES: executable_test - -func equals(_ lhs: T, _ rhs: T) -> Bool { - lhs == rhs -} - -// CHECK: true -print(equals((), ())) - -// CHECK: true -print(equals((128, 316), (128, 316))) - -// CHECK: false -print(equals((128, 316), (316, 128))) - -// CHECK: true -print(equals(((1, 2), 3), ((1, 2), 3))) - -// CHECK: false -print(equals(((1, 2), 3), ((1, 2), 4))) - -@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) -func opaqueEquatableValue() -> some Equatable { - (1, 2, 3, 4, 5) -} - -if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) { - print(opaqueEquatableValue() == opaqueEquatableValue()) -} - -struct Wrapper { - let value: T -} - -extension Wrapper: Equatable where T: Equatable {} - -// CHECK: true -print(Wrapper(value: ()) == Wrapper(value: ())) - -// CHECK: true -print(Wrapper(value: (128, 316)) == Wrapper(value: (128, 316))) - -// CHECK: false -print(Wrapper(value: (128, 316)) == Wrapper(value: (316, 128))) - -func use(_ thing: T) -> Bool { - equals((thing, thing), (thing, thing)) -} - -// CHECK: true -print(use(128)) - -class Foo: Equatable { - var age: Int - - init(age: Int) { - self.age = age - } - - static func == (lhs: Foo, rhs: Foo) -> Bool { - lhs.age == rhs.age - } -} - -// CHECK: true -print(equals((Foo(age: 128), false, 0), (Foo(age: 128), false, 0))) - -// CHECK: false -print(equals((Foo(age: 128), false, 0), (Foo(age: 316), false, 0))) From 13b5bf4cfd3c3c4088580a8c20c30067beb352c5 Mon Sep 17 00:00:00 2001 From: Azoy Date: Fri, 17 Jul 2020 20:33:04 -0400 Subject: [PATCH 655/745] [Compatibility53] Backport Tuple Comparable Conformance Add protocol witnesses for Comparable requirements remove unused table --- .../BuiltinProtocolConformances.cpp | 336 +++++++++++++++++- .../Compatibility53/ProtocolConformance.cpp | 21 +- 2 files changed, 341 insertions(+), 16 deletions(-) diff --git a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp index 1d39c759c458e..c3893c8b668b5 100644 --- a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp +++ b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp @@ -30,6 +30,13 @@ static const ProtocolDescriptor *getEquatableDescriptor() { return descriptor; } +static const ProtocolDescriptor *getComparableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSLMp"))); + return descriptor; +} + static const WitnessTable *conformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *); @@ -39,6 +46,21 @@ static const WitnessTable *conformsToProtocol(const Metadata *type, return func(type, protocol); } +template +struct _WitnessTable { + const ProtocolConformanceDescriptor *Conformance; + const void *Witnesses[NumWitnesses]; +}; + +using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, + SWIFT_CONTEXT const Metadata *, + const Metadata *, + const WitnessTable *); + +//===----------------------------------------------------------------------===// +// Tuple Equatable Conformance +//===----------------------------------------------------------------------===// + #define TUPLE_EQUATABLE_WT SYMBOL("_swift_tupleEquatable_wt") // Define the conformance descriptor for tuple Equatable. We do this in @@ -66,7 +88,7 @@ extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; // dependency to libswiftCore (which is where the Equatable protocol desciptor // lives), we have to manually implant this before calling any user code. __attribute__((constructor)) -void _emplaceEquatableDescriptor() { +void _emplaceTupleEquatableDescriptor() { auto tupleEquatableConf = const_cast( reinterpret_cast(&_swift_tupleEquatable_conf)); auto equatable = getEquatableDescriptor(); @@ -75,12 +97,6 @@ void _emplaceEquatableDescriptor() { *tupleEquatableConf = intptr_t(equatable) - intptr_t(tupleEquatableConf); } -template -struct _WitnessTable { - const ProtocolConformanceDescriptor *Conformance; - const void *Witnesses[NumWitnesses]; -}; - SWIFT_RUNTIME_EXPORT const _WitnessTable<1> _swift_tupleEquatable_wt = { &_swift_tupleEquatable_conf, @@ -119,10 +135,7 @@ bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, // Grab the specific witness for this element type. auto equatableTable = reinterpret_cast(conformance); auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; - using Fn = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, - SWIFT_CONTEXT const Metadata *, - const Metadata *, const WitnessTable *); - auto equals = reinterpret_cast(equalsWitness); + auto equals = reinterpret_cast(equalsWitness); // Call the equal function. auto result = equals(value1, value2, elt.Type, elt.Type, conformance); @@ -135,3 +148,304 @@ bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, // Otherwise this tuple has value equality with all elements. return true; } + +//===----------------------------------------------------------------------===// +// Tuple Comparable Conformance +//===----------------------------------------------------------------------===// + +#define TUPLE_COMPARABLE_WT SYMBOL("_swift_tupleComparable_wt") + +// Define the conformance descriptor for tuple Comparable. We do this in +// assembly to work around relative reference issues. +__asm( + " .section __DATA,__data\n" + " .globl " TUPLE_COMPARABLE_CONF "\n" + " .p2align 2\n" + TUPLE_COMPARABLE_CONF ":\n" + // This is an indirectable relative reference to the Comparable protocol + // descriptor. However, this is 0 here because the compatibility libraries + // can't have a dependency on libswiftCore (which is where Comparable lives). + " .long 0\n" + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This is a direct relative reference to the witness table defined below. + " .long ((" TUPLE_COMPARABLE_WT ") - (" TUPLE_COMPARABLE_CONF ")) - 8\n" + // 32 are the ConformanceFlags with the type reference bit set to MetadataKind. + " .long 32\n" +); + +extern const ProtocolConformanceDescriptor _swift_tupleComparable_conf; + +// Due to the fact that the compatibility libraries can't have a hard +// dependency to libswiftCore (which is where the Comparable protocol desciptor +// lives), we have to manually implant this before calling any user code. +__attribute__((constructor)) +void _emplaceTupleComparableDescriptor() { + auto tupleComparableConf = const_cast( + reinterpret_cast(&_swift_tupleComparable_conf)); + auto comparable = getComparableDescriptor(); + + // This is an indirectable pointer. + *tupleComparableConf = intptr_t(comparable) - intptr_t(tupleComparableConf); +} + +// The base Equatable protocol is itself a requirement, thus the requirement +// count is 5 (Equatable + 4 operators) and the witness is the tuple Equatable +// table. +SWIFT_RUNTIME_EXPORT +const _WitnessTable<5> _swift_tupleComparable_wt = { + &_swift_tupleComparable_conf, + { + reinterpret_cast(&_swift_tupleEquatable_wt), + reinterpret_cast(_swift_tupleComparable_lessThan), + reinterpret_cast(_swift_tupleComparable_lessThanOrEqual), + reinterpret_cast(_swift_tupleComparable_greaterThanOrEqual), + reinterpret_cast(_swift_tupleComparable_greaterThan) + } +}; + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_lessThan(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable) { + auto tuple = cast(Self); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Ensure we actually have a conformance to Comparable for this element type. + auto comparable = getComparableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, comparable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Comparable...?? + if (!conformance) + swift_unreachable("Tuple comparability requires that all elements \ + be Comparable."); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their less than function + // and return the result. + auto lessThanWitness = comparableTable[1 + WitnessTableFirstRequirementOffset]; + auto lessThan = reinterpret_cast(lessThanWitness); + + // Call the less than function. + auto isLessThan = lessThan(value1, value2, elt.Type, elt.Type, conformance); + + return isLessThan; + } + + // Otherwise these tuples are completely equal, thus they are not less than + // each other. + return false; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_lessThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Ensure we actually have a conformance to Comparable for this element type. + auto comparable = getComparableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, comparable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Comparable...?? + if (!conformance) + swift_unreachable("Tuple comparability requires that all elements \ + be Comparable."); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their less than or equal + // function and return the result. + auto lessThanOrEqualWitness = + comparableTable[WitnessTableFirstRequirementOffset + 2]; + auto lessThanOrEqual = + reinterpret_cast(lessThanOrEqualWitness); + + // Call the less than function. + auto isLessThanOrEqual = lessThanOrEqual(value1, value2, elt.Type, elt.Type, + conformance); + + return isLessThanOrEqual; + } + + // Otherwise these tuples are completely equal. + return true; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_greaterThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Ensure we actually have a conformance to Comparable for this element type. + auto comparable = getComparableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, comparable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Comparable...?? + if (!conformance) + swift_unreachable("Tuple comparability requires that all elements \ + be Comparable."); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their greater than or + // equal function and return the result. + auto greaterThanOrEqualWitness = + comparableTable[WitnessTableFirstRequirementOffset + 3]; + auto greaterThanOrEqual = + reinterpret_cast(greaterThanOrEqualWitness); + + // Call the greater than or equal function. + auto isGreaterThanOrEqual = greaterThanOrEqual(value1, value2, elt.Type, + elt.Type, conformance); + + return isGreaterThanOrEqual; + } + + // Otherwise these tuples are completely equal. + return true; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Ensure we actually have a conformance to Comparable for this element type. + auto comparable = getComparableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, comparable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Comparable...?? + if (!conformance) + swift_unreachable("Tuple comparability requires that all elements \ + be Comparable."); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their greater than + // function and return the result. + auto greaterThanWitness = + comparableTable[WitnessTableFirstRequirementOffset + 4]; + auto greaterThan = + reinterpret_cast(greaterThanWitness); + + // Call the greater than function. + auto isGreaterThan = greaterThan(value1, value2, elt.Type, elt.Type, + conformance); + + return isGreaterThan; + } + + // Otherwise these tuples are completely equal, thus they are not greater than + // each other. + return false; +} diff --git a/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp b/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp index 8520769271543..76f82e7b8af2c 100644 --- a/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp +++ b/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp @@ -32,6 +32,13 @@ static const ProtocolDescriptor *getEquatableDescriptor() { return descriptor; } +static const ProtocolDescriptor *getComparableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSLMp"))); + return descriptor; +} + static const WitnessTable *conformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *); @@ -45,9 +52,10 @@ static bool tupleConformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { auto tuple = cast(type); - // At the moment, tuples can only conform to Equatable, so reject all other - // protocols. - if (protocol != getEquatableDescriptor()) + // At the moment, tuples can only conform to Equatable and Comparable, so + // reject all other protocols. + if (protocol != getEquatableDescriptor() && + protocol != getComparableDescriptor()) return false; for (size_t i = 0; i != tuple->NumElements; i += 1) { @@ -60,12 +68,15 @@ static bool tupleConformsToProtocol(const Metadata *type, } extern const WitnessTable _swift_tupleEquatable_wt; +extern const WitnessTable _swift_tupleComparable_wt; static const WitnessTable *getTupleConformanceWitnessTable( const ProtocolDescriptor *protocol) { - if (protocol == getEquatableDescriptor()) { + if (protocol == getEquatableDescriptor()) return &_swift_tupleEquatable_wt; - } + + if (protocol == getComparableDescriptor()) + return &_swift_tupleComparable_wt; return nullptr; } From 58e643e1b07b215d944cff736eb9202ce9035218 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sun, 19 Jul 2020 21:38:16 -0400 Subject: [PATCH 656/745] [Runtime] Tuple Comparable associated conformance is not evenly aligned --- stdlib/public/runtime/BuiltinProtocolConformances.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp index a136916b4e8d5..e70a0edad3835 100644 --- a/stdlib/public/runtime/BuiltinProtocolConformances.cpp +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -291,9 +291,10 @@ __asm( " .long " COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR "@GOTPCREL + 5\n" #endif // This is a direct relative reference to the associated conformance for - // Equatable defined above in assembly. + // Equatable defined above in assembly. NOTE: This is minus 23 because the + // associated conformance structure is 1 aligned. " .long ((\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - \ - (" TUPLE_COMPARABLE_CONF ")) - 24\n" + (" TUPLE_COMPARABLE_CONF ")) - 23\n" #if defined(__ELF__) // This is an indirectable relative reference to the GOT equivalent for the // Comparable.< method descriptor, hence why we add 1 to indicate indirect. From 4ff28f2b40918b016640a3e933c67a7e49d40ead Mon Sep 17 00:00:00 2001 From: Azoy Date: Sun, 19 Jul 2020 22:07:26 -0400 Subject: [PATCH 657/745] Implement Tuple Hashable Conformance --- .../Runtime/BuiltinProtocolConformances.h | 39 ++++ lib/AST/Module.cpp | 5 +- lib/IRGen/IRGenMangler.cpp | 5 + .../runtime/BuiltinProtocolConformances.cpp | 200 ++++++++++++++++++ stdlib/public/runtime/ProtocolConformance.cpp | 13 +- test/IRGen/builtin_conformances.swift | 26 +++ test/Interpreter/builtin_conformances.swift | 95 ++++++++- test/expr/unary/keypath/keypath.swift | 1 - 8 files changed, 375 insertions(+), 9 deletions(-) diff --git a/include/swift/Runtime/BuiltinProtocolConformances.h b/include/swift/Runtime/BuiltinProtocolConformances.h index 5b2dd9ef3db36..71cf4fef11631 100644 --- a/include/swift/Runtime/BuiltinProtocolConformances.h +++ b/include/swift/Runtime/BuiltinProtocolConformances.h @@ -112,6 +112,45 @@ bool _swift_tupleComparable_greaterThan(OpaqueValue *tuple1, OpaqueValue *tuple2 SWIFT_CONTEXT Metadata *swiftSelf, Metadata *Self, void *witnessTable); +//===----------------------------------------------------------------------===// +// Tuple Hashable Conformance +//===----------------------------------------------------------------------===// + +// public protocol Hashable {} +#define SWIFT_HASHABLE_MANGLING SH + +#define HASHABLE_DESCRIPTOR PROTOCOL_DESCRIPTOR_SYM(SWIFT_HASHABLE_MANGLING) + +#define HASHABLE_DESCRIPTOR_SYMBOL SYMBOL("$sSHMp") + +// Swift._hashValue(for: A) -> Swift.Int +#define SWIFT_HASHVALUE_FUNC $ss10_hashValue3forSix_tSHRzlF +// Swift.Hasher.combine(A) -> () +#define SWIFT_HASHER_COMBINE_FUNC $ss6HasherV7combineyyxSHRzlF + +#define HASHABLE_BASE_CONFORMANCE_DESCRIPTOR SYMBOL("$sSHSQTb") +#define HASHABLE_HASHVALUE_METHOD_DESCRIPTOR SYMBOL("$sSH9hashValueSivgTq") +#define HASHABLE_HASH_METHOD_DESCRIPTOR SYMBOL("$sSH4hash4intoys6HasherVz_tFTq") +#define HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR \ + SYMBOL("$sSH13_rawHashValue4seedS2i_tFTq") + +#define TUPLE_HASHABLE_CONF SYMBOL("_swift_tupleHashable_conf") +#define TUPLE_HASHABLE_HASHVALUE SYMBOL("_swift_tupleHashable_hashValue") +#define TUPLE_HASHABLE_HASH SYMBOL("_swift_tupleHashable_hash") + +/// The protocol witness for Swift.Hashable.hashValue.getter: Swift.Int in +/// conformance (A...): Swift.Hashable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +intptr_t _swift_tupleHashable_hashValue(SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable); + +/// The protocol witness for Swift.Hashable.hash(into:) in conformance +/// (A...): Swift.Hashable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +void _swift_tupleHashable_hash(OpaqueValue *hasher, + SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable); + } // end namespace swift #endif diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index bd7c57e6bebe1..34a97a61f8526 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -1014,12 +1014,13 @@ LookupConformanceInModuleRequest::evaluate( return ProtocolConformanceRef(protocol); // Tuples have builtin conformances implemented within the runtime. - // These conformances so far consist of Equatable and Comparable. + // These conformances so far consist of Equatable, Comparable, and Hashable. if (auto tuple = type->getAs()) { auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable); auto comparable = ctx.getProtocol(KnownProtocolKind::Comparable); + auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable); - if (protocol != equatable && protocol != comparable) + if (protocol != equatable && protocol != comparable && protocol != hashable) return ProtocolConformanceRef::forInvalid(); SmallVector elementConformances; diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index 7eb01dbb90a13..c6bd374d7df0b 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -157,6 +157,7 @@ std::string IRGenMangler::mangleProtocolConformanceDescriptor( if (conformance->getType()->is()) { auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable); auto comparable = ctx.getProtocol(KnownProtocolKind::Comparable); + auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable); if (conformance->getProtocol() == equatable) { return "_swift_tupleEquatable_conf"; @@ -166,6 +167,10 @@ std::string IRGenMangler::mangleProtocolConformanceDescriptor( return "_swift_tupleComparable_conf"; } + if (conformance->getProtocol() == hashable) { + return "_swift_tupleHashable_conf"; + } + llvm_unreachable("mangling conformance descriptor for unknown tuple \ protocol"); } diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp index e70a0edad3835..768db5ae6d086 100644 --- a/stdlib/public/runtime/BuiltinProtocolConformances.cpp +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -608,3 +608,203 @@ bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1, // each other. return false; } + +//===----------------------------------------------------------------------===// +// Tuple Hashable Conformance +//===----------------------------------------------------------------------===// + +#if defined(__ELF__) +// Create a GOT equivalent for the Hashable reference. +__asm( + " .type got." HASHABLE_DESCRIPTOR_SYMBOL ", @object\n" + " .section .data.rel.ro\n" + " .p2align 3\n" + "got." HASHABLE_DESCRIPTOR_SYMBOL ":\n" + " .quad (" HASHABLE_DESCRIPTOR_SYMBOL ")\n" + " .size got." HASHABLE_DESCRIPTOR_SYMBOL ", 8\n" +); + +// Create a GOT equivalent for the Hashable base conformance to Equatable. +__asm( + " .type got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ":\n" + " .quad (" HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ")\n" + " .size got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ", 8\n" +); + +// Create a GOT equivalent for the Hashable.hashValue method descriptor. +__asm( + " .type got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ":\n" + " .quad (" HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ")\n" + " .size got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ", 8\n" +); + +// Create a GOT equivalent for the Hashable.hash(into:) method descriptor. +__asm( + " .type got." HASHABLE_HASH_METHOD_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." HASHABLE_HASH_METHOD_DESCRIPTOR ":\n" + " .quad (" HASHABLE_HASH_METHOD_DESCRIPTOR ")\n" + " .size got." HASHABLE_HASH_METHOD_DESCRIPTOR ", 8\n" +); + +// Create a GOT equivalent for the Hashable._rawHashValue method descriptor. +__asm( + " .type got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ":\n" + " .quad (" HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ")\n" + " .size got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ", 8\n" +); +#endif + +// Define the conformance descriptor for tuple Hashable. We do this in +// assembly to work around relative reference issues. +__asm( + #if defined(__ELF__) + " .type __swift_tupleHashable_private, @object\n" + " .local __swift_tupleHashable_private\n" + " .comm __swift_tupleHashable_private, 128, 16\n" + " .protected " TUPLE_HASHABLE_CONF "\n" + " .type " TUPLE_HASHABLE_CONF ", @object\n" + " .section .rodata\n" + #elif defined(__MACH__) + " .zerofill __DATA, __bss, __swift_tupleHashable_private, 128, 4\n" + " .section __TEXT, __const\n" + #endif + " .globl " TUPLE_HASHABLE_CONF "\n" + " .p2align 2\n" + TUPLE_HASHABLE_CONF ":\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Hashable protocol descriptor, hence why we add 1 to indicate indirect. + " .long (got." HASHABLE_DESCRIPTOR_SYMBOL " - \ + (" TUPLE_HASHABLE_CONF ")) + 1\n" + #elif defined(__MACH__) + " .long " HASHABLE_DESCRIPTOR_SYMBOL "@GOTPCREL + 5\n" + #endif + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This indicates that we have no witness table pattern. We use a generic + // witness table for builtin conformances. + " .long 0\n" + // 196640 are the ConformanceFlags with the type reference bit set to + // MetadataKind, the has resilient witness bit, and the generic witness table + // bit. + " .long 196640\n" + // This 4 is the ResilientWitnessesHeader indicating we have 4 resilient + // witnesses. + " .long 4\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Hashable base conformance for Equatable, hence why we add 1 to indicate + // indirect. + " .long ((got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR " - \ + (" TUPLE_HASHABLE_CONF ")) - 20) + 1\n" + #elif defined(__MACH__) + " .long " HASHABLE_BASE_CONFORMANCE_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the associated conformance for + // Equatable defined above in assembly. NOTE: We intentionally use the + // Comparable implementation for this because the implementation is the same + // for both Hashable and Comparable. Both want to grab the Equatable table + // from its elements whose witness table is located in the same place for both + // protocols. NOTE: This is minus 23 because the associated conformance + // structure is 1 aligned. + " .long ((\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - \ + (" TUPLE_HASHABLE_CONF ")) - 23\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Hashable.hashValue method descriptor, hence why we add 1 to indicate + // indirect. + " .long ((got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR " - \ + (" TUPLE_HASHABLE_CONF ")) - 28) + 1\n" + #elif defined(__MACH__) + " .long " HASHABLE_HASHVALUE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the hashValue witness defined below. + " .long ((" TUPLE_HASHABLE_HASHVALUE ") - (" TUPLE_HASHABLE_CONF ")) - 32\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Hashable.hash(into:) method descriptor, hence why we add 1 to indicate + // indirect. + " .long ((got." HASHABLE_HASH_METHOD_DESCRIPTOR " - \ + (" TUPLE_HASHABLE_CONF ")) - 36) + 1\n" + #elif defined(__MACH__) + " .long " HASHABLE_HASH_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the hash(into:) witness defined below. + " .long ((" TUPLE_HASHABLE_HASH ") - (" TUPLE_HASHABLE_CONF ")) - 40\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Hashable._rawHashValue method descriptor, hence why we add 1 to indicate + // indirect. + " .long ((got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR " - \ + (" TUPLE_HASHABLE_CONF ")) - 44) + 1\n" + #elif defined(__MACH__) + " .long " HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This 0 indicates that we are requesting the default implementation for the + // _rawHashValue getter. + " .long 0\n" + // The witness table size in words. + " .short 0\n" + // The witness table private size in words & requires instantiation. + " .short 1\n" + // The witness table instantiator function. + " .long 0\n" + // This is a direct relative reference to the private data for the + // conformance. + " .long (__swift_tupleHashable_private - (" TUPLE_HASHABLE_CONF ")) - 60\n" + #if defined(__ELF__) + " .size " TUPLE_HASHABLE_CONF ", 64\n" + #endif +); + +// These are all function values that we reinterpret later. +extern void *SWIFT_HASHVALUE_FUNC; +extern void *SWIFT_HASHER_COMBINE_FUNC; + +using HashValueFn = SWIFT_CC(swift) intptr_t(OpaqueValue *value, Metadata *Self, + void *witnessTable); +using HasherCombineFn = SWIFT_CC(swift) void(OpaqueValue *value, + const Metadata *Self, + const WitnessTable *witnessTable, + SWIFT_CONTEXT OpaqueValue *hasher); + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +intptr_t _swift_tupleHashable_hashValue(SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable) { + auto _hashValue = reinterpret_cast(&SWIFT_HASHVALUE_FUNC); + return _hashValue(tuple, Self, witnessTable); +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +void _swift_tupleHashable_hash(OpaqueValue *hasher, + SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable) { + auto tupleTy = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements and hash them into the Hasher. + for (size_t i = 0; i != tupleTy->NumElements; i += 1) { + auto elt = tupleTy->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the element value from the tuple. + auto value = reinterpret_cast( + reinterpret_cast(tuple) + elt.Offset); + + auto hasherCombine = + reinterpret_cast(&SWIFT_HASHER_COMBINE_FUNC); + + // Call the combine function on the hasher for this element value and we're + // done! + hasherCombine(value, elt.Type, conformance, hasher); + } +} diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index 6f281e0178156..5987dc9a91c07 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -378,16 +378,18 @@ searchInConformanceCache(const Metadata *type, extern const ProtocolDescriptor EQUATABLE_DESCRIPTOR; extern const ProtocolDescriptor COMPARABLE_DESCRIPTOR; +extern const ProtocolDescriptor HASHABLE_DESCRIPTOR; static bool tupleConformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { auto tuple = cast(type); - // At the moment, tuples can only conform to Equatable and Comparable, so - // reject all other protocols. + // At the moment, tuples can only conform to Equatable, Comparable and + // Hashable, so reject all other protocols. auto equatable = &EQUATABLE_DESCRIPTOR; auto comparable = &COMPARABLE_DESCRIPTOR; - if (protocol != equatable && protocol != comparable) + auto hashable = &HASHABLE_DESCRIPTOR; + if (protocol != equatable && protocol != comparable && protocol != hashable) return false; for (size_t i = 0; i != tuple->NumElements; i += 1) { @@ -401,6 +403,7 @@ static bool tupleConformsToProtocol(const Metadata *type, extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; extern const ProtocolConformanceDescriptor _swift_tupleComparable_conf; +extern const ProtocolConformanceDescriptor _swift_tupleHashable_conf; static const ProtocolConformanceDescriptor *getTupleConformanceDescriptor( const ProtocolDescriptor *protocol) { @@ -412,6 +415,10 @@ static const ProtocolConformanceDescriptor *getTupleConformanceDescriptor( return &_swift_tupleComparable_conf; } + if (protocol == &HASHABLE_DESCRIPTOR) { + return &_swift_tupleHashable_conf; + } + return nullptr; } diff --git a/test/IRGen/builtin_conformances.swift b/test/IRGen/builtin_conformances.swift index 2053d69457881..44c549c442fb1 100644 --- a/test/IRGen/builtin_conformances.swift +++ b/test/IRGen/builtin_conformances.swift @@ -2,6 +2,7 @@ // CHECK-LABEL: @_swift_tupleEquatable_conf = external global %swift.protocol_conformance_descriptor // CHECK-LABEL: @_swift_tupleComparable_conf = external global %swift.protocol_conformance_descriptor +// CHECK-LABEL: @_swift_tupleHashable_conf = external global %swift.protocol_conformance_descriptor struct Wrapper { let value: T @@ -60,3 +61,28 @@ public func testTupleComparable() { // CHECK: {{%.*}} = call swiftcc i1 {{.*}}({{%.*}}.1* noalias nocapture undef, {{%.*}}.1* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_COMPARABLE_WT1]]) let _ = Wrapper(value: ()) < Wrapper(value: ()) } + +//===----------------------------------------------------------------------===// +// Tuple Hashable conformance +//===----------------------------------------------------------------------===// + +extension Wrapper: Hashable where T: Hashable {} + +public func hashValue(for instance: T) -> Int { + instance.hashValue +} + +public func useHashable(_ thing: T) -> Int { + // CHECK: [[USE_HASHABLE_WT:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleHashable_conf, %swift.type* {{%.*}}, i8*** {{%.*}}) + // CHECK-NEXT: {{%.*}} = call swiftcc i64 {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_HASHABLE_WT]]) + hashValue(for: (thing, thing)) +} + +public func testTupleHashable() { + // CHECK: [[TEST_TUPLE_HASHABLE_WT1:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleHashable_conf, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8*** undef) + // CHECK: {{%.*}} = call swiftcc i64 {{.*}}(%swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_HASHABLE_WT1]]) + let _ = hashValue(for: ()) + + // CHECK: {{%.*}} = call swiftcc i64 {{.*}}(%swift.type* {{%.*}}, i8** [[TEST_TUPLE_HASHABLE_WT1]], {{%.*}} noalias nocapture swiftself undef) + let _ = Wrapper(value: ()).hashValue +} diff --git a/test/Interpreter/builtin_conformances.swift b/test/Interpreter/builtin_conformances.swift index 182cb85904608..be926737f9615 100644 --- a/test/Interpreter/builtin_conformances.swift +++ b/test/Interpreter/builtin_conformances.swift @@ -6,6 +6,7 @@ struct Wrapper { } extension Wrapper: Equatable where T: Equatable {} +extension Wrapper: Hashable where T: Hashable {} extension Wrapper: Comparable where T: Comparable { static func <(lhs: Wrapper, rhs: Wrapper) -> Bool { @@ -33,6 +34,12 @@ extension Foo: Comparable { } } +extension Foo: Hashable { + func hash(into hasher: inout Hasher) { + hasher.combine(age) + } +} + //===----------------------------------------------------------------------===// // Tuple Equatable Conformance //===----------------------------------------------------------------------===// @@ -125,7 +132,6 @@ print(compareGTE((1, 2), (1, 2))) // false print(compareGT((1, 2), (1, 2))) - // true print(compareLT((1, 2), (2, 1))) // true @@ -135,7 +141,6 @@ print(compareGTE((1, 2), (2, 1))) // false print(compareGT((1, 2), (2, 1))) - // false print(compareLT(((1, 2), 3), ((1, 2), 3))) // true @@ -145,7 +150,6 @@ print(compareGTE(((1, 2), 3), ((1, 2), 3))) // false print(compareGT(((1, 2), 3), ((1, 2), 3))) - // true print(compareLT(((1, 2), 3), ((1, 2), 4))) // true @@ -218,3 +222,88 @@ print(compareLTE((Foo(age: 128), 0), (Foo(age: 734), 0))) print(compareGTE((Foo(age: 128), 0), (Foo(age: 734), 0))) // CHECK: false print(compareGT((Foo(age: 128), 0), (Foo(age: 734), 0))) + +//===----------------------------------------------------------------------===// +// Tuple Hashable Conformance +//===----------------------------------------------------------------------===// + +var grid = [(x: Int, y: Int): Int]() + +grid[(x: 0, y: 0)] = 0 +// CHECK: 0 +print(grid[(x: 0, y: 0)]!) +// CHECK: 0 +print(grid[(0, 0)]!) + +grid[(x: 1, y: 1)] = 1 +// CHECK: 1 +print(grid[(x: 1, y: 1)]!) +// CHECK: 1 +print(grid[(1, 1)]!) + +let tupleSet: Set = [(x: 0, y: 1), (x: 128, y: 32), (x: 10, y: 0)] + +// CHECK: true +print(tupleSet.contains((x: 0, y: 1))) + +// CHECK: true +print(tupleSet.contains((0, 1))) + +func compareHashes(_ lhs: T, _ rhs: T) -> Bool { + lhs.hashValue == rhs.hashValue +} + +// CHECK: true +print(compareHashes((), ())) + +// CHECK: true +print(compareHashes((1, 2), (1, 2))) + +// CHECK: false +print(compareHashes((1, 2), (1, 3))) + +// CHECK: false +print(compareHashes((1, 2), (2, 1))) + +// CHECK: true +print(compareHashes(((1, 2), 3), ((1, 2), 3))) + +// CHECK: false +print(compareHashes(((1, 2), 3), ((1, 2), 4))) + +// CHECK: false +print(compareHashes(((1, 2), 3), ((3, 2), 1))) + +@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) +func opaqueTupleHashableValue() -> some Hashable { + (1, 2, 3, 4, 5) +} + +if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) { + _ = opaqueTupleHashableValue().hashValue == opaqueTupleHashableValue().hashValue +} + +// CHECK: true +print(compareHashes(Wrapper(value: ()), Wrapper(value: ()))) + +// CHECK: true +print(compareHashes(Wrapper(value: (1, 2)), Wrapper(value: (1, 2)))) + +// CHECK: false +print(compareHashes(Wrapper(value: (1, 2)), Wrapper(value: (1, 3)))) + +// CHECK: false +print(compareHashes(Wrapper(value: (1, 2)), Wrapper(value: (2, 1)))) + +func useHashable(_ thing: T) -> Bool { + compareHashes((thing, thing), (thing, thing)) +} + +// CHECK: true +print(useHashable(128)) + +// CHECK: true +print(compareHashes((Foo(age: 128), 1), (Foo(age: 128), 1))) + +// CHECK: false +print(compareHashes((Foo(age: 128), 1), (Foo(age: 0), 1))) diff --git a/test/expr/unary/keypath/keypath.swift b/test/expr/unary/keypath/keypath.swift index 08282b4cd1bf2..812b489f47d25 100644 --- a/test/expr/unary/keypath/keypath.swift +++ b/test/expr/unary/keypath/keypath.swift @@ -873,7 +873,6 @@ func sr11562() { } _ = \S4.[1, 4] // expected-error {{subscript expects a single parameter of type '(Int, Int)'}} {{12-12=(}} {{16-16=)}} - // expected-error@-1 {{subscript index of type '(Int, Int)' in a key path must be Hashable}} } // SR-12290: Ban keypaths with contextual root and without a leading dot From 5e03e333cb9478dbc0d586524e0ddf9107f2dd52 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sun, 19 Jul 2020 22:41:29 -0400 Subject: [PATCH 658/745] [Compatibility53] Backport Tuple Hashable Conformance --- .../BuiltinProtocolConformances.cpp | 134 ++++++++++++++++++ .../Compatibility53/ProtocolConformance.cpp | 18 ++- 2 files changed, 149 insertions(+), 3 deletions(-) diff --git a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp index c3893c8b668b5..322b0b18993c2 100644 --- a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp +++ b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp @@ -37,6 +37,13 @@ static const ProtocolDescriptor *getComparableDescriptor() { return descriptor; } +static const ProtocolDescriptor *getHashableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSHMp"))); + return descriptor; +} + static const WitnessTable *conformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *); @@ -449,3 +456,130 @@ bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1, // each other. return false; } + +//===----------------------------------------------------------------------===// +// Tuple Hashable Conformance +//===----------------------------------------------------------------------===// + +#define TUPLE_HASHABLE_WT SYMBOL("_swift_tupleHashable_wt") + +// Define the conformance descriptor for tuple Hashable. We do this in +// assembly to work around relative reference issues. +__asm( + " .section __DATA,__data\n" + " .globl " TUPLE_HASHABLE_CONF "\n" + " .p2align 2\n" + TUPLE_HASHABLE_CONF ":\n" + // This is an indirectable relative reference to the Hashable protocol + // descriptor. However, this is 0 here because the compatibility libraries + // can't have a dependency on libswiftCore (which is where Hashable lives). + " .long 0\n" + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This is a direct relative reference to the witness table defined below. + " .long ((" TUPLE_HASHABLE_WT ") - (" TUPLE_HASHABLE_CONF ")) - 8\n" + // 32 are the ConformanceFlags with the type reference bit set to MetadataKind. + " .long 32\n" +); + +extern const ProtocolConformanceDescriptor _swift_tupleHashable_conf; + +// Due to the fact that the compatibility libraries can't have a hard +// dependency to libswiftCore (which is where the Hashable protocol desciptor +// lives), we have to manually implant this before calling any user code. +__attribute__((constructor)) +void _emplaceTupleHashableDescriptor() { + auto tupleHashableConf = const_cast( + reinterpret_cast(&_swift_tupleHashable_conf)); + auto hashable = getHashableDescriptor(); + + // This is an indirectable pointer. + *tupleHashableConf = intptr_t(hashable) - intptr_t(tupleHashableConf); +} + +// The base Equatable protocol is itself a requirement, thus the requirement +// count is 4 (Equatable + hashValue + hash(into:) + _rawHashValue) and the +// witness is the tuple Equatable table. +SWIFT_RUNTIME_EXPORT +_WitnessTable<4> _swift_tupleHashable_wt = { + &_swift_tupleHashable_conf, + { + reinterpret_cast(&_swift_tupleEquatable_wt), + reinterpret_cast(_swift_tupleHashable_hashValue), + reinterpret_cast(_swift_tupleHashable_hash), + nullptr + } +}; + +static void *get_rawHashValueDefaultImplFunc() { + auto impl = SWIFT_LAZY_CONSTANT( + dlsym(RTLD_DEFAULT, "$sSHsE13_rawHashValue4seedS2i_tF")); + return impl; +} + +// Due to the fact that the compatibility libraries can't have a hard +// dependency to libswiftCore (which is where the _rawHashValue default impl +// lives), we have to manually implant this before calling any user code. +__attribute__((constructor)) +void _emplaceTupleHashable_rawHashValueDefaultImpl() { + _swift_tupleHashable_wt.Witnesses[3] = get_rawHashValueDefaultImplFunc(); +} + +using HashValueFn = SWIFT_CC(swift) intptr_t(OpaqueValue *value, Metadata *Self, + void *witnessTable); +using HasherCombineFn = SWIFT_CC(swift) void(OpaqueValue *value, + const Metadata *Self, + const WitnessTable *witnessTable, + SWIFT_CONTEXT OpaqueValue *hasher); + +static HashValueFn *get_hashValueFunc() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, STR(SWIFT_HASHVALUE_FUNC)))); + return descriptor; +} + +static HasherCombineFn *getHashCombineFunc() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, STR(SWIFT_HASHER_COMBINE_FUNC)))); + return descriptor; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +intptr_t swift::_swift_tupleHashable_hashValue(SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable) { + auto _hashValue = get_hashValueFunc(); + return _hashValue(tuple, Self, witnessTable); +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +void swift::_swift_tupleHashable_hash(OpaqueValue *hasher, + SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable) { + auto tupleTy = cast(Self); + + // Loop through all elements and hash them into the Hasher. + for (size_t i = 0; i != tupleTy->NumElements; i += 1) { + auto elt = tupleTy->getElement(i); + + // Ensure we actually have a conformance to Hashable for this element type. + auto hashable = getHashableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, hashable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Hashable...?? + if (!conformance) + swift_unreachable("Tuple hasing requires that all elements be Hashable."); + + // Get the element value from the tuple. + auto value = reinterpret_cast( + reinterpret_cast(tuple) + elt.Offset); + + auto hasherCombine = getHashCombineFunc(); + + // Call the combine function on the hasher for this element value and we're + // done! + hasherCombine(value, elt.Type, conformance, hasher); + } +} diff --git a/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp b/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp index 76f82e7b8af2c..71b90ab19152a 100644 --- a/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp +++ b/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp @@ -39,6 +39,13 @@ static const ProtocolDescriptor *getComparableDescriptor() { return descriptor; } +static const ProtocolDescriptor *getHashableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSHMp"))); + return descriptor; +} + static const WitnessTable *conformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *); @@ -52,10 +59,11 @@ static bool tupleConformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { auto tuple = cast(type); - // At the moment, tuples can only conform to Equatable and Comparable, so - // reject all other protocols. + // At the moment, tuples can only conform to Equatable, Comparable, and + // Hashable, so reject all other protocols. if (protocol != getEquatableDescriptor() && - protocol != getComparableDescriptor()) + protocol != getComparableDescriptor() && + protocol != getHashableDescriptor()) return false; for (size_t i = 0; i != tuple->NumElements; i += 1) { @@ -69,6 +77,7 @@ static bool tupleConformsToProtocol(const Metadata *type, extern const WitnessTable _swift_tupleEquatable_wt; extern const WitnessTable _swift_tupleComparable_wt; +extern const WitnessTable _swift_tupleHashable_wt; static const WitnessTable *getTupleConformanceWitnessTable( const ProtocolDescriptor *protocol) { @@ -78,6 +87,9 @@ static const WitnessTable *getTupleConformanceWitnessTable( if (protocol == getComparableDescriptor()) return &_swift_tupleComparable_wt; + if (protocol == getHashableDescriptor()) + return &_swift_tupleHashable_wt; + return nullptr; } From 746837548b13c762af6607cc8aba561d3ef62d72 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sat, 25 Jul 2020 18:15:44 -0400 Subject: [PATCH 659/745] [ABI] Split TypeReferenceKind from TypeMetadataRecord --- include/swift/ABI/Metadata.h | 20 +++++--------------- include/swift/ABI/MetadataValues.h | 14 +++++++++++++- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 4bfcc243aebe8..4d33e60f7fd73 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -2295,38 +2295,28 @@ struct TargetTypeMetadataRecord { union { /// A direct reference to a nominal type descriptor. RelativeDirectPointerIntPair, - TypeReferenceKind> + TypeMetadataRecordKind> DirectNominalTypeDescriptor; /// An indirect reference to a nominal type descriptor. RelativeDirectPointerIntPair * __ptrauth_swift_type_descriptor>, - TypeReferenceKind> + TypeMetadataRecordKind> IndirectNominalTypeDescriptor; - - // We only allow a subset of the TypeReferenceKinds here. - // Should we just acknowledge that this is a different enum? }; public: - TypeReferenceKind getTypeKind() const { + TypeMetadataRecordKind getTypeKind() const { return DirectNominalTypeDescriptor.getInt(); } const TargetContextDescriptor * getContextDescriptor() const { switch (getTypeKind()) { - case TypeReferenceKind::DirectTypeDescriptor: + case TypeMetadataRecordKind::DirectTypeDescriptor: return DirectNominalTypeDescriptor.getPointer(); - case TypeReferenceKind::IndirectTypeDescriptor: + case TypeMetadataRecordKind::IndirectTypeDescriptor: return *IndirectNominalTypeDescriptor.getPointer(); - - // These types (and any others we might add to TypeReferenceKind - // in the future) are just never used in these lists. - case TypeReferenceKind::DirectObjCClassName: - case TypeReferenceKind::IndirectObjCClass: - case TypeReferenceKind::MetadataKind: - return nullptr; } return nullptr; diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index 00adcbdba61d4..fc564085c57a1 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -361,7 +361,19 @@ enum : unsigned { NumGenericMetadataPrivateDataWords = 16, }; -/// Kinds of type metadata/protocol conformance records. +/// Kinds of type metadata reocrds. +enum class TypeMetadataRecordKind : unsigned { + /// A direct reference to a nominal type descriptor. + DirectTypeDescriptor = 0x00, + + /// An indirect reference to a nominal type descriptor. + IndirectTypeDescriptor = 0x01, + + First_Kind = DirectTypeDescriptor, + Last_Kind = IndirectTypeDescriptor, +}; + +/// Kinds of references to type metadata. enum class TypeReferenceKind : unsigned { /// The conformance is for a nominal type referenced directly; /// getTypeDescriptor() points to the type context descriptor. From 7a6ea99eab60e7a4e787cd0e3daae3ac2e255a26 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sat, 25 Jul 2020 18:17:56 -0400 Subject: [PATCH 660/745] [Runtime] Clean up the assembly around Tuple builtin conformances forgot + 1 --- include/swift/AST/ProtocolConformance.h | 12 +- .../runtime/BuiltinProtocolConformances.cpp | 322 ++++-------------- stdlib/public/runtime/Metadata.cpp | 4 +- .../BuiltinProtocolConformances.cpp | 10 +- 4 files changed, 79 insertions(+), 269 deletions(-) diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index 31fda612c717f..9a0b6a6b2ad50 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -1085,7 +1085,8 @@ class BuiltinProtocolConformance final : public RootProtocolConformance, } bool hasTypeWitness(AssociatedTypeDecl *assocType) const { - llvm_unreachable("builtin-conformances never have associated types"); + llvm_unreachable("builtin-conformances currently don't have associated \ + types"); } /// Retrieve the type witness and type decl (if one exists) @@ -1093,7 +1094,8 @@ class BuiltinProtocolConformance final : public RootProtocolConformance, TypeWitnessAndDecl getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, SubstOptions options=None) const { - llvm_unreachable("builtin-conformances never have associated types"); + llvm_unreachable("builtin-conformances currently don't have associated \ + types"); } /// Given that the requirement signature of the protocol directly states @@ -1101,7 +1103,8 @@ class BuiltinProtocolConformance final : public RootProtocolConformance, /// return its associated conformance. ProtocolConformanceRef getAssociatedConformance(Type assocType, ProtocolDecl *protocol) const { - llvm_unreachable("builtin-conformances never have associated types"); + llvm_unreachable("builtin-conformances currently don't have associated \ + types"); } /// Retrieve the witness corresponding to the given value requirement. @@ -1112,7 +1115,8 @@ class BuiltinProtocolConformance final : public RootProtocolConformance, /// Determine whether the witness for the given requirement /// is either the default definition or was otherwise deduced. bool usesDefaultDefinition(AssociatedTypeDecl *requirement) const { - llvm_unreachable("builtin-conformances never have associated types"); + llvm_unreachable("builtin-conformances currently don't have associated \ + types"); } static bool classof(const ProtocolConformance *conformance) { diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp index 768db5ae6d086..90d54b74d3362 100644 --- a/stdlib/public/runtime/BuiltinProtocolConformances.cpp +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -28,31 +28,20 @@ using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, const Metadata *, const WitnessTable *); +#if defined(__ELF__) +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOTPCREL + 1" +#elif defined(__MACH__) +#if defined(__aarch64__) +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOT - . + 1" +#else +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOTPCREL + 5" +#endif +#endif + //===----------------------------------------------------------------------===// // Tuple Equatable Conformance //===----------------------------------------------------------------------===// -#if defined(__ELF__) -// Create a GOT equivalent for the Equatable reference. -__asm( - " .type got." EQUATABLE_DESCRIPTOR_SYMBOL ", @object\n" - " .section .data.rel.ro\n" - " .p2align 3\n" - "got." EQUATABLE_DESCRIPTOR_SYMBOL ":\n" - " .quad (" EQUATABLE_DESCRIPTOR_SYMBOL ")\n" - " .size got." EQUATABLE_DESCRIPTOR_SYMBOL ", 8\n" -); - -// Create a GOT equivalent for the Equatable.== method descriptor. -__asm( - " .type got." EQUATABLE_EE_METHOD_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." EQUATABLE_EE_METHOD_DESCRIPTOR ":\n" - " .quad (" EQUATABLE_EE_METHOD_DESCRIPTOR ")\n" - " .size got." EQUATABLE_EE_METHOD_DESCRIPTOR ", 8\n" -); -#endif - // Define the conformance descriptor for tuple Equatable. We do this in // assembly to work around relative reference issues. __asm( @@ -70,14 +59,9 @@ __asm( " .globl " TUPLE_EQUATABLE_CONF "\n" " .p2align 2\n" TUPLE_EQUATABLE_CONF ":\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Equatable protocol descriptor, hence why we add 1 to indicate indirect. - " .long (got." EQUATABLE_DESCRIPTOR_SYMBOL " - \ - (" TUPLE_EQUATABLE_CONF ")) + 1\n" - #elif defined(__MACH__) - " .long " EQUATABLE_DESCRIPTOR_SYMBOL "@GOTPCREL + 5\n" - #endif + // This is an indirectable relative reference to the GOT entry for the + // Equatable protocol descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(EQUATABLE_DESCRIPTOR_SYMBOL) "\n" // 769 is the MetadataKind::Tuple " .long 769\n" // This indicates that we have no witness table pattern. We use a generic @@ -90,16 +74,11 @@ __asm( // This 1 is the ResilientWitnessesHeader indicating we have 1 resilient // witness. " .long 1\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Equatable.== method descriptor, hence why we add 1 to indicate indirect. - " .long ((got." EQUATABLE_EE_METHOD_DESCRIPTOR " - \ - (" TUPLE_EQUATABLE_CONF ")) - 20) + 1\n" - #elif defined(__MACH__) - " .long " EQUATABLE_EE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + // This is an indirectable relative reference to the GOT entry for the + // Equatable == method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(EQUATABLE_EE_METHOD_DESCRIPTOR) "\n" // This is a direct relative reference to the equals witness defined below. - " .long ((" TUPLE_EQUATABLE_EQUALS ") - (" TUPLE_EQUATABLE_CONF ")) - 24\n" + " .long (" TUPLE_EQUATABLE_EQUALS ") - .\n" // The witness table size in words. " .short 0\n" // The witness table private size in words & requires instantiation. @@ -108,7 +87,7 @@ __asm( " .long 0\n" // This is a direct relative reference to the private data for the // conformance. - " .long (__swift_tupleEquatable_private - (" TUPLE_EQUATABLE_CONF ")) - 36\n" + " .long __swift_tupleEquatable_private - .\n" #if defined(__ELF__) " .size " TUPLE_EQUATABLE_CONF ", 40\n" #endif @@ -158,63 +137,6 @@ bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, // Tuple Comparable Conformance //===----------------------------------------------------------------------===// -#if defined(__ELF__) -// Create a GOT equivalent for the Comparable reference. -__asm( - " .type got." COMPARABLE_DESCRIPTOR_SYMBOL ", @object\n" - " .section .data.rel.ro\n" - " .p2align 3\n" - "got." COMPARABLE_DESCRIPTOR_SYMBOL ":\n" - " .quad (" COMPARABLE_DESCRIPTOR_SYMBOL ")\n" - " .size got." COMPARABLE_DESCRIPTOR_SYMBOL ", 8\n" -); - -// Create a GOT equivalent for the Comparable base conformance to Equatable. -__asm( - " .type got." COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR ":\n" - " .quad (" COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR ")\n" - " .size got." COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR ", 8\n" -); - -// Create a GOT equivalent for the Comparable.< method descriptor. -__asm( - " .type got." COMPARABLE_LT_METHOD_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." COMPARABLE_LT_METHOD_DESCRIPTOR ":\n" - " .quad (" COMPARABLE_LT_METHOD_DESCRIPTOR ")\n" - " .size got." COMPARABLE_LT_METHOD_DESCRIPTOR ", 8\n" -); - -// Create a GOT equivalent for the Comparable.<= method descriptor. -__asm( - " .type got." COMPARBALE_LTE_METHOD_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." COMPARBALE_LTE_METHOD_DESCRIPTOR ":\n" - " .quad (" COMPARBALE_LTE_METHOD_DESCRIPTOR ")\n" - " .size got." COMPARBALE_LTE_METHOD_DESCRIPTOR ", 8\n" -); - -// Create a GOT equivalent for the Comparable.>= method descriptor. -__asm( - " .type got." COMPARABLE_GTE_METHOD_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." COMPARABLE_GTE_METHOD_DESCRIPTOR ":\n" - " .quad (" COMPARABLE_GTE_METHOD_DESCRIPTOR ")\n" - " .size got." COMPARABLE_GTE_METHOD_DESCRIPTOR ", 8\n" -); - -// Create a GOT equivalent for the Comparable.> method descriptor. -__asm( - " .type got." COMPARABLE_GT_METHOD_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." COMPARABLE_GT_METHOD_DESCRIPTOR ":\n" - " .quad (" COMPARABLE_GT_METHOD_DESCRIPTOR ")\n" - " .size got." COMPARABLE_GT_METHOD_DESCRIPTOR ", 8\n" -); -#endif - // Define the associated conformance structure for tuple Comparable. We do this // in assembly to work around relative reference issues. __asm( @@ -235,8 +157,7 @@ __asm( " .byte 7\n" // This is a direct relative reference to the base accessor for Equatable // defined below. - " .long ((" TUPLE_COMPARABLE_BASEACCESSOREQUATABLE ") - \ - (\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\")) - 2\n" + " .long (" TUPLE_COMPARABLE_BASEACCESSOREQUATABLE ") - .\n" // This 0 is our null terminator. " .byte 0\n" #if defined (__ELF__) @@ -261,14 +182,9 @@ __asm( " .globl " TUPLE_COMPARABLE_CONF "\n" " .p2align 2\n" TUPLE_COMPARABLE_CONF ":\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Comparable protocol descriptor, hence why we add 1 to indicate indirect. - " .long (got." COMPARABLE_DESCRIPTOR_SYMBOL " - \ - (" TUPLE_COMPARABLE_CONF ")) + 1\n" - #elif defined(__MACH__) - " .long " COMPARABLE_DESCRIPTOR_SYMBOL "@GOTPCREL + 5\n" - #endif + // This is an indirectable relative reference to the GOT entry for the + // Comparable protocol descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARABLE_DESCRIPTOR_SYMBOL) "\n" // 769 is the MetadataKind::Tuple " .long 769\n" // This indicates that we have no witness table pattern. We use a generic @@ -281,69 +197,36 @@ __asm( // This 5 is the ResilientWitnessesHeader indicating we have 5 resilient // witnesses. " .long 5\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Comparable base conformance for Equatable, hence why we add 1 to indicate - // indirect. - " .long ((got." COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR " - \ - (" TUPLE_COMPARABLE_CONF ")) - 20) + 1\n" - #elif defined(__MACH__) - " .long " COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + // This is an indirectable relative reference to the GOT entry for the + // Comparable base conformance for Equatable. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR) "\n" // This is a direct relative reference to the associated conformance for - // Equatable defined above in assembly. NOTE: This is minus 23 because the + // Equatable defined above in assembly. NOTE: We + 1 here because the // associated conformance structure is 1 aligned. - " .long ((\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - \ - (" TUPLE_COMPARABLE_CONF ")) - 23\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Comparable.< method descriptor, hence why we add 1 to indicate indirect. - " .long ((got." COMPARABLE_LT_METHOD_DESCRIPTOR " - \ - (" TUPLE_COMPARABLE_CONF ")) - 28) + 1\n" - #elif defined(__MACH__) - " .long " COMPARABLE_LT_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + " .long (\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - . + 1\n" + // This is an indirectable relative reference to the GOT entry for the + // Comparable.< method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARABLE_LT_METHOD_DESCRIPTOR) "\n" // This is a direct relative reference to the less than witness defined below. - " .long ((" TUPLE_COMPARABLE_LESSTHAN ") - (" TUPLE_COMPARABLE_CONF ")) - 32\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Comparable.<= method descriptor, hence why we add 1 to indicate - // indirect. - " .long ((got." COMPARBALE_LTE_METHOD_DESCRIPTOR " - \ - (" TUPLE_COMPARABLE_CONF ")) - 36) + 1\n" - #elif defined(__MACH__) - " .long " COMPARBALE_LTE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + " .long (" TUPLE_COMPARABLE_LESSTHAN ") - .\n" + // This is an indirectable relative reference to the GOT entry for the + // Comparable.<= method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARBALE_LTE_METHOD_DESCRIPTOR) "\n" // This is a direct relative reference to the less than or equal witness // defined below. - " .long ((" TUPLE_COMPARABLE_LESSTHANOREQUAL ") - \ - (" TUPLE_COMPARABLE_CONF ")) - 40\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Comparable.>= method descriptor, hence why we add 1 to indicate - // indirect. - " .long ((got." COMPARABLE_GTE_METHOD_DESCRIPTOR " - \ - (" TUPLE_COMPARABLE_CONF ")) - 44) + 1\n" - #elif defined(__MACH__) - " .long " COMPARABLE_GTE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + " .long (" TUPLE_COMPARABLE_LESSTHANOREQUAL ") - .\n" + // This is an indirectable relative reference to the GOT entry for the + // Comparable.>= method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARABLE_GTE_METHOD_DESCRIPTOR) "\n" // This is a direct relative reference to the greater than or equal witness // defined below. - " .long ((" TUPLE_COMPARABLE_GREATERTHANOREQUAL ") - \ - (" TUPLE_COMPARABLE_CONF ")) - 48\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Comparable.> method descriptor, hence why we add 1 to indicate - // indirect. - " .long ((got." COMPARABLE_GT_METHOD_DESCRIPTOR " - \ - (" TUPLE_COMPARABLE_CONF ")) - 52) + 1\n" - #elif defined(__MACH__) - " .long " COMPARABLE_GT_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + " .long (" TUPLE_COMPARABLE_GREATERTHANOREQUAL ") - .\n" + // This is an indirectable relative reference to the GOT entry for the + // Comparable.> method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARABLE_GT_METHOD_DESCRIPTOR) "\n" // This is a direct relative reference to the greater than witness defined // below. - " .long ((" TUPLE_COMPARABLE_GREATERTHAN ") - \ - (" TUPLE_COMPARABLE_CONF ")) - 56\n" + " .long (" TUPLE_COMPARABLE_GREATERTHAN ") - .\n" // The witness table size in words. " .short 0\n" // The witness table private size in words & requires instantiation. @@ -352,7 +235,7 @@ __asm( " .long 0\n" // This is a direct relative reference to the private data for the // conformance. - " .long (__swift_tupleComparable_private - (" TUPLE_COMPARABLE_CONF ")) - 68\n" + " .long __swift_tupleComparable_private - .\n" #if defined(__ELF__) " .size " TUPLE_COMPARABLE_CONF ", 72\n" #endif @@ -365,6 +248,7 @@ _swift_tupleComparable_baseAccessorEquatable(Metadata *assocType, void **witnessTable) { auto tuple = cast(assocType); std::vector instantiationArgs; + instantiationArgs.reserve(tuple->NumElements); // Fill the instantiationArgs with the element Equatable tables. for (size_t i = 0; i != tuple->NumElements; i += 1) { @@ -613,54 +497,6 @@ bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1, // Tuple Hashable Conformance //===----------------------------------------------------------------------===// -#if defined(__ELF__) -// Create a GOT equivalent for the Hashable reference. -__asm( - " .type got." HASHABLE_DESCRIPTOR_SYMBOL ", @object\n" - " .section .data.rel.ro\n" - " .p2align 3\n" - "got." HASHABLE_DESCRIPTOR_SYMBOL ":\n" - " .quad (" HASHABLE_DESCRIPTOR_SYMBOL ")\n" - " .size got." HASHABLE_DESCRIPTOR_SYMBOL ", 8\n" -); - -// Create a GOT equivalent for the Hashable base conformance to Equatable. -__asm( - " .type got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ":\n" - " .quad (" HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ")\n" - " .size got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ", 8\n" -); - -// Create a GOT equivalent for the Hashable.hashValue method descriptor. -__asm( - " .type got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ":\n" - " .quad (" HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ")\n" - " .size got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ", 8\n" -); - -// Create a GOT equivalent for the Hashable.hash(into:) method descriptor. -__asm( - " .type got." HASHABLE_HASH_METHOD_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." HASHABLE_HASH_METHOD_DESCRIPTOR ":\n" - " .quad (" HASHABLE_HASH_METHOD_DESCRIPTOR ")\n" - " .size got." HASHABLE_HASH_METHOD_DESCRIPTOR ", 8\n" -); - -// Create a GOT equivalent for the Hashable._rawHashValue method descriptor. -__asm( - " .type got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ":\n" - " .quad (" HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ")\n" - " .size got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ", 8\n" -); -#endif - // Define the conformance descriptor for tuple Hashable. We do this in // assembly to work around relative reference issues. __asm( @@ -678,14 +514,9 @@ __asm( " .globl " TUPLE_HASHABLE_CONF "\n" " .p2align 2\n" TUPLE_HASHABLE_CONF ":\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Hashable protocol descriptor, hence why we add 1 to indicate indirect. - " .long (got." HASHABLE_DESCRIPTOR_SYMBOL " - \ - (" TUPLE_HASHABLE_CONF ")) + 1\n" - #elif defined(__MACH__) - " .long " HASHABLE_DESCRIPTOR_SYMBOL "@GOTPCREL + 5\n" - #endif + // This is an indirectable relative reference to the GOT entry for the + // Hashable protocol descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(HASHABLE_DESCRIPTOR_SYMBOL) "\n" // 769 is the MetadataKind::Tuple " .long 769\n" // This indicates that we have no witness table pattern. We use a generic @@ -698,55 +529,30 @@ __asm( // This 4 is the ResilientWitnessesHeader indicating we have 4 resilient // witnesses. " .long 4\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Hashable base conformance for Equatable, hence why we add 1 to indicate - // indirect. - " .long ((got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR " - \ - (" TUPLE_HASHABLE_CONF ")) - 20) + 1\n" - #elif defined(__MACH__) - " .long " HASHABLE_BASE_CONFORMANCE_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + // This is an indirectable relative reference to the GOT entry for the + // Hashable base conformance for Equatable. + " .long " INDIRECT_RELREF_GOTPCREL(HASHABLE_BASE_CONFORMANCE_DESCRIPTOR) "\n" // This is a direct relative reference to the associated conformance for // Equatable defined above in assembly. NOTE: We intentionally use the // Comparable implementation for this because the implementation is the same // for both Hashable and Comparable. Both want to grab the Equatable table // from its elements whose witness table is located in the same place for both - // protocols. NOTE: This is minus 23 because the associated conformance + // protocols. NOTE: We + 1 here because the associated conformance // structure is 1 aligned. - " .long ((\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - \ - (" TUPLE_HASHABLE_CONF ")) - 23\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Hashable.hashValue method descriptor, hence why we add 1 to indicate - // indirect. - " .long ((got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR " - \ - (" TUPLE_HASHABLE_CONF ")) - 28) + 1\n" - #elif defined(__MACH__) - " .long " HASHABLE_HASHVALUE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + " .long (\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - . + 1\n" + // This is an indirectable relative reference to the GOT entry for the + // Hashable.hashValue method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(HASHABLE_HASHVALUE_METHOD_DESCRIPTOR) "\n" // This is a direct relative reference to the hashValue witness defined below. - " .long ((" TUPLE_HASHABLE_HASHVALUE ") - (" TUPLE_HASHABLE_CONF ")) - 32\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Hashable.hash(into:) method descriptor, hence why we add 1 to indicate - // indirect. - " .long ((got." HASHABLE_HASH_METHOD_DESCRIPTOR " - \ - (" TUPLE_HASHABLE_CONF ")) - 36) + 1\n" - #elif defined(__MACH__) - " .long " HASHABLE_HASH_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + " .long (" TUPLE_HASHABLE_HASHVALUE ") - .\n" + // This is an indirectable relative reference to the GOT entry for the + // Hashable.hash(into:) method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(HASHABLE_HASH_METHOD_DESCRIPTOR) "\n" // This is a direct relative reference to the hash(into:) witness defined below. - " .long ((" TUPLE_HASHABLE_HASH ") - (" TUPLE_HASHABLE_CONF ")) - 40\n" - #if defined(__ELF__) + " .long (" TUPLE_HASHABLE_HASH ") - .\n" // This is an indirectable relative reference to the GOT equivalent for the - // Hashable._rawHashValue method descriptor, hence why we add 1 to indicate - // indirect. - " .long ((got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR " - \ - (" TUPLE_HASHABLE_CONF ")) - 44) + 1\n" - #elif defined(__MACH__) - " .long " HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + // Hashable._rawHashValue method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR) "\n" // This 0 indicates that we are requesting the default implementation for the // _rawHashValue getter. " .long 0\n" @@ -758,7 +564,7 @@ __asm( " .long 0\n" // This is a direct relative reference to the private data for the // conformance. - " .long (__swift_tupleHashable_private - (" TUPLE_HASHABLE_CONF ")) - 60\n" + " .long __swift_tupleHashable_private - .\n" #if defined(__ELF__) " .size " TUPLE_HASHABLE_CONF ", 64\n" #endif diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 77c930c06b9df..95a81dd885a99 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -4837,7 +4837,7 @@ WitnessTableCacheEntry::allocate( void **fullTable = reinterpret_cast(this + 1); // Zero out the witness table. - memset(fullTable, 0, getWitnessTableSize(conformance)); + memset(fullTable, 0, getWitnessTableSize(Type, conformance)); // Instantiate the table. return instantiateWitnessTable(Type, Conformance, instantiationArgs, fullTable); @@ -4858,7 +4858,7 @@ getNondependentWitnessTable(const ProtocolConformanceDescriptor *conformance, } // Allocate space for the table. - auto tableSize = WitnessTableCacheEntry::getWitnessTableSize(conformance); + auto tableSize = WitnessTableCacheEntry::getWitnessTableSize(type, conformance); TaggedMetadataAllocator allocator; auto buffer = (void **)allocator.Allocate(tableSize, alignof(void*)); memset(buffer, 0, tableSize); diff --git a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp index 322b0b18993c2..3419bc5b419e0 100644 --- a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp +++ b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp @@ -84,7 +84,7 @@ __asm( // 769 is the MetadataKind::Tuple " .long 769\n" // This is a direct relative reference to the witness table defined below. - " .long ((" TUPLE_EQUATABLE_WT ") - (" TUPLE_EQUATABLE_CONF ")) - 8\n" + " .long (" TUPLE_EQUATABLE_WT ") - .\n" // 32 are the ConformanceFlags with the type reference bit set to MetadataKind. " .long 32\n" ); @@ -176,7 +176,7 @@ __asm( // 769 is the MetadataKind::Tuple " .long 769\n" // This is a direct relative reference to the witness table defined below. - " .long ((" TUPLE_COMPARABLE_WT ") - (" TUPLE_COMPARABLE_CONF ")) - 8\n" + " .long (" TUPLE_COMPARABLE_WT ") - .\n" // 32 are the ConformanceFlags with the type reference bit set to MetadataKind. " .long 32\n" ); @@ -477,7 +477,7 @@ __asm( // 769 is the MetadataKind::Tuple " .long 769\n" // This is a direct relative reference to the witness table defined below. - " .long ((" TUPLE_HASHABLE_WT ") - (" TUPLE_HASHABLE_CONF ")) - 8\n" + " .long (" TUPLE_HASHABLE_WT ") - .\n" // 32 are the ConformanceFlags with the type reference bit set to MetadataKind. " .long 32\n" ); @@ -535,14 +535,14 @@ using HasherCombineFn = SWIFT_CC(swift) void(OpaqueValue *value, static HashValueFn *get_hashValueFunc() { auto descriptor = SWIFT_LAZY_CONSTANT( reinterpret_cast( - dlsym(RTLD_DEFAULT, STR(SWIFT_HASHVALUE_FUNC)))); + dlsym(RTLD_DEFAULT, XSTR(SWIFT_HASHVALUE_FUNC)))); return descriptor; } static HasherCombineFn *getHashCombineFunc() { auto descriptor = SWIFT_LAZY_CONSTANT( reinterpret_cast( - dlsym(RTLD_DEFAULT, STR(SWIFT_HASHER_COMBINE_FUNC)))); + dlsym(RTLD_DEFAULT, XSTR(SWIFT_HASHER_COMBINE_FUNC)))); return descriptor; } From b78da2dcc2b26e6e1479e132e5236aa14196de96 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sun, 2 Aug 2020 23:35:00 -0400 Subject: [PATCH 661/745] [Runtime] Add stubs for indirect symbols on armv7/s, add missing newlines for ELF directives, fix compile crash for arm64e --- .../runtime/BuiltinProtocolConformances.cpp | 100 +++++++++++++++--- 1 file changed, 83 insertions(+), 17 deletions(-) diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp index 90d54b74d3362..8798c3e2e04e0 100644 --- a/stdlib/public/runtime/BuiltinProtocolConformances.cpp +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -33,6 +33,9 @@ using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, #elif defined(__MACH__) #if defined(__aarch64__) #define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOT - . + 1" +#elif defined(__arm__) +// Darwin doesn't support @GOT like syntax for 32 bit ARM. +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) "L" SYMBOL "$non_lazy_ptr - . + 1" #else #define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOTPCREL + 5" #endif @@ -42,6 +45,22 @@ using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, // Tuple Equatable Conformance //===----------------------------------------------------------------------===// +// For 32 bit ARM (specifically armv7 and armv7s for iphoneos), emit non-lazy +// pointer stubs to indirectly reference. Darwin doesn't support @GOT syntax for +// ARM. +#if defined(__MACH__) && defined(__arm__) && !defined(__aarch64__) +__asm( + " .section __DATA, __nl_symbol_ptr, non_lazy_symbol_pointers\n" + " .p2align 2\n" + "L" EQUATABLE_DESCRIPTOR_SYMBOL "$non_lazy_ptr:\n" + " .indirect_symbol " EQUATABLE_DESCRIPTOR_SYMBOL "\n" + " .long 0\n" + "L" EQUATABLE_EE_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " EQUATABLE_EE_METHOD_DESCRIPTOR "\n" + " .long 0\n" +); +#endif + // Define the conformance descriptor for tuple Equatable. We do this in // assembly to work around relative reference issues. __asm( @@ -137,14 +156,42 @@ bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, // Tuple Comparable Conformance //===----------------------------------------------------------------------===// +// For 32 bit ARM (specifically armv7 and armv7s for iphoneos), emit non-lazy +// pointer stubs to indirectly reference. Darwin doesn't support @GOT syntax for +// ARM. +#if defined(__MACH__) && defined(__arm__) && !defined(__aarch64__) +__asm( + " .section __DATA, __nl_symbol_ptr, non_lazy_symbol_pointers\n" + " .p2align 2\n" + "L" COMPARABLE_DESCRIPTOR_SYMBOL "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARABLE_DESCRIPTOR_SYMBOL "\n" + " .long 0\n" + "L" COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR "\n" + " .long 0\n" + "L" COMPARABLE_LT_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARABLE_LT_METHOD_DESCRIPTOR "\n" + " .long 0\n" + "L" COMPARBALE_LTE_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARBALE_LTE_METHOD_DESCRIPTOR "\n" + " .long 0\n" + "L" COMPARABLE_GTE_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARABLE_GTE_METHOD_DESCRIPTOR "\n" + " .long 0\n" + "L" COMPARABLE_GT_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARABLE_GT_METHOD_DESCRIPTOR "\n" + " .long 0\n" +); +#endif + // Define the associated conformance structure for tuple Comparable. We do this // in assembly to work around relative reference issues. __asm( #if defined(__ELF__) " .hidden \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" " .type \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\", @object\n" - " .section swift5_typeref, \"a\"" - " .weak \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"" + " .section swift5_typeref, \"a\"\n" + " .weak \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" #elif defined(__MACH__) " .private_extern \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" " .section __TEXT, __swift5_typeref\n" @@ -497,6 +544,31 @@ bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1, // Tuple Hashable Conformance //===----------------------------------------------------------------------===// +// For 32 bit ARM (specifically armv7 and armv7s for iphoneos), emit non-lazy +// pointer stubs to indirectly reference. Darwin doesn't support @GOT syntax for +// ARM. +#if defined(__MACH__) && defined(__arm__) && !defined(__aarch64__) +__asm( + " .section __DATA, __nl_symbol_ptr, non_lazy_symbol_pointers\n" + " .p2align 2\n" + "L" HASHABLE_DESCRIPTOR_SYMBOL "$non_lazy_ptr:\n" + " .indirect_symbol " HASHABLE_DESCRIPTOR_SYMBOL "\n" + " .long 0\n" + "L" HASHABLE_BASE_CONFORMANCE_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " HASHABLE_BASE_CONFORMANCE_DESCRIPTOR "\n" + " .long 0\n" + "L" HASHABLE_HASHVALUE_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " HASHABLE_HASHVALUE_METHOD_DESCRIPTOR "\n" + " .long 0\n" + "L" HASHABLE_HASH_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " HASHABLE_HASH_METHOD_DESCRIPTOR "\n" + " .long 0\n" + "L" HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR "\n" + " .long 0\n" +); +#endif + // Define the conformance descriptor for tuple Hashable. We do this in // assembly to work around relative reference issues. __asm( @@ -570,22 +642,19 @@ __asm( #endif ); -// These are all function values that we reinterpret later. -extern void *SWIFT_HASHVALUE_FUNC; -extern void *SWIFT_HASHER_COMBINE_FUNC; +extern "C" SWIFT_CC(swift) +intptr_t SWIFT_HASHVALUE_FUNC(OpaqueValue *value, Metadata *Self, + void *witnessTable); -using HashValueFn = SWIFT_CC(swift) intptr_t(OpaqueValue *value, Metadata *Self, - void *witnessTable); -using HasherCombineFn = SWIFT_CC(swift) void(OpaqueValue *value, - const Metadata *Self, - const WitnessTable *witnessTable, - SWIFT_CONTEXT OpaqueValue *hasher); +extern "C" SWIFT_CC(swift) +void SWIFT_HASHER_COMBINE_FUNC(OpaqueValue *value, const Metadata *Self, + const WitnessTable *witnessTable, + SWIFT_CONTEXT OpaqueValue *hasher); SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) intptr_t _swift_tupleHashable_hashValue(SWIFT_CONTEXT OpaqueValue *tuple, Metadata *Self, void *witnessTable) { - auto _hashValue = reinterpret_cast(&SWIFT_HASHVALUE_FUNC); - return _hashValue(tuple, Self, witnessTable); + return SWIFT_HASHVALUE_FUNC(tuple, Self, witnessTable); } SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) @@ -606,11 +675,8 @@ void _swift_tupleHashable_hash(OpaqueValue *hasher, auto value = reinterpret_cast( reinterpret_cast(tuple) + elt.Offset); - auto hasherCombine = - reinterpret_cast(&SWIFT_HASHER_COMBINE_FUNC); - // Call the combine function on the hasher for this element value and we're // done! - hasherCombine(value, elt.Type, conformance, hasher); + SWIFT_HASHER_COMBINE_FUNC(value, elt.Type, conformance, hasher); } } From 05cc4aa8af1268d0d9738e9523b4bd57f0e7ad94 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sun, 9 Aug 2020 21:37:18 -0400 Subject: [PATCH 662/745] [Runtime] Emit non lazy stub pointers for 32 bit x86 for tuple conformances --- .../runtime/BuiltinProtocolConformances.cpp | 65 ++++++++++++++----- .../rdar27830834.swift | 2 - 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp index 8798c3e2e04e0..fe5e4322de280 100644 --- a/stdlib/public/runtime/BuiltinProtocolConformances.cpp +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -28,29 +28,52 @@ using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, const Metadata *, const WitnessTable *); +// Elf indirect symbol references. #if defined(__ELF__) #define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOTPCREL + 1" -#elif defined(__MACH__) +#endif + +// MachO indirect symbol references. +#if defined(__MACH__) + +// 64 bit arm MachO #if defined(__aarch64__) #define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOT - . + 1" + +// 32 bit arm MachO #elif defined(__arm__) -// Darwin doesn't support @GOT like syntax for 32 bit ARM. +// MachO doesn't support @GOT like relocations for 32 bit arm. +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) "L" SYMBOL "$non_lazy_ptr - . + 1" +#endif + +// 64 bit x86_64 MachO +#if defined(__x86_64__) +// The + 4 is required for all x86_64 MachO GOTPC relocations. +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOTPCREL + 4 + 1" + +// 32 bit x86 MachO +#elif defined(__i386__) +// MachO doesn't support @GOT like relocations for 32 bit x86. #define INDIRECT_RELREF_GOTPCREL(SYMBOL) "L" SYMBOL "$non_lazy_ptr - . + 1" -#else -#define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOTPCREL + 5" #endif + #endif //===----------------------------------------------------------------------===// // Tuple Equatable Conformance //===----------------------------------------------------------------------===// -// For 32 bit ARM (specifically armv7 and armv7s for iphoneos), emit non-lazy -// pointer stubs to indirectly reference. Darwin doesn't support @GOT syntax for -// ARM. -#if defined(__MACH__) && defined(__arm__) && !defined(__aarch64__) +// For 32 bit ARM and i386 (specifically armv7, armv7s, and armv7k), emit +// non-lazy pointer stubs to indirectly reference. Darwin doesn't support @GOT +// syntax for those archs. +#if defined(__MACH__) && \ + ((defined(__arm__) && !defined(__aarch64__)) || defined(__i386__)) __asm( +#if defined(__arm__) " .section __DATA, __nl_symbol_ptr, non_lazy_symbol_pointers\n" +#elif defined(__i386__) + " .section __IMPORT, __pointers, non_lazy_symbol_pointers\n" +#endif " .p2align 2\n" "L" EQUATABLE_DESCRIPTOR_SYMBOL "$non_lazy_ptr:\n" " .indirect_symbol " EQUATABLE_DESCRIPTOR_SYMBOL "\n" @@ -156,12 +179,17 @@ bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, // Tuple Comparable Conformance //===----------------------------------------------------------------------===// -// For 32 bit ARM (specifically armv7 and armv7s for iphoneos), emit non-lazy -// pointer stubs to indirectly reference. Darwin doesn't support @GOT syntax for -// ARM. -#if defined(__MACH__) && defined(__arm__) && !defined(__aarch64__) +// For 32 bit ARM and i386 (specifically armv7, armv7s, and armv7k), emit +// non-lazy pointer stubs to indirectly reference. Darwin doesn't support @GOT +// syntax for those archs. +#if defined(__MACH__) && \ + ((defined(__arm__) && !defined(__aarch64__)) || defined(__i386__)) __asm( +#if defined(__arm__) " .section __DATA, __nl_symbol_ptr, non_lazy_symbol_pointers\n" +#elif defined(__i386__) + " .section __IMPORT, __pointers, non_lazy_symbol_pointers\n" +#endif " .p2align 2\n" "L" COMPARABLE_DESCRIPTOR_SYMBOL "$non_lazy_ptr:\n" " .indirect_symbol " COMPARABLE_DESCRIPTOR_SYMBOL "\n" @@ -544,12 +572,17 @@ bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1, // Tuple Hashable Conformance //===----------------------------------------------------------------------===// -// For 32 bit ARM (specifically armv7 and armv7s for iphoneos), emit non-lazy -// pointer stubs to indirectly reference. Darwin doesn't support @GOT syntax for -// ARM. -#if defined(__MACH__) && defined(__arm__) && !defined(__aarch64__) +// For 32 bit ARM and i386 (specifically armv7, armv7s, and armv7k), emit +// non-lazy pointer stubs to indirectly reference. Darwin doesn't support @GOT +// syntax for those archs. +#if defined(__MACH__) && \ + ((defined(__arm__) && !defined(__aarch64__)) || defined(__i386__)) __asm( +#if defined(__arm__) " .section __DATA, __nl_symbol_ptr, non_lazy_symbol_pointers\n" +#elif defined(__i386__) + " .section __IMPORT, __pointers, non_lazy_symbol_pointers\n" +#endif " .p2align 2\n" "L" HASHABLE_DESCRIPTOR_SYMBOL "$non_lazy_ptr:\n" " .indirect_symbol " HASHABLE_DESCRIPTOR_SYMBOL "\n" diff --git a/validation-test/Sema/type_checker_crashers_fixed/rdar27830834.swift b/validation-test/Sema/type_checker_crashers_fixed/rdar27830834.swift index f7be7a54079b2..b2aa737da625a 100644 --- a/validation-test/Sema/type_checker_crashers_fixed/rdar27830834.swift +++ b/validation-test/Sema/type_checker_crashers_fixed/rdar27830834.swift @@ -2,5 +2,3 @@ var d = [String:String]() _ = "\(d.map{ [$0 : $0] })" -// expected-error@-1 {{type 'Dictionary.Element' (aka '(key: String, value: String)') cannot conform to 'Hashable'; only concrete types such as structs, enums and classes can conform to protocols}} -// expected-note@-2 {{required by generic struct 'Dictionary' where 'Key' = 'Dictionary.Element' (aka '(key: String, value: String)')}} From 48ea83723e117a258b9798048f03a92d12104512 Mon Sep 17 00:00:00 2001 From: Azoy Date: Wed, 26 Aug 2020 00:10:09 -0400 Subject: [PATCH 663/745] Manually define _rawHashValue in compatibility libraries, AST fixes for builtin protocol conformance Remember to use substituted type in builtin conformance substitution --- lib/AST/ProtocolConformance.cpp | 19 +++++++- lib/AST/SubstitutionMap.cpp | 14 +++++- .../BuiltinProtocolConformances.cpp | 45 +++++++++++-------- test/IRGen/builtin_conformances.swift | 6 +-- 4 files changed, 59 insertions(+), 25 deletions(-) diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index f890f5bdbc9ea..1c6156608e043 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -1170,9 +1170,24 @@ ProtocolConformance::subst(TypeSubstitutionFn subs, const_cast(this), subMap); } - case ProtocolConformanceKind::Self: - case ProtocolConformanceKind::Builtin: + case ProtocolConformanceKind::Self: { return const_cast(this); + } + case ProtocolConformanceKind::Builtin: { + auto origType = getType(); + if (!origType->hasTypeParameter() && + !origType->hasArchetype()) + return const_cast(this); + + auto substType = origType.subst(subs, conformances, options); + if (substType->isEqual(origType)) + return const_cast(this); + + // All builtin conformances are concrete at the moment, so it's safe to + // directly call getConcrete. + return getProtocol()->getModuleContext() + ->lookupConformance(substType, getProtocol()).getConcrete(); + } case ProtocolConformanceKind::Inherited: { // Substitute the base. auto inheritedConformance diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp index 7653cc6a29961..b512029598d8a 100644 --- a/lib/AST/SubstitutionMap.cpp +++ b/lib/AST/SubstitutionMap.cpp @@ -420,7 +420,19 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { // For the second step, we're looking into the requirement signature for // this protocol. auto concrete = conformance.getConcrete(); - auto normal = concrete->getRootNormalConformance(); + auto root = concrete->getRootConformance(); + + // If we see a builtin conformance here as the root, simply lookup the + // conformance for the substitute type. + if (isa(root)) { + auto substType = type.subst(*this); + if (substType->hasError()) + return ProtocolConformanceRef(proto); + + return proto->getParentModule()->lookupConformance(substType, proto); + } + + auto normal = cast(root); // If we haven't set the signature conformances yet, force the issue now. if (normal->getSignatureConformances().empty()) { diff --git a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp index 3419bc5b419e0..759fe05f44ef0 100644 --- a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp +++ b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp @@ -497,6 +497,27 @@ void _emplaceTupleHashableDescriptor() { *tupleHashableConf = intptr_t(hashable) - intptr_t(tupleHashableConf); } +using RawHashValueFn = SWIFT_CC(swift) intptr_t(intptr_t seed, + const Metadata *Self, + const WitnessTable *witnessTable, + SWIFT_CONTEXT OpaqueValue *value); + +static RawHashValueFn *get_rawHashValueDefaultImplFunc() { + auto func = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSHsE13_rawHashValue4seedS2i_tF"))); + return func; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +intptr_t _swift_tupleHashable_rawHashValue(intptr_t seed, + SWIFT_CONTEXT OpaqueValue *tuple, + const Metadata *Self, + const WitnessTable *witnessTable) { + auto _rawHashValue = get_rawHashValueDefaultImplFunc(); + return _rawHashValue(seed, Self, witnessTable, tuple); +} + // The base Equatable protocol is itself a requirement, thus the requirement // count is 4 (Equatable + hashValue + hash(into:) + _rawHashValue) and the // witness is the tuple Equatable table. @@ -507,24 +528,10 @@ _WitnessTable<4> _swift_tupleHashable_wt = { reinterpret_cast(&_swift_tupleEquatable_wt), reinterpret_cast(_swift_tupleHashable_hashValue), reinterpret_cast(_swift_tupleHashable_hash), - nullptr + reinterpret_cast(_swift_tupleHashable_rawHashValue) } }; -static void *get_rawHashValueDefaultImplFunc() { - auto impl = SWIFT_LAZY_CONSTANT( - dlsym(RTLD_DEFAULT, "$sSHsE13_rawHashValue4seedS2i_tF")); - return impl; -} - -// Due to the fact that the compatibility libraries can't have a hard -// dependency to libswiftCore (which is where the _rawHashValue default impl -// lives), we have to manually implant this before calling any user code. -__attribute__((constructor)) -void _emplaceTupleHashable_rawHashValueDefaultImpl() { - _swift_tupleHashable_wt.Witnesses[3] = get_rawHashValueDefaultImplFunc(); -} - using HashValueFn = SWIFT_CC(swift) intptr_t(OpaqueValue *value, Metadata *Self, void *witnessTable); using HasherCombineFn = SWIFT_CC(swift) void(OpaqueValue *value, @@ -533,17 +540,17 @@ using HasherCombineFn = SWIFT_CC(swift) void(OpaqueValue *value, SWIFT_CONTEXT OpaqueValue *hasher); static HashValueFn *get_hashValueFunc() { - auto descriptor = SWIFT_LAZY_CONSTANT( + auto func = SWIFT_LAZY_CONSTANT( reinterpret_cast( dlsym(RTLD_DEFAULT, XSTR(SWIFT_HASHVALUE_FUNC)))); - return descriptor; + return func; } static HasherCombineFn *getHashCombineFunc() { - auto descriptor = SWIFT_LAZY_CONSTANT( + auto func = SWIFT_LAZY_CONSTANT( reinterpret_cast( dlsym(RTLD_DEFAULT, XSTR(SWIFT_HASHER_COMBINE_FUNC)))); - return descriptor; + return func; } SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) diff --git a/test/IRGen/builtin_conformances.swift b/test/IRGen/builtin_conformances.swift index 44c549c442fb1..39175da5d7148 100644 --- a/test/IRGen/builtin_conformances.swift +++ b/test/IRGen/builtin_conformances.swift @@ -74,15 +74,15 @@ public func hashValue(for instance: T) -> Int { public func useHashable(_ thing: T) -> Int { // CHECK: [[USE_HASHABLE_WT:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleHashable_conf, %swift.type* {{%.*}}, i8*** {{%.*}}) - // CHECK-NEXT: {{%.*}} = call swiftcc i64 {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_HASHABLE_WT]]) + // CHECK-NEXT: {{%.*}} = call swiftcc i{{.*}} {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_HASHABLE_WT]]) hashValue(for: (thing, thing)) } public func testTupleHashable() { // CHECK: [[TEST_TUPLE_HASHABLE_WT1:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleHashable_conf, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8*** undef) - // CHECK: {{%.*}} = call swiftcc i64 {{.*}}(%swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_HASHABLE_WT1]]) + // CHECK: {{%.*}} = call swiftcc i{{.*}} {{.*}}(%swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_HASHABLE_WT1]]) let _ = hashValue(for: ()) - // CHECK: {{%.*}} = call swiftcc i64 {{.*}}(%swift.type* {{%.*}}, i8** [[TEST_TUPLE_HASHABLE_WT1]], {{%.*}} noalias nocapture swiftself undef) + // CHECK: {{%.*}} = call swiftcc i{{.*}} {{.*}}(%swift.type* {{%.*}}, i8** [[TEST_TUPLE_HASHABLE_WT1]], {{%.*}} noalias nocapture swiftself undef) let _ = Wrapper(value: ()).hashValue } From 4a8948373e6ef1b07845afe8992b1af25b904087 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 22 Oct 2020 13:55:14 -0700 Subject: [PATCH 664/745] ClangImporter: support the MS anonymous structure extension The Microsoft extension permits the creation of structures without a name which effectively create an anonymous inline structure. Accommodate that in the ClangImporter. For more details about the extension, see: https://docs.microsoft.com/en-us/previous-versions/a3bbz53t(v=vs.140)#anonymous-structs Thanks to @brentdax for the suggestion of the additional check! --- lib/ClangImporter/ImportDecl.cpp | 7 +++++-- test/ClangImporter/Inputs/ctypes_msvc.h | 8 ++++++++ test/ClangImporter/ctypes_parse_msvc.swift | 4 ++++ 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 test/ClangImporter/Inputs/ctypes_msvc.h create mode 100644 test/ClangImporter/ctypes_parse_msvc.swift diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 2a058ac9ed739..0fed64cc4479a 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -1390,7 +1390,8 @@ createValueConstructor(ClangImporter::Implementation &Impl, continue; if (auto clangField = dyn_cast(var->getClangDecl())) - if (clangField->isAnonymousStructOrUnion()) + if (clangField->isAnonymousStructOrUnion() || + clangField->getDeclName().isEmpty()) generateParamName = false; } @@ -2397,6 +2398,8 @@ namespace { if (field->isAnonymousStructOrUnion()) { IdStream << "__Anonymous_field" << field->getFieldIndex(); } else { + assert(!field->getDeclName().isEmpty() && + "Microsoft anonymous struct extension?"); IdStream << field->getName(); } ImportedName Result; @@ -4011,7 +4014,7 @@ namespace { Optional correctSwiftName; ImportedName importedName; - if (!decl->isAnonymousStructOrUnion()) { + if (!decl->isAnonymousStructOrUnion() && !decl->getDeclName().isEmpty()) { importedName = importFullName(decl, correctSwiftName); if (!importedName) { return nullptr; diff --git a/test/ClangImporter/Inputs/ctypes_msvc.h b/test/ClangImporter/Inputs/ctypes_msvc.h new file mode 100644 index 0000000000000..81de4bc21817e --- /dev/null +++ b/test/ClangImporter/Inputs/ctypes_msvc.h @@ -0,0 +1,8 @@ + +typedef struct S { + unsigned char uc; +} S; + +typedef struct T { + S; +} T; diff --git a/test/ClangImporter/ctypes_parse_msvc.swift b/test/ClangImporter/ctypes_parse_msvc.swift new file mode 100644 index 0000000000000..8a121f23feaf7 --- /dev/null +++ b/test/ClangImporter/ctypes_parse_msvc.swift @@ -0,0 +1,4 @@ +// RUN: %target-swift-frontend -Xcc -fms-extensions -import-objc-header %S/Inputs/ctypes_msvc.h -typecheck -verify %s + +_ = T().uc +_ = T(S(uc: 0)) From a7f4534a77de3e1ee0d8edf96d66b9ebd76dca0e Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 22 Oct 2020 21:53:33 -0400 Subject: [PATCH 665/745] Harden swift-rpathize against possible first-column output from dyldinfo --- utils/swift-rpathize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/swift-rpathize.py b/utils/swift-rpathize.py index 5db01717fad0c..72a684797f15f 100755 --- a/utils/swift-rpathize.py +++ b/utils/swift-rpathize.py @@ -60,7 +60,7 @@ def rpathize(filename): # The output from dyldinfo -dylibs is a line of header followed by one # install name per line, indented with spaces. dylib_regex = re.compile( - r"^\s*(?P/usr/lib/swift/(?P.*\.dylib))\s*$") + r"(^|.*\s)(?P/usr/lib/swift/(?P.*\.dylib))\s*$") # Build a command to invoke install_name_tool. command = ['install_name_tool'] From 9f8b96605825fa0aa1abaa4cfef8d94ac03017d9 Mon Sep 17 00:00:00 2001 From: scentini Date: Fri, 23 Oct 2020 10:34:44 +0200 Subject: [PATCH 666/745] Add a test that checks that ClangImporter chooses only exported modules (#34392) --- .../Inputs/HelperModule.swift | 2 ++ .../Inputs/MainModule.swift | 3 ++ .../Inputs/foreign-a.h | 1 + .../Inputs/foreign-b.h | 1 + .../Inputs/module.modulemap | 13 +++++++ .../Inputs/textual-header.h | 1 + .../print-qualified-clang-types.swift | 36 +++++++++++++++++++ 7 files changed, 57 insertions(+) create mode 100644 test/Interop/C/modules/print-qualified-clang-types/Inputs/HelperModule.swift create mode 100644 test/Interop/C/modules/print-qualified-clang-types/Inputs/MainModule.swift create mode 100644 test/Interop/C/modules/print-qualified-clang-types/Inputs/foreign-a.h create mode 100644 test/Interop/C/modules/print-qualified-clang-types/Inputs/foreign-b.h create mode 100644 test/Interop/C/modules/print-qualified-clang-types/Inputs/module.modulemap create mode 100644 test/Interop/C/modules/print-qualified-clang-types/Inputs/textual-header.h create mode 100644 test/Interop/C/modules/print-qualified-clang-types/print-qualified-clang-types.swift diff --git a/test/Interop/C/modules/print-qualified-clang-types/Inputs/HelperModule.swift b/test/Interop/C/modules/print-qualified-clang-types/Inputs/HelperModule.swift new file mode 100644 index 0000000000000..38cd0e7b41e91 --- /dev/null +++ b/test/Interop/C/modules/print-qualified-clang-types/Inputs/HelperModule.swift @@ -0,0 +1,2 @@ +import ForeignA +@_exported import ForeignB diff --git a/test/Interop/C/modules/print-qualified-clang-types/Inputs/MainModule.swift b/test/Interop/C/modules/print-qualified-clang-types/Inputs/MainModule.swift new file mode 100644 index 0000000000000..6f7a6a5629788 --- /dev/null +++ b/test/Interop/C/modules/print-qualified-clang-types/Inputs/MainModule.swift @@ -0,0 +1,3 @@ +import HelperModule + +public func funcTakingForeignStruct(_ param: ForeignStruct) {} diff --git a/test/Interop/C/modules/print-qualified-clang-types/Inputs/foreign-a.h b/test/Interop/C/modules/print-qualified-clang-types/Inputs/foreign-a.h new file mode 100644 index 0000000000000..ca297206959cc --- /dev/null +++ b/test/Interop/C/modules/print-qualified-clang-types/Inputs/foreign-a.h @@ -0,0 +1 @@ +#include "textual-header.h" diff --git a/test/Interop/C/modules/print-qualified-clang-types/Inputs/foreign-b.h b/test/Interop/C/modules/print-qualified-clang-types/Inputs/foreign-b.h new file mode 100644 index 0000000000000..ca297206959cc --- /dev/null +++ b/test/Interop/C/modules/print-qualified-clang-types/Inputs/foreign-b.h @@ -0,0 +1 @@ +#include "textual-header.h" diff --git a/test/Interop/C/modules/print-qualified-clang-types/Inputs/module.modulemap b/test/Interop/C/modules/print-qualified-clang-types/Inputs/module.modulemap new file mode 100644 index 0000000000000..bc5f5f4f50bab --- /dev/null +++ b/test/Interop/C/modules/print-qualified-clang-types/Inputs/module.modulemap @@ -0,0 +1,13 @@ +module ForeignA { + // Nest the header in a sub-module to make sure these are handled correctly. + module Sub { + header "foreign-a.h" + } +} + +module ForeignB { + // Nest the header in a sub-module to make sure these are handled correctly. + module Sub { + header "foreign-b.h" + } +} diff --git a/test/Interop/C/modules/print-qualified-clang-types/Inputs/textual-header.h b/test/Interop/C/modules/print-qualified-clang-types/Inputs/textual-header.h new file mode 100644 index 0000000000000..a53012248d706 --- /dev/null +++ b/test/Interop/C/modules/print-qualified-clang-types/Inputs/textual-header.h @@ -0,0 +1 @@ +typedef struct {} ForeignStruct; diff --git a/test/Interop/C/modules/print-qualified-clang-types/print-qualified-clang-types.swift b/test/Interop/C/modules/print-qualified-clang-types/print-qualified-clang-types.swift new file mode 100644 index 0000000000000..13a78745481e8 --- /dev/null +++ b/test/Interop/C/modules/print-qualified-clang-types/print-qualified-clang-types.swift @@ -0,0 +1,36 @@ +// Check that when qualifying Clang types with a module name, we choose a +// visible module. Clang types need special treatment because multiple Clang +// modules can contain the same type declarations from a textually included +// header, but not all of these modules may be visible. If we choose a module +// that isn't visible, we produce `.swiftinterface` files that don't compile. +// +// To test this, the test sets up the following structure: +// +// MainModule (Swift module) +// import HelperModule (Swift module) +// import ForeignA (Clang module) +// #include "textual-header.h" +// @_exported import ForeignB (Clang module) +// #include "textual-header.h" +// +// `ForeignA` and `ForeignB` both include the same textual header, which +// defines the struct `ForeignStruct`. +// +// Because `ForeignB` is re-exported by `HelperModule`, it is visible from +// `MainModule`, but `ForeignA` is not. This means that when `ForeignStruct` is +// used in `MainModule`, its qualified name should be printed as +// `ForeignB.ForeignStruct`, not `ForeignA.ForeignStruct`. +// +// In addition to checking for the presence of the expected string in the +// `.swiftinterface` file, we also verify that it compiles without error. +// +// This is a regression test for https://bugs.swift.org/browse/SR-13032. + +// RUN: %empty-directory(%t) +// RUN: mkdir %t/helper_module %t/main_module +// RUN: %target-swift-frontend -enable-library-evolution -swift-version 5 -emit-module -o %t/helper_module/HelperModule.swiftmodule %S/Inputs/HelperModule.swift -I %S/Inputs +// RUN: %target-swift-frontend -enable-library-evolution -swift-version 5 -emit-module -o %t/main_module/MainModule.swiftmodule -emit-module-interface-path %t/main_module/MainModule.swiftinterface -I %t/helper_module %S/Inputs/MainModule.swift -I %S/Inputs +// RUN: %FileCheck --input-file=%t/main_module/MainModule.swiftinterface %s +// RUN: %target-swift-frontend -typecheck -swift-version 5 %t/main_module/MainModule.swiftinterface -I %t/helper_module -I %S/Inputs + +// CHECK: public func funcTakingForeignStruct(_ param: ForeignB.ForeignStruct) From 16e541709e7a9292d4cbd7d9c338cfe38f1b64ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=85=B7=E9=85=B7=E7=9A=84=E5=93=80=E6=AE=BF?= Date: Fri, 23 Oct 2020 21:09:33 +0800 Subject: [PATCH 667/745] Update ObjectIdentifier.swift --- stdlib/public/core/ObjectIdentifier.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/ObjectIdentifier.swift b/stdlib/public/core/ObjectIdentifier.swift index 2ef5a9493a8ac..cd9cc57f4d194 100644 --- a/stdlib/public/core/ObjectIdentifier.swift +++ b/stdlib/public/core/ObjectIdentifier.swift @@ -12,11 +12,12 @@ /// A unique identifier for a class instance or metatype. /// +/// And the unique identifier is only valid for comparisons during the lifetime +/// of the instance. +/// /// In Swift, only class instances and metatypes have unique identities. There /// is no notion of identity for structs, enums, functions, or tuples. /// -/// `ObjectIdentifier` is only guaranteed to remain unique for the -/// lifetime of an object. @frozen // trivial-implementation public struct ObjectIdentifier { @usableFromInline // trivial-implementation From 9c9c5da944f7ba0ea5adf7b90528b473fc9effca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=85=B7=E9=85=B7=E7=9A=84=E5=93=80=E6=AE=BF?= Date: Fri, 23 Oct 2020 21:10:04 +0800 Subject: [PATCH 668/745] Update ObjectIdentifier.swift --- stdlib/public/core/ObjectIdentifier.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/stdlib/public/core/ObjectIdentifier.swift b/stdlib/public/core/ObjectIdentifier.swift index cd9cc57f4d194..d8972c0b67743 100644 --- a/stdlib/public/core/ObjectIdentifier.swift +++ b/stdlib/public/core/ObjectIdentifier.swift @@ -17,7 +17,6 @@ /// /// In Swift, only class instances and metatypes have unique identities. There /// is no notion of identity for structs, enums, functions, or tuples. -/// @frozen // trivial-implementation public struct ObjectIdentifier { @usableFromInline // trivial-implementation From 0fb40805d5e2e4d08c103066e73909c01c3976fe Mon Sep 17 00:00:00 2001 From: Eric Miotto <1094986+edymtt@users.noreply.github.com> Date: Fri, 23 Oct 2020 07:12:47 -0700 Subject: [PATCH 669/745] Fix destination for clang-builtin-headers-in-clang-resource-dir (#34364) `LLVM_LIBRARY_OUTPUT_INTDIR` already points to the `lib` subdirectory of LLVM build folder. Addresses rdar://70486284 --- stdlib/public/SwiftShims/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/public/SwiftShims/CMakeLists.txt b/stdlib/public/SwiftShims/CMakeLists.txt index 4c2b3c4e59588..33c49a3f8861e 100644 --- a/stdlib/public/SwiftShims/CMakeLists.txt +++ b/stdlib/public/SwiftShims/CMakeLists.txt @@ -223,8 +223,8 @@ endif() # need to use a different version of the headers than the installed Clang. This # should be used in conjunction with clang-resource-dir-symlink. file(TO_CMAKE_PATH "${LLVM_LIBRARY_OUTPUT_INTDIR}" - _SWIFT_SHIMS_PATH_TO_CLANG_BUILD) -swift_install_in_component(DIRECTORY "${_SWIFT_SHIMS_PATH_TO_CLANG_BUILD}/lib/clang" + _SWIFT_SHIMS_PATH_TO_CLANG_LIB_BUILD) +swift_install_in_component(DIRECTORY "${_SWIFT_SHIMS_PATH_TO_CLANG_LIB_BUILD}/clang" DESTINATION "lib" COMPONENT clang-builtin-headers-in-clang-resource-dir PATTERN "*.h") From b98581704633d14d286758800f53bebdfcbd7e00 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sun, 30 Aug 2020 15:21:31 -0400 Subject: [PATCH 670/745] [Runtime] Add Native Windows support for builtin conformances Force C name mangling for Windows --- .../runtime/BuiltinProtocolConformances.cpp | 21 ++++++++++++++++++- stdlib/public/runtime/ProtocolConformance.cpp | 12 +++++------ test/IRGen/builtin_conformances.swift | 6 +++--- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp index fe5e4322de280..7226fa84fabac 100644 --- a/stdlib/public/runtime/BuiltinProtocolConformances.cpp +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -56,7 +56,11 @@ using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, // MachO doesn't support @GOT like relocations for 32 bit x86. #define INDIRECT_RELREF_GOTPCREL(SYMBOL) "L" SYMBOL "$non_lazy_ptr - . + 1" #endif +#endif +// Windows native indirect symbol references. +#if defined(_WIN32) +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) "__imp_" SYMBOL " - . + 1" #endif //===----------------------------------------------------------------------===// @@ -97,6 +101,10 @@ __asm( #elif defined(__MACH__) " .zerofill __DATA, __bss, __swift_tupleEquatable_private, 128, 4\n" " .section __TEXT, __const\n" + #elif defined(_WIN32) + " .lcomm __swift_tupleEquatable_private, 128, 16\n" + " .section .rdata, \"dr\"\n" + #pragma comment(linker, "/EXPORT:_swift_tupleEquatable_conf,DATA") #endif " .globl " TUPLE_EQUATABLE_CONF "\n" " .p2align 2\n" @@ -135,7 +143,7 @@ __asm( #endif ); -extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; +extern "C" const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, @@ -225,6 +233,9 @@ __asm( " .section __TEXT, __swift5_typeref\n" " .globl \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" " .weak_definition \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + #elif defined(_WIN32) + " .section .sw5tyrf$B, \"dr\", discard, \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + " .globl \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" #endif " .p2align 1\n" "\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\":\n" @@ -253,6 +264,10 @@ __asm( #elif defined(__MACH__) " .zerofill __DATA, __bss, __swift_tupleComparable_private, 128, 4\n" " .section __TEXT, __const\n" + #elif defined(_WIN32) + " .lcomm __swift_tupleComparable_private, 128, 16\n" + " .section .rdata, \"dr\"\n" + #pragma comment(linker, "/EXPORT:_swift_tupleComparable_conf,DATA") #endif " .globl " TUPLE_COMPARABLE_CONF "\n" " .p2align 2\n" @@ -615,6 +630,10 @@ __asm( #elif defined(__MACH__) " .zerofill __DATA, __bss, __swift_tupleHashable_private, 128, 4\n" " .section __TEXT, __const\n" + #elif defined(_WIN32) + " .lcomm __swift_tupleHashable_private, 128, 16\n" + " .section .rdata, \"dr\"\n" + #pragma comment(linker, "/EXPORT:_swift_tupleHashable_conf,DATA") #endif " .globl " TUPLE_HASHABLE_CONF "\n" " .p2align 2\n" diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index 5987dc9a91c07..da21c70bf6acf 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -376,9 +376,9 @@ searchInConformanceCache(const Metadata *type, return {false, nullptr}; } -extern const ProtocolDescriptor EQUATABLE_DESCRIPTOR; -extern const ProtocolDescriptor COMPARABLE_DESCRIPTOR; -extern const ProtocolDescriptor HASHABLE_DESCRIPTOR; +extern "C" const ProtocolDescriptor EQUATABLE_DESCRIPTOR; +extern "C" const ProtocolDescriptor COMPARABLE_DESCRIPTOR; +extern "C" const ProtocolDescriptor HASHABLE_DESCRIPTOR; static bool tupleConformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { @@ -401,9 +401,9 @@ static bool tupleConformsToProtocol(const Metadata *type, return true; } -extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; -extern const ProtocolConformanceDescriptor _swift_tupleComparable_conf; -extern const ProtocolConformanceDescriptor _swift_tupleHashable_conf; +extern "C" const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; +extern "C" const ProtocolConformanceDescriptor _swift_tupleComparable_conf; +extern "C" const ProtocolConformanceDescriptor _swift_tupleHashable_conf; static const ProtocolConformanceDescriptor *getTupleConformanceDescriptor( const ProtocolDescriptor *protocol) { diff --git a/test/IRGen/builtin_conformances.swift b/test/IRGen/builtin_conformances.swift index 39175da5d7148..f97f905d4a782 100644 --- a/test/IRGen/builtin_conformances.swift +++ b/test/IRGen/builtin_conformances.swift @@ -1,8 +1,8 @@ // RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s -// CHECK-LABEL: @_swift_tupleEquatable_conf = external global %swift.protocol_conformance_descriptor -// CHECK-LABEL: @_swift_tupleComparable_conf = external global %swift.protocol_conformance_descriptor -// CHECK-LABEL: @_swift_tupleHashable_conf = external global %swift.protocol_conformance_descriptor +// CHECK-LABEL: @_swift_tupleEquatable_conf = external{{( dllimport)?}} global %swift.protocol_conformance_descriptor +// CHECK-LABEL: @_swift_tupleComparable_conf = external{{( dllimport)?}} global %swift.protocol_conformance_descriptor +// CHECK-LABEL: @_swift_tupleHashable_conf = external{{( dllimport)?}} global %swift.protocol_conformance_descriptor struct Wrapper { let value: T From 2e65e498240ee26987d5fdcf549555cf9a9f5903 Mon Sep 17 00:00:00 2001 From: Benjamin Kindle Date: Fri, 23 Oct 2020 12:57:56 -0400 Subject: [PATCH 671/745] remove extra "by" --- docs/Driver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Driver.md b/docs/Driver.md index 8a935bd1f8b51..58054d01d5518 100644 --- a/docs/Driver.md +++ b/docs/Driver.md @@ -205,7 +205,7 @@ in becoming more like non-whole-module builds. ## Incremental Builds ## -Incremental builds in Swift work by primarily by cross-file dependency +Incremental builds in Swift work primarily by cross-file dependency analysis, described in [DependencyAnalysis.md](DependencyAnalysis.md). Compiling a single file might be necessary because that file has changed, but it could also be because that file depends on something else that might have From 1f479896f4c12270cc8189f12f01622017db9df2 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Fri, 23 Oct 2020 09:54:17 -0700 Subject: [PATCH 672/745] Revert "[NFC] Clarify semantics of getImportedModules." This reverts commit 4b5d8851147bed1f0945b7e23cbf788ce7869891. --- include/swift/AST/Module.h | 32 +++-------------------- include/swift/Basic/OptionSet.h | 9 ------- lib/AST/ImportCache.cpp | 2 -- lib/AST/Module.cpp | 34 ++++++++++--------------- lib/Frontend/ModuleInterfaceSupport.cpp | 3 +-- lib/Serialization/Serialization.cpp | 6 +---- 6 files changed, 18 insertions(+), 68 deletions(-) diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index a4cf40c60f6fa..c2b94e0e0fc10 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -593,8 +593,7 @@ class ModuleDecl : public DeclContext, public TypeDecl { Default = 1 << 1, /// Include imports declared with `@_implementationOnly`. ImplementationOnly = 1 << 2, - /// Include imports of SPIs declared with `@_spi`. Non-SPI imports are - /// included whether or not this flag is specified. + /// Include imports of SPIs declared with `@_spi` SPIAccessControl = 1 << 3, /// Include imports shadowed by a cross-import overlay. Unshadowed imports /// are included whether or not this flag is specified. @@ -605,33 +604,8 @@ class ModuleDecl : public DeclContext, public TypeDecl { /// Looks up which modules are imported by this module. /// - /// \p filter controls which imports are included in the list. - /// - /// There are three axes for categorizing imports: - /// 1. Privacy: Exported/Private/ImplementationOnly (mutually exclusive). - /// 2. SPI/non-SPI: An import of any privacy level may be @_spi("SPIName"). - /// 3. Shadowed/Non-shadowed: An import of any privacy level may be shadowed - /// by a cross-import overlay. - /// - /// It is also possible for SPI imports to be shadowed by a cross-import - /// overlay. - /// - /// If \p filter contains multiple privacy levels, modules at all the privacy - /// levels are included. - /// - /// If \p filter contains \c ImportFilterKind::SPIAccessControl, then both - /// SPI and non-SPI imports are included. Otherwise, only non-SPI imports are - /// included. - /// - /// If \p filter contains \c ImportFilterKind::ShadowedByCrossImportOverlay, - /// both shadowed and non-shadowed imports are included. Otherwise, only - /// non-shadowed imports are included. - /// - /// Clang modules have some additional complexities; see the implementation of - /// \c ClangModuleUnit::getImportedModules for details. - /// - /// \pre \p filter must contain at least one privacy level, i.e. one of - /// \c Exported or \c Private or \c ImplementationOnly. + /// \p filter controls whether public, private, or any imports are included + /// in this list. void getImportedModules(SmallVectorImpl &imports, ImportFilter filter = ImportFilterKind::Exported) const; diff --git a/include/swift/Basic/OptionSet.h b/include/swift/Basic/OptionSet.h index 3d632c6c1b6bf..c5758b250ca9c 100644 --- a/include/swift/Basic/OptionSet.h +++ b/include/swift/Basic/OptionSet.h @@ -19,7 +19,6 @@ #include "llvm/ADT/None.h" -#include #include #include #include @@ -99,14 +98,6 @@ class OptionSet { return Storage == set.Storage; } - /// Check if this option set contains any options from \p set. - /// - /// \pre \p set must be non-empty. - bool containsAny(OptionSet set) const { - assert((bool)set && "argument must be non-empty"); - return (bool)((*this) & set); - } - // '==' and '!=' are deliberately not defined because they provide a pitfall // where someone might use '==' but really want 'contains'. If you actually // want '==' behavior, use 'containsOnly'. diff --git a/lib/AST/ImportCache.cpp b/lib/AST/ImportCache.cpp index af6e922ba0773..e824d99111f4e 100644 --- a/lib/AST/ImportCache.cpp +++ b/lib/AST/ImportCache.cpp @@ -174,7 +174,6 @@ ImportSet &ImportCache::getImportSet(const DeclContext *dc) { imports.emplace_back(ImportPath::Access(), mod); if (file) { - // Should include both SPI & non-SPI. file->getImportedModules(imports, {ModuleDecl::ImportFilterKind::Default, ModuleDecl::ImportFilterKind::ImplementationOnly, @@ -259,7 +258,6 @@ ImportCache::getAllAccessPathsNotShadowedBy(const ModuleDecl *mod, stack.emplace_back(ImportPath::Access(), currentMod); if (auto *file = dyn_cast(dc)) { - // Should include both SPI & non-SPI file->getImportedModules(stack, {ModuleDecl::ImportFilterKind::Default, ModuleDecl::ImportFilterKind::ImplementationOnly, diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 6b4a60c42d24f..633bf8a2aa0cf 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -95,7 +95,7 @@ void BuiltinUnit::LookupCache::lookupValue( SmallVectorImpl &Result) { // Only qualified lookup ever finds anything in the builtin module. if (LookupKind != NLKind::QualifiedLookup) return; - + ValueDecl *&Entry = Cache[Name]; ASTContext &Ctx = M.getParentModule()->getASTContext(); if (!Entry) { @@ -175,7 +175,7 @@ class swift::SourceLookupCache { /// Throw away as much memory as possible. void invalidate(); - + void lookupValue(DeclName Name, NLKind LookupKind, SmallVectorImpl &Result); @@ -203,13 +203,13 @@ class swift::SourceLookupCache { void lookupVisibleDecls(ImportPath::Access AccessPath, VisibleDeclConsumer &Consumer, NLKind LookupKind); - + void populateMemberCache(const SourceFile &SF); void populateMemberCache(const ModuleDecl &Mod); void lookupClassMembers(ImportPath::Access AccessPath, VisibleDeclConsumer &consumer); - + void lookupClassMember(ImportPath::Access accessPath, DeclName name, SmallVectorImpl &results); @@ -331,7 +331,7 @@ void SourceLookupCache::lookupValue(DeclName Name, NLKind LookupKind, SmallVectorImpl &Result) { auto I = TopLevelValues.find(Name); if (I == TopLevelValues.end()) return; - + Result.reserve(I->second.size()); for (ValueDecl *Elt : I->second) Result.push_back(Elt); @@ -398,7 +398,7 @@ void SourceLookupCache::lookupVisibleDecls(ImportPath::Access AccessPath, void SourceLookupCache::lookupClassMembers(ImportPath::Access accessPath, VisibleDeclConsumer &consumer) { assert(accessPath.size() <= 1 && "can only refer to top-level decls"); - + if (!accessPath.empty()) { for (auto &member : ClassMembers) { // Non-simple names are also stored under their simple name, so make @@ -432,11 +432,11 @@ void SourceLookupCache::lookupClassMember(ImportPath::Access accessPath, DeclName name, SmallVectorImpl &results) { assert(accessPath.size() <= 1 && "can only refer to top-level decls"); - + auto iter = ClassMembers.find(name); if (iter == ClassMembers.end()) return; - + if (!accessPath.empty()) { for (ValueDecl *vd : iter->second) { auto *nominal = vd->getDeclContext()->getSelfNominalTypeDecl(); @@ -1163,13 +1163,6 @@ void SourceFile::lookupPrecedenceGroupDirect( void ModuleDecl::getImportedModules(SmallVectorImpl &modules, ModuleDecl::ImportFilter filter) const { - assert(filter.containsAny(ImportFilter({ - ModuleDecl::ImportFilterKind::Exported, - ModuleDecl::ImportFilterKind::Default, - ModuleDecl::ImportFilterKind::ImplementationOnly})) - && "filter should have at least one of Exported|Private|ImplementationOnly" - ); - FORWARD(getImportedModules, (modules, filter)); } @@ -1194,12 +1187,11 @@ SourceFile::getImportedModules(SmallVectorImpl &modules, requiredFilter |= ModuleDecl::ImportFilterKind::Exported; else if (desc.options.contains(ImportFlags::ImplementationOnly)) requiredFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly; + else if (desc.options.contains(ImportFlags::SPIAccessControl)) + requiredFilter |= ModuleDecl::ImportFilterKind::SPIAccessControl; else requiredFilter |= ModuleDecl::ImportFilterKind::Default; - if (desc.options.contains(ImportFlags::SPIAccessControl)) - requiredFilter |= ModuleDecl::ImportFilterKind::SPIAccessControl; - if (!separatelyImportedOverlays.lookup(desc.module.importedModule).empty()) requiredFilter |= ModuleDecl::ImportFilterKind::ShadowedByCrossImportOverlay; @@ -2374,7 +2366,7 @@ bool FileUnit::walk(ASTWalker &walker) { #ifndef NDEBUG PrettyStackTraceDecl debugStack("walking into decl", D); #endif - + if (D->walk(walker)) return true; @@ -2513,7 +2505,7 @@ SourceFile::lookupOpaqueResultType(StringRef MangledName) { auto found = ValidatedOpaqueReturnTypes.find(MangledName); if (found != ValidatedOpaqueReturnTypes.end()) return found->second; - + // If there are unvalidated decls with opaque types, go through and validate // them now. (void) getOpaqueReturnTypeDecls(); @@ -2521,7 +2513,7 @@ SourceFile::lookupOpaqueResultType(StringRef MangledName) { found = ValidatedOpaqueReturnTypes.find(MangledName); if (found != ValidatedOpaqueReturnTypes.end()) return found->second; - + // Otherwise, we don't have a matching opaque decl. return nullptr; } diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index 4c442fd32feec..ea04e971aef0a 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -115,8 +115,7 @@ static void printImports(raw_ostream &out, SmallVector ioiImport; M->getImportedModules(ioiImport, - {ModuleDecl::ImportFilterKind::ImplementationOnly, - ModuleDecl::ImportFilterKind::SPIAccessControl}); + ModuleDecl::ImportFilterKind::ImplementationOnly); ioiImportSet.insert(ioiImport.begin(), ioiImport.end()); } diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index dc881ddb61732..a554e7f3ab290 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -1055,11 +1055,7 @@ void Serializer::writeInputBlock(const SerializationOptions &options) { ImportSet privateImportSet = getImportsAsSet(M, ModuleDecl::ImportFilterKind::Default); ImportSet spiImportSet = - getImportsAsSet(M, { - ModuleDecl::ImportFilterKind::Exported, - ModuleDecl::ImportFilterKind::Default, - ModuleDecl::ImportFilterKind::SPIAccessControl - }); + getImportsAsSet(M, ModuleDecl::ImportFilterKind::SPIAccessControl); auto clangImporter = static_cast(M->getASTContext().getClangModuleLoader()); From 989ea5bf5e547f7d11c0d3cb71bb254de31009e1 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 22 Oct 2020 16:40:36 -0700 Subject: [PATCH 673/745] [IRGen] Track repeated requirements for async functions in NecessaryBindings. Previously, NecessaryBindings used a SetVector to store the GenericRequirements that it accumulates. That was a problem for the layout of async contexts where it is possible for the same generic argument to be repeated. Meanwhile, that is correct for partial application forwarders where there is a single thunk for every partial apply and consequently the bindings can be packed in without duplication. Here, a SetVector is used only when the NecessaryBindings are for partial apply forwarders. When the NecessaryBindings are instead for async functions, a SmallVector is used. --- lib/IRGen/GenProto.cpp | 30 +++++++++--------- lib/IRGen/NecessaryBindings.h | 58 ++++++++++++++++++++++++++--------- 2 files changed, 58 insertions(+), 30 deletions(-) diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 9fad29f9fd92a..02df6de244b2a 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -2688,21 +2688,21 @@ void irgen::emitPolymorphicParametersFromArray(IRGenFunction &IGF, Size NecessaryBindings::getBufferSize(IRGenModule &IGM) const { // We need one pointer for each archetype or witness table. - return IGM.getPointerSize() * Requirements.size(); + return IGM.getPointerSize() * size(); } void NecessaryBindings::restore(IRGenFunction &IGF, Address buffer, MetadataState metadataState) const { - bindFromGenericRequirementsBuffer(IGF, Requirements.getArrayRef(), buffer, + bindFromGenericRequirementsBuffer(IGF, getRequirements(), buffer, metadataState, - [&](CanType type) { return type;}); + [&](CanType type) { return type; }); } template static void save(const NecessaryBindings &bindings, IRGenFunction &IGF, Address buffer, Transform transform) { emitInitOfGenericRequirementsBuffer( - IGF, bindings.getRequirements().getArrayRef(), buffer, + IGF, bindings.getRequirements(), buffer, [&](GenericRequirement requirement) -> llvm::Value * { CanType type = requirement.TypeParameter; if (auto protocol = requirement.Protocol) { @@ -2770,14 +2770,13 @@ void NecessaryBindings::addTypeMetadata(CanType type) { // Generic types are trickier, because they can require conformances. // Otherwise, just record the need for this metadata. - Requirements.insert({type, nullptr}); + addRequirement({type, nullptr}); } /// Add all the abstract conditional conformances in the specialized /// conformance to the \p requirements. -static void addAbstractConditionalRequirements( - SpecializedProtocolConformance *specializedConformance, - llvm::SetVector &requirements) { +void NecessaryBindings::addAbstractConditionalRequirements( + SpecializedProtocolConformance *specializedConformance) { auto subMap = specializedConformance->getSubstitutionMap(); auto condRequirements = specializedConformance->getConditionalRequirements(); for (auto req : condRequirements) { @@ -2789,7 +2788,7 @@ static void addAbstractConditionalRequirements( auto archetype = dyn_cast(ty); if (!archetype) continue; - requirements.insert({ty, proto}); + addRequirement({ty, proto}); } // Recursively add conditional requirements. for (auto &conf : subMap.getConformances()) { @@ -2799,7 +2798,7 @@ static void addAbstractConditionalRequirements( dyn_cast(conf.getConcrete()); if (!specializedConf) continue; - addAbstractConditionalRequirements(specializedConf, requirements); + addAbstractConditionalRequirements(specializedConf); } } @@ -2811,8 +2810,8 @@ void NecessaryBindings::addProtocolConformance(CanType type, dyn_cast(concreteConformance); // The partial apply forwarder does not have the context to reconstruct // abstract conditional conformance requirements. - if (forPartialApply && specializedConf) { - addAbstractConditionalRequirements(specializedConf, Requirements); + if (forPartialApply() && specializedConf) { + addAbstractConditionalRequirements(specializedConf); } else if (forAsyncFunction()) { ProtocolDecl *protocol = conf.getRequirement(); GenericRequirement requirement; @@ -2821,7 +2820,7 @@ void NecessaryBindings::addProtocolConformance(CanType type, std::pair pair{requirement, conf}; Conformances.insert(pair); - Requirements.insert({type, concreteConformance->getProtocol()}); + addRequirement({type, concreteConformance->getProtocol()}); } return; } @@ -2829,7 +2828,7 @@ void NecessaryBindings::addProtocolConformance(CanType type, // TODO: pass something about the root conformance necessary to // reconstruct this. - Requirements.insert({type, conf.getAbstract()}); + addRequirement({type, conf.getAbstract()}); } llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF, @@ -3033,7 +3032,8 @@ NecessaryBindings NecessaryBindings::computeBindings( bool forPartialApplyForwarder, bool considerParameterSources) { NecessaryBindings bindings; - bindings.forPartialApply = forPartialApplyForwarder; + bindings.kind = + forPartialApplyForwarder ? Kind::PartialApply : Kind::AsyncFunction; // Bail out early if we don't have polymorphic parameters. if (!hasPolymorphicParameters(origType)) diff --git a/lib/IRGen/NecessaryBindings.h b/lib/IRGen/NecessaryBindings.h index eea62e66033a1..638be9eababc7 100644 --- a/lib/IRGen/NecessaryBindings.h +++ b/lib/IRGen/NecessaryBindings.h @@ -32,8 +32,9 @@ namespace swift { enum class MetadataState : size_t; class ProtocolDecl; class ProtocolConformanceRef; + class SpecializedProtocolConformance; -namespace irgen { + namespace irgen { class Address; class IRGenFunction; class IRGenModule; @@ -42,14 +43,32 @@ namespace irgen { /// NecessaryBindings - The set of metadata that must be saved in /// order to perform some set of operations on a type. class NecessaryBindings { - llvm::SetVector Requirements; + enum class Kind { + /// Are the bindings to be computed for a partial apply forwarder. + /// In the case this is true we need to store/restore the conformance of a + /// specialized type with conditional conformance because the conditional + /// requirements are not available in the partial apply forwarder. + PartialApply, + AsyncFunction, + }; + Kind kind; + llvm::SetVector RequirementsSet; + llvm::SmallVector RequirementsVector; llvm::DenseMap Conformances; - /// Are the bindings to be computed for a partial apply forwarder. - /// In the case this is true we need to store/restore the conformance of a - /// specialized type with conditional conformance because the conditional - /// requirements are not available in the partial apply forwarder. - bool forPartialApply = false; + void addRequirement(GenericRequirement requirement) { + switch (kind) { + case Kind::PartialApply: + RequirementsSet.insert(requirement); + break; + case Kind::AsyncFunction: + RequirementsVector.push_back(requirement); + break; + } + } + + void addAbstractConditionalRequirements( + SpecializedProtocolConformance *specializedConformance); public: NecessaryBindings() = default; @@ -70,7 +89,12 @@ class NecessaryBindings { /// Get the requirement from the bindings at index i. const GenericRequirement &operator[](size_t i) const { - return Requirements[i]; + switch (kind) { + case Kind::PartialApply: + return RequirementsSet[i]; + case Kind::AsyncFunction: + return RequirementsVector[i]; + } } ProtocolConformanceRef @@ -78,16 +102,14 @@ class NecessaryBindings { return Conformances.lookup(requirement); } - size_t size() const { - return Requirements.size(); - } + size_t size() const { return getRequirements().size(); } /// Add whatever information is necessary to reconstruct a witness table /// reference for the given type. void addProtocolConformance(CanType type, ProtocolConformanceRef conf); /// Is the work to do trivial? - bool empty() const { return Requirements.empty(); } + bool empty() const { return getRequirements().empty(); } /// Returns the required size of the bindings. /// Pointer alignment is sufficient. @@ -101,11 +123,17 @@ class NecessaryBindings { /// Restore the necessary bindings from the given buffer. void restore(IRGenFunction &IGF, Address buffer, MetadataState state) const; - const llvm::SetVector &getRequirements() const { - return Requirements; + const llvm::ArrayRef getRequirements() const { + switch (kind) { + case Kind::PartialApply: + return RequirementsSet.getArrayRef(); + case Kind::AsyncFunction: + return RequirementsVector; + } } - bool forAsyncFunction() { return !forPartialApply; } + bool forPartialApply() { return kind == Kind::PartialApply; } + bool forAsyncFunction() { return kind == Kind::AsyncFunction; } private: static NecessaryBindings computeBindings(IRGenModule &IGM, From 6829b6656de18cdf53de4a71dcdfc0d35e280472 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 22 Oct 2020 13:01:38 -0700 Subject: [PATCH 674/745] [NFC] Replaced include with forward declaration. --- lib/IRGen/NecessaryBindings.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/IRGen/NecessaryBindings.h b/lib/IRGen/NecessaryBindings.h index 638be9eababc7..0426d6d533ace 100644 --- a/lib/IRGen/NecessaryBindings.h +++ b/lib/IRGen/NecessaryBindings.h @@ -25,8 +25,6 @@ #include "llvm/ADT/SetVector.h" #include "swift/AST/Types.h" -#include "Explosion.h" - namespace swift { class CanType; enum class MetadataState : size_t; @@ -36,6 +34,7 @@ namespace swift { namespace irgen { class Address; + class Explosion; class IRGenFunction; class IRGenModule; class Size; From b8a5b62f720bd351a55cfa293de5a9a6d33a6e5a Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 22 Oct 2020 15:54:50 -0700 Subject: [PATCH 675/745] [NFC] Cached the layout in AsyncCallEmission. --- lib/IRGen/GenCall.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 3b79222fc3e26..a6b15cdce5e32 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -2061,11 +2061,15 @@ class AsyncCallEmission final : public CallEmission { Size contextSize; Address context; llvm::Value *thickContext = nullptr; + Optional asyncContextLayout; AsyncContextLayout getAsyncContextLayout() { - return ::getAsyncContextLayout(IGF, getCallee().getOrigFunctionType(), - getCallee().getSubstFunctionType(), - getCallee().getSubstitutions()); + if (!asyncContextLayout) { + asyncContextLayout.emplace(::getAsyncContextLayout( + IGF, getCallee().getOrigFunctionType(), + getCallee().getSubstFunctionType(), getCallee().getSubstitutions())); + } + return *asyncContextLayout; } void saveValue(ElementLayout layout, Explosion &explosion, bool isOutlined) { From 128c7bc03a55c682210844d42ffc5fcec48d2335 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 22 Oct 2020 16:47:51 -0700 Subject: [PATCH 676/745] [NFC] Whitespace. --- lib/IRGen/IRGenSIL.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index e35b43b3f1ddf..cbe1397185c09 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -2686,7 +2686,7 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { } } - Explosion llArgs; + Explosion llArgs; WitnessMetadata witnessMetadata; auto emission = getCallEmissionForLoweredValue( *this, origCalleeType, substCalleeType, calleeLV, selfValue, From e066588460d6986ed0c435dbf6265beb138b77db Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 22 Oct 2020 17:22:46 -0700 Subject: [PATCH 677/745] [IRGen] Temporarily treat @async @convention(c) as not async. --- lib/IRGen/IRGenSIL.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index cbe1397185c09..74913b824b4c2 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1331,7 +1331,11 @@ std::unique_ptr getCOrObjCEntryPointArgumentEmission(IRGenSILFunction &IGF, SILBasicBlock &entry, Explosion &allParamValues) { - if (IGF.CurSILFn->isAsync()) { + if (IGF.CurSILFn->isAsync() && + !(/*FIXME: Remove this condition once Task.runDetached is + available. rdar://problem/70597390*/ + IGF.CurSILFn->getLoweredFunctionType()->getRepresentation() == + SILFunctionTypeRepresentation::CFunctionPointer)) { llvm_unreachable("unsupported"); } else { return std::make_unique( @@ -3141,7 +3145,12 @@ static void emitReturnInst(IRGenSILFunction &IGF, auto &retTI = cast(IGF.getTypeInfo(resultTy)); retTI.initialize(IGF, result, IGF.IndirectReturn, false); IGF.Builder.CreateRetVoid(); - } else if (IGF.isAsync()) { + } else if (IGF.isAsync() && + !(/*FIXME: Remove this condition once Task.runDetached is + available. rdar://problem/70597390*/ + IGF.CurSILFn->getLoweredFunctionType() + ->getRepresentation() == + SILFunctionTypeRepresentation::CFunctionPointer)) { // If we're generating an async function, store the result into the buffer. assert(!IGF.IndirectReturn.isValid() && "Formally direct results should stay direct results for async " From 9135eafd36390677a4c428bafa9229064f6bced8 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 22 Oct 2020 17:43:53 -0700 Subject: [PATCH 678/745] [Test] Temporarily mark async execution tests' main @async. --- test/IRGen/async/run-call-classinstance-int64-to-void.sil | 2 +- test/IRGen/async/run-call-classinstance-void-to-void.sil | 2 +- test/IRGen/async/run-call-existential-to-void.sil | 2 +- test/IRGen/async/run-call-generic-to-generic.sil | 2 +- test/IRGen/async/run-call-generic-to-void.sil | 2 +- test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil | 2 +- test/IRGen/async/run-call-int64-and-int64-to-void.sil | 2 +- test/IRGen/async/run-call-int64-to-void.sil | 2 +- .../async/run-call-protocolextension_instance-void-to-int64.sil | 2 +- .../async/run-call-protocolwitness_instance-void-to-int64.sil | 2 +- test/IRGen/async/run-call-structinstance-int64-to-void.sil | 2 +- test/IRGen/async/run-call-void-throws-to-int-throwing.sil | 2 +- ...hrows-to-int-throwing_call-async-nothrow_call-sync-throw.sil | 2 +- .../run-call-void-throws-to-int-throwing_call-async-throw.sil | 2 +- ...hrows-to-int-throwing_call-sync-nothrow_call-async-throw.sil | 2 +- .../run-call-void-throws-to-int-throwing_call-sync-throw.sil | 2 +- test/IRGen/async/run-call-void-to-existential.sil | 2 +- test/IRGen/async/run-call-void-to-int64-and-int64.sil | 2 +- test/IRGen/async/run-call-void-to-int64.sil | 2 +- test/IRGen/async/run-call-void-to-struct_large.sil | 2 +- ...ic-protocolwitness_instance-generic-to-int64-and-generic.sil | 2 +- .../run-call_generic-protocolwitness_instance-void-to-int64.sil | 2 +- test/IRGen/async/run-partialapply-capture-class-to-void.sil | 2 +- ...rtialapply-capture-generic_conformer-and-generic-to-void.sil | 2 +- ...ialapply-capture-inout-generic-and-in-generic-to-generic.sil | 2 +- .../async/run-partialapply-capture-int64-int64-to-int64.sil | 2 +- test/IRGen/async/run-partialapply-capture-int64-to-generic.sil | 2 +- ...re-struct_classinstance_classinstance-and-int64-to-int64.sil | 2 +- ...-capture-structgeneric_classinstance_to_struct_and_error.sil | 2 +- ...ly-capture-structgeneric_polymorphic_constrained-to-void.sil | 2 +- ...pture-type_structgeneric_polymorphic_constrained-to-void.sil | 2 +- ...partialapply-capture-type_thin-and-classinstance-to-void.sil | 2 +- 32 files changed, 32 insertions(+), 32 deletions(-) diff --git a/test/IRGen/async/run-call-classinstance-int64-to-void.sil b/test/IRGen/async/run-call-classinstance-int64-to-void.sil index 1f1adbfd61d7c..9b2eceaa6b085 100644 --- a/test/IRGen/async/run-call-classinstance-int64-to-void.sil +++ b/test/IRGen/async/run-call-classinstance-int64-to-void.sil @@ -83,7 +83,7 @@ sil_vtable S { #S.deinit!deallocator: @S_deallocating_deinit } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %s_type = metatype $@thick S.Type diff --git a/test/IRGen/async/run-call-classinstance-void-to-void.sil b/test/IRGen/async/run-call-classinstance-void-to-void.sil index 40da285fed9d8..5de07692d126e 100644 --- a/test/IRGen/async/run-call-classinstance-void-to-void.sil +++ b/test/IRGen/async/run-call-classinstance-void-to-void.sil @@ -83,7 +83,7 @@ sil_vtable S { #S.deinit!deallocator: @S_deallocating_deinit } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %s_type = metatype $@thick S.Type diff --git a/test/IRGen/async/run-call-existential-to-void.sil b/test/IRGen/async/run-call-existential-to-void.sil index 29019ab19464f..93b55ac09339a 100644 --- a/test/IRGen/async/run-call-existential-to-void.sil +++ b/test/IRGen/async/run-call-existential-to-void.sil @@ -66,7 +66,7 @@ bb0: return %result : $() } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %call = function_ref @call : $@convention(thin) () -> () // CHECK: 7384783 %result = apply %call() : $@convention(thin) () -> () diff --git a/test/IRGen/async/run-call-generic-to-generic.sil b/test/IRGen/async/run-call-generic-to-generic.sil index d5da1c9f97e21..92ada1cd4c009 100644 --- a/test/IRGen/async/run-call-generic-to-generic.sil +++ b/test/IRGen/async/run-call-generic-to-generic.sil @@ -25,7 +25,7 @@ bb0(%out : $*T, %in : $*T): return %result : $() } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %int_literal = integer_literal $Builtin.Int64, 42 diff --git a/test/IRGen/async/run-call-generic-to-void.sil b/test/IRGen/async/run-call-generic-to-void.sil index 94f6d550c3edf..71c3a801c3105 100644 --- a/test/IRGen/async/run-call-generic-to-void.sil +++ b/test/IRGen/async/run-call-generic-to-void.sil @@ -25,7 +25,7 @@ bb0(%instance : $*T): return %result : $() } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %int_literal = integer_literal $Builtin.Int64, 922337203685477580 diff --git a/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil b/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil index 1bce84155f6de..d91cb7e495200 100644 --- a/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil +++ b/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil @@ -26,7 +26,7 @@ bb0(%0 : $*T, %1 : $*T): return %6 : $Bool } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %int1_literal = integer_literal $Builtin.Int64, 42 diff --git a/test/IRGen/async/run-call-int64-and-int64-to-void.sil b/test/IRGen/async/run-call-int64-and-int64-to-void.sil index 8921d5ac538a9..de42eb8d96e6c 100644 --- a/test/IRGen/async/run-call-int64-and-int64-to-void.sil +++ b/test/IRGen/async/run-call-int64-and-int64-to-void.sil @@ -26,7 +26,7 @@ entry(%int1: $Int64, %int2: $Int64): return %result2 : $() } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %int_literal1 = integer_literal $Builtin.Int64, 42 diff --git a/test/IRGen/async/run-call-int64-to-void.sil b/test/IRGen/async/run-call-int64-to-void.sil index 5425e64cc2a2d..47a721bd2d2eb 100644 --- a/test/IRGen/async/run-call-int64-to-void.sil +++ b/test/IRGen/async/run-call-int64-to-void.sil @@ -25,7 +25,7 @@ entry(%int: $Int64): return %result : $() } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %int_literal = integer_literal $Builtin.Int64, 42 diff --git a/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil b/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil index fae1f0549f1bf..e6f351f5e0821 100644 --- a/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil +++ b/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil @@ -58,7 +58,7 @@ bb0(%self_addr : $*I): return %result : $Int64 } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %i_type = metatype $@thin I.Type %i_int_literal = integer_literal $Builtin.Int64, 99 diff --git a/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil b/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil index b260aaf31ac30..b849f437367c0 100644 --- a/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil +++ b/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil @@ -48,7 +48,7 @@ bb0(%self_addr : $*I): return %result : $Int64 } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %i_type = metatype $@thin I.Type %i_int_literal = integer_literal $Builtin.Int64, 99 diff --git a/test/IRGen/async/run-call-structinstance-int64-to-void.sil b/test/IRGen/async/run-call-structinstance-int64-to-void.sil index 612cbdb9c4b44..2dc51da75418c 100644 --- a/test/IRGen/async/run-call-structinstance-int64-to-void.sil +++ b/test/IRGen/async/run-call-structinstance-int64-to-void.sil @@ -45,7 +45,7 @@ bb0(%self : $S, %int : $Int64): return %out : $() } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): %s_type = metatype $@thin S.Type %storage_literal = integer_literal $Builtin.Int64, 987654321 diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing.sil index a2340dcb89146..8937f131ff1f6 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing.sil @@ -112,7 +112,7 @@ bb0: throw %error : $Error } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %call = function_ref @call : $@convention(thin) () -> () // CHECK: 42 %result = apply %call() : $@convention(thin) () -> () diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil index b8cb7270ebc1e..5c4b06b7b928f 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil @@ -135,7 +135,7 @@ bb0: return %Int64 : $Int64 } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %call = function_ref @call : $@convention(thin) () -> () // CHECK: 42 %result = apply %call() : $@convention(thin) () -> () diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil index de2055a6f1756..e2db01768eb88 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil @@ -122,7 +122,7 @@ bb0: throw %error : $Error } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %call = function_ref @call : $@convention(thin) () -> () // CHECK: 42 %result = apply %call() : $@convention(thin) () -> () diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil index 4d45943257fa6..632ab842c90c2 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil @@ -136,7 +136,7 @@ bb0: throw %error : $Error } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %call = function_ref @call : $@convention(thin) () -> () // CHECK: 42 %result = apply %call() : $@convention(thin) () -> () diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil index 64a5a42098f99..c0f577f5f20c7 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil @@ -122,7 +122,7 @@ bb0: throw %error : $Error } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %call = function_ref @call : $@convention(thin) () -> () // CHECK: 42 %result = apply %call() : $@convention(thin) () -> () diff --git a/test/IRGen/async/run-call-void-to-existential.sil b/test/IRGen/async/run-call-void-to-existential.sil index 24cbcc6f91b8d..1974e197b9dde 100644 --- a/test/IRGen/async/run-call-void-to-existential.sil +++ b/test/IRGen/async/run-call-void-to-existential.sil @@ -70,7 +70,7 @@ bb0: return %result : $() } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %call = function_ref @call : $@convention(thin) () -> () %result = apply %call() : $@convention(thin) () -> () // CHECK: S(int: 42) diff --git a/test/IRGen/async/run-call-void-to-int64-and-int64.sil b/test/IRGen/async/run-call-void-to-int64-and-int64.sil index 27a5ae1767ec6..bee1e7821006f 100644 --- a/test/IRGen/async/run-call-void-to-int64-and-int64.sil +++ b/test/IRGen/async/run-call-void-to-int64-and-int64.sil @@ -27,7 +27,7 @@ sil @voidToInt64AndInt64 : $@async @convention(thin) () -> (Int64, Int64) { return %tuple : $(Int64, Int64) } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %voidToInt64AndInt64 = function_ref @voidToInt64AndInt64 : $@async @convention(thin) () -> (Int64, Int64) diff --git a/test/IRGen/async/run-call-void-to-int64.sil b/test/IRGen/async/run-call-void-to-int64.sil index 48efd5e337cc0..b3342d3bcffae 100644 --- a/test/IRGen/async/run-call-void-to-int64.sil +++ b/test/IRGen/async/run-call-void-to-int64.sil @@ -24,7 +24,7 @@ sil @voidToInt64 : $@async @convention(thin) () -> (Int64) { return %int : $Int64 } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %voidToInt64 = function_ref @voidToInt64 : $@async @convention(thin) () -> Int64 diff --git a/test/IRGen/async/run-call-void-to-struct_large.sil b/test/IRGen/async/run-call-void-to-struct_large.sil index 006355b21a1f5..5e662c3c7aed5 100644 --- a/test/IRGen/async/run-call-void-to-struct_large.sil +++ b/test/IRGen/async/run-call-void-to-struct_large.sil @@ -121,7 +121,7 @@ sil hidden @printBig : $@convention(thin) () -> () { return %out : $() } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %printBig = function_ref @printBig : $@convention(thin) () -> () %result = apply %printBig() : $@convention(thin) () -> () // CHECK: Big(i1: 1, i2: 2, i3: 3, i4: 4, i5: 5, i6: 6, i7: 7, i8: 8, i9: 9, i0: 0) diff --git a/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil b/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil index 796e25df19519..48695c565dc2f 100644 --- a/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil +++ b/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil @@ -59,7 +59,7 @@ bb0(%out_addr : $*U, %self_addr : $*T, %in_addr : $*U): return %result : $Int64 } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): %I_type = metatype $@thin I.Type %int_literal = integer_literal $Builtin.Int64, 99 diff --git a/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil b/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil index 99b78463e333e..2b312d014803d 100644 --- a/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil +++ b/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil @@ -55,7 +55,7 @@ bb0(%t : $*T): return %result : $Int64 } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): %i_type = metatype $@thin I.Type %i_int_literal = integer_literal $Builtin.Int64, 99 diff --git a/test/IRGen/async/run-partialapply-capture-class-to-void.sil b/test/IRGen/async/run-partialapply-capture-class-to-void.sil index fcffde323e0d0..efc565c631944 100644 --- a/test/IRGen/async/run-partialapply-capture-class-to-void.sil +++ b/test/IRGen/async/run-partialapply-capture-class-to-void.sil @@ -74,7 +74,7 @@ entry(%instance : $S): return %partiallyApplied : $@async @callee_owned () -> () } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): %s_type = metatype $@thick S.Type %allocating_init = function_ref @S_allocating_init : $@convention(method) (@thick S.Type) -> @owned S diff --git a/test/IRGen/async/run-partialapply-capture-generic_conformer-and-generic-to-void.sil b/test/IRGen/async/run-partialapply-capture-generic_conformer-and-generic-to-void.sil index b84751bf93e70..ba533178cf249 100644 --- a/test/IRGen/async/run-partialapply-capture-generic_conformer-and-generic-to-void.sil +++ b/test/IRGen/async/run-partialapply-capture-generic_conformer-and-generic-to-void.sil @@ -64,7 +64,7 @@ bb0(%0 : $*S): -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): %observableImpl = alloc_ref $ObservableImpl strong_retain %observableImpl : $ObservableImpl diff --git a/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil b/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil index 5e951d9802f3e..c19183ee1b8c6 100644 --- a/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil +++ b/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil @@ -37,7 +37,7 @@ entry(%a : $*T): return %p : $@async @callee_owned (@in T) -> @out T } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): %in_literal = integer_literal $Builtin.Int64, 876 %in = struct $Int64 (%in_literal : $Builtin.Int64) diff --git a/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil b/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil index adf8147ddbeb5..c3748c8f889a2 100644 --- a/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil +++ b/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil @@ -35,7 +35,7 @@ bb0: return %result : $() } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): %createAndInvokeClosure = function_ref @createAndInvokeClosure : $@convention(thin) () -> () %createAndInvokeClosure_result = apply %createAndInvokeClosure() : $@convention(thin) () -> () diff --git a/test/IRGen/async/run-partialapply-capture-int64-to-generic.sil b/test/IRGen/async/run-partialapply-capture-int64-to-generic.sil index b50eca7e86b33..efe3f29899233 100644 --- a/test/IRGen/async/run-partialapply-capture-int64-to-generic.sil +++ b/test/IRGen/async/run-partialapply-capture-int64-to-generic.sil @@ -36,7 +36,7 @@ entry(%out_t : $*T, %x : $Int32, %in_t : $*T): return %result : $() } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): %callee = function_ref @callee : $@async @convention(thin) (Int32, @in_guaranteed T) -> @out T %first_literal = integer_literal $Builtin.Int32, 1 diff --git a/test/IRGen/async/run-partialapply-capture-struct_classinstance_classinstance-and-int64-to-int64.sil b/test/IRGen/async/run-partialapply-capture-struct_classinstance_classinstance-and-int64-to-int64.sil index bebd7e2cd57db..7820bad1ef185 100644 --- a/test/IRGen/async/run-partialapply-capture-struct_classinstance_classinstance-and-int64-to-int64.sil +++ b/test/IRGen/async/run-partialapply-capture-struct_classinstance_classinstance-and-int64-to-int64.sil @@ -81,7 +81,7 @@ bb0(%x : $S): return %p : $@async @callee_owned (Int64) -> Int64 } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): %s_type = metatype $@thick C.Type %allocating_init = function_ref @S_allocating_init : $@convention(method) (@thick C.Type) -> @owned C diff --git a/test/IRGen/async/run-partialapply-capture-structgeneric_classinstance_to_struct_and_error.sil b/test/IRGen/async/run-partialapply-capture-structgeneric_classinstance_to_struct_and_error.sil index e9b8aecc7745d..5be0b3da78075 100644 --- a/test/IRGen/async/run-partialapply-capture-structgeneric_classinstance_to_struct_and_error.sil +++ b/test/IRGen/async/run-partialapply-capture-structgeneric_classinstance_to_struct_and_error.sil @@ -61,7 +61,7 @@ bb0(%0 : $*A2): return %5 : $@async @callee_guaranteed () -> (@owned A1, @error Error) } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): %a3 = alloc_ref $A3 %a2 = struct $A2 (%a3 : $A3) diff --git a/test/IRGen/async/run-partialapply-capture-structgeneric_polymorphic_constrained-to-void.sil b/test/IRGen/async/run-partialapply-capture-structgeneric_polymorphic_constrained-to-void.sil index 5b5a47e58d89c..0b25ee3380b50 100644 --- a/test/IRGen/async/run-partialapply-capture-structgeneric_polymorphic_constrained-to-void.sil +++ b/test/IRGen/async/run-partialapply-capture-structgeneric_polymorphic_constrained-to-void.sil @@ -50,7 +50,7 @@ bb0(%0 : $*τ_0_1): return %9 : $@async @callee_owned () -> () } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): %bind_polymorphic_param_from_context = function_ref @bind_polymorphic_param_from_context : $@convention(thin) <τ_0_1>(@in τ_0_1) -> @owned @async @callee_owned () -> () %int_literal = integer_literal $Builtin.Int64, 9999 diff --git a/test/IRGen/async/run-partialapply-capture-type_structgeneric_polymorphic_constrained-to-void.sil b/test/IRGen/async/run-partialapply-capture-type_structgeneric_polymorphic_constrained-to-void.sil index 92fe600aaead5..7b7924f4cacce 100644 --- a/test/IRGen/async/run-partialapply-capture-type_structgeneric_polymorphic_constrained-to-void.sil +++ b/test/IRGen/async/run-partialapply-capture-type_structgeneric_polymorphic_constrained-to-void.sil @@ -52,7 +52,7 @@ bb0(%0 : $*τ_0_1): return %9 : $@callee_owned @async (@owned WeakBox>) -> () } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): %bind_polymorphic_param_from_forwarder_parameter = function_ref @bind_polymorphic_param_from_forwarder_parameter : $@convention(thin) <τ_0_1>(@in τ_0_1) -> @callee_owned @async (@owned WeakBox>) -> () %int_literal = integer_literal $Builtin.Int64, 9999 diff --git a/test/IRGen/async/run-partialapply-capture-type_thin-and-classinstance-to-void.sil b/test/IRGen/async/run-partialapply-capture-type_thin-and-classinstance-to-void.sil index cc2bcb615c74d..3fc4b1fd5bac1 100644 --- a/test/IRGen/async/run-partialapply-capture-type_thin-and-classinstance-to-void.sil +++ b/test/IRGen/async/run-partialapply-capture-type_thin-and-classinstance-to-void.sil @@ -83,7 +83,7 @@ entry(%S_type: $@thin S.Type, %C_instance: $C): return %closure : $@async @callee_owned () -> () } -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): %c_type = metatype $@thick C.Type %allocating_init = function_ref @C_allocating_init : $@convention(method) (@thick C.Type) -> @owned C From c2469fcf90fa733fad1d7a3c4fcef6dc18ead113 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 15 Oct 2020 20:08:33 -0700 Subject: [PATCH 679/745] [Test] Made async exec tests obey async rule. Previously the execution tests for the async calling convention ignored the rule that callers of async functions must themselves be async. Failing to obey that rule prevented landing of the SIL verification change that enforces that rule. Here, the tests are modified to satisfy that rule so that the SIL verification change can be landed. --- test/IRGen/async/partial_apply.sil | 38 +++++++++---------- .../run-call-classinstance-int64-to-void.sil | 6 +-- .../run-call-classinstance-void-to-void.sil | 6 +-- .../async/run-call-existential-to-void.sil | 6 +-- .../run-call-structinstance-int64-to-void.sil | 6 +-- .../run-call-void-throws-to-int-throwing.sil | 6 +-- ...ing_call-async-nothrow_call-sync-throw.sil | 12 +++--- ...hrows-to-int-throwing_call-async-throw.sil | 6 +-- ...ing_call-sync-nothrow_call-async-throw.sil | 12 +++--- ...throws-to-int-throwing_call-sync-throw.sil | 12 +++--- .../async/run-call-void-to-existential.sil | 6 +-- .../async/run-call-void-to-struct_large.sil | 6 +-- ..._instance-generic-to-int64-and-generic.sil | 6 +-- ...protocolwitness_instance-void-to-int64.sil | 6 +-- ...nout-generic-and-in-generic-to-generic.sil | 6 +-- ...tialapply-capture-int64-int64-to-int64.sil | 12 +++--- ...tance_classinstance-and-int64-to-int64.sil | 6 +-- ...eneric_polymorphic_constrained-to-void.sil | 6 +-- ...eneric_polymorphic_constrained-to-void.sil | 6 +-- 19 files changed, 85 insertions(+), 85 deletions(-) diff --git a/test/IRGen/async/partial_apply.sil b/test/IRGen/async/partial_apply.sil index cf41d13636db2..75e77c632d414 100644 --- a/test/IRGen/async/partial_apply.sil +++ b/test/IRGen/async/partial_apply.sil @@ -372,25 +372,25 @@ bb0(%0 : $*A2): return %5 : $@async @callee_guaranteed () -> (@owned A1, @error Error) } -sil @capture_class : $@async @convention(thin) (@guaranteed A3) -> () - -// CHECK-LABEL: define{{.*}} swiftcc i8* @partial_apply_stack_in_coroutine(i8* {{.*}} %0, %T13partial_apply2A3C* %1) -sil @partial_apply_stack_in_coroutine : $@yield_once (@owned A3) -> () { -entry(%0: $A3): - %f = function_ref @capture_class : $@async @convention(thin) (@guaranteed A3) -> () - %p = partial_apply [callee_guaranteed] [on_stack] %f(%0) : $@async @convention(thin) (@guaranteed A3) -> () - apply %p() : $@noescape @async @callee_guaranteed () -> () - dealloc_stack %p : $@noescape @async @callee_guaranteed () -> () - %1000 = integer_literal $Builtin.Int32, 1000 - yield (), resume resume, unwind unwind - -resume: - %ret = tuple () - return %ret : $() - -unwind: - unwind -} +//sil @capture_class : $@async @convention(thin) (@guaranteed A3) -> () +// +//// CHECK LABEL: define{{.*}} swiftcc i8* @partial_apply_stack_in_coroutine(i8* {{.*}} %0, %T13partial_apply2A3C* %1) +//sil @partial_apply_stack_in_coroutine : $@async @yield_once (@owned A3) -> () { +//entry(%0: $A3): +// %f = function_ref @capture_class : $@async @convention(thin) (@guaranteed A3) -> () +// %p = partial_apply [callee_guaranteed] [on_stack] %f(%0) : $@async @convention(thin) (@guaranteed A3) -> () +// apply %p() : $@noescape @async @callee_guaranteed () -> () +// dealloc_stack %p : $@noescape @async @callee_guaranteed () -> () +// %1000 = integer_literal $Builtin.Int32, 1000 +// yield (), resume resume, unwind unwind +// +//resume: +// %ret = tuple () +// return %ret : $() +// +//unwind: +// unwind +//} sil_vtable A3 {} diff --git a/test/IRGen/async/run-call-classinstance-int64-to-void.sil b/test/IRGen/async/run-call-classinstance-int64-to-void.sil index 9b2eceaa6b085..49e39ad4420e5 100644 --- a/test/IRGen/async/run-call-classinstance-int64-to-void.sil +++ b/test/IRGen/async/run-call-classinstance-int64-to-void.sil @@ -30,12 +30,12 @@ class S { // CHECK-LL: define hidden swiftcc void @classinstanceSInt64ToVoid(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { sil hidden @classinstanceSInt64ToVoid : $@async @convention(method) (Int64, @guaranteed S) -> () { bb0(%int : $Int64, %instance : $S): - %take_s = function_ref @take_S : $@convention(thin) (@guaranteed S) -> () - %result = apply %take_s(%instance) : $@convention(thin) (@guaranteed S) -> () + %take_s = function_ref @take_S : $@async @convention(thin) (@guaranteed S) -> () + %result = apply %take_s(%instance) : $@async @convention(thin) (@guaranteed S) -> () return %result : $() } -sil hidden @take_S : $@convention(thin) (@guaranteed S) -> () { +sil hidden @take_S : $@async @convention(thin) (@guaranteed S) -> () { bb0(%instance : $S): %any = alloc_stack $Any strong_retain %instance : $S diff --git a/test/IRGen/async/run-call-classinstance-void-to-void.sil b/test/IRGen/async/run-call-classinstance-void-to-void.sil index 5de07692d126e..36477914fde32 100644 --- a/test/IRGen/async/run-call-classinstance-void-to-void.sil +++ b/test/IRGen/async/run-call-classinstance-void-to-void.sil @@ -30,12 +30,12 @@ class S { // CHECK-LL: define hidden swiftcc void @classinstanceSVoidToVoid(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { sil hidden @classinstanceSVoidToVoid : $@async @convention(method) (@guaranteed S) -> () { bb0(%instance : $S): - %take_s = function_ref @take_S : $@convention(thin) (@guaranteed S) -> () - %result = apply %take_s(%instance) : $@convention(thin) (@guaranteed S) -> () + %take_s = function_ref @take_S : $@async @convention(thin) (@guaranteed S) -> () + %result = apply %take_s(%instance) : $@async @convention(thin) (@guaranteed S) -> () return %result : $() } -sil hidden @take_S : $@convention(thin) (@guaranteed S) -> () { +sil hidden @take_S : $@async @convention(thin) (@guaranteed S) -> () { bb0(%instance : $S): %any = alloc_stack $Any strong_retain %instance : $S diff --git a/test/IRGen/async/run-call-existential-to-void.sil b/test/IRGen/async/run-call-existential-to-void.sil index 93b55ac09339a..b2bb878655182 100644 --- a/test/IRGen/async/run-call-existential-to-void.sil +++ b/test/IRGen/async/run-call-existential-to-void.sil @@ -49,7 +49,7 @@ bb0(%existential : $*P): return %result : $() } -sil hidden @call : $@convention(thin) () -> () { +sil hidden @call : $@async @convention(thin) () -> () { bb0: %S_type = metatype $@thin S.Type %int_literal = integer_literal $Builtin.Int64, 7384783 @@ -68,8 +68,8 @@ bb0: sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): - %call = function_ref @call : $@convention(thin) () -> () // CHECK: 7384783 - %result = apply %call() : $@convention(thin) () -> () + %call = function_ref @call : $@async @convention(thin) () -> () // CHECK: 7384783 + %result = apply %call() : $@async @convention(thin) () -> () %2 = integer_literal $Builtin.Int32, 0 %3 = struct $Int32 (%2 : $Builtin.Int32) diff --git a/test/IRGen/async/run-call-structinstance-int64-to-void.sil b/test/IRGen/async/run-call-structinstance-int64-to-void.sil index 2dc51da75418c..260f2a577b32f 100644 --- a/test/IRGen/async/run-call-structinstance-int64-to-void.sil +++ b/test/IRGen/async/run-call-structinstance-int64-to-void.sil @@ -26,13 +26,13 @@ struct S { // CHECK-LL: define hidden swiftcc void @structinstanceSInt64ToVoid(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { sil hidden @structinstanceSInt64ToVoid : $@async @convention(method) (Int64, S) -> () { bb0(%int : $Int64, %self : $S): - %takeSAndInt64 = function_ref @takeSAndInt64 : $@convention(thin) (S, Int64) -> () - %takeSAndInt64_result = apply %takeSAndInt64(%self, %int) : $@convention(thin) (S, Int64) -> () + %takeSAndInt64 = function_ref @takeSAndInt64 : $@async @convention(thin) (S, Int64) -> () + %takeSAndInt64_result = apply %takeSAndInt64(%self, %int) : $@async @convention(thin) (S, Int64) -> () %out = tuple () return %out : $() } -sil hidden @takeSAndInt64 : $@convention(thin) (S, Int64) -> () { +sil hidden @takeSAndInt64 : $@async @convention(thin) (S, Int64) -> () { bb0(%self : $S, %int : $Int64): %s_addr = alloc_stack $S store %self to %s_addr : $*S diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing.sil index 8937f131ff1f6..413ab5335de1f 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing.sil @@ -61,7 +61,7 @@ sil_witness_table hidden E: Error module main { } -sil hidden @call : $@convention(thin) () -> () { +sil hidden @call : $@async @convention(thin) () -> () { bb0: %voidThrowsToInt = function_ref @voidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) try_apply %voidThrowsToInt() : $@async @convention(thin) () -> (Int, @error Error), normal bb1, error bb3 @@ -114,8 +114,8 @@ bb0: sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): - %call = function_ref @call : $@convention(thin) () -> () // CHECK: 42 - %result = apply %call() : $@convention(thin) () -> () + %call = function_ref @call : $@async @convention(thin) () -> () // CHECK: 42 + %result = apply %call() : $@async @convention(thin) () -> () %2 = integer_literal $Builtin.Int32, 0 %3 = struct $Int32 (%2 : $Builtin.Int32) diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil index 5c4b06b7b928f..d4ae5a3f4f7eb 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil @@ -60,7 +60,7 @@ sil_witness_table hidden E: Error module main { } -sil hidden @call : $@convention(thin) () -> () { +sil hidden @call : $@async @convention(thin) () -> () { bb0: %voidThrowsToInt = function_ref @voidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) try_apply %voidThrowsToInt() : $@async @convention(thin) () -> (Int64, @error Error), normal bb1, error bb3 @@ -102,8 +102,8 @@ entry: %asyncVoidThrowsToInt = function_ref @asyncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) try_apply %asyncVoidThrowsToInt() : $@async @convention(thin) () -> (Int64, @error Error), normal success1, error error1 success1(%out1 : $Int64): - %syncVoidThrowsToInt = function_ref @syncVoidThrowsToInt : $@convention(thin) () -> (Int64, @error Error) - try_apply %syncVoidThrowsToInt() : $@convention(thin) () -> (Int64, @error Error), normal success2, error error2 + %syncVoidThrowsToInt = function_ref @syncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) + try_apply %syncVoidThrowsToInt() : $@async @convention(thin) () -> (Int64, @error Error), normal success2, error error2 success2(%out : $Int64): return %out : $Int64 error1(%error1 : $Error): @@ -114,7 +114,7 @@ error(%error : $Error): throw %error : $Error } -sil hidden @syncVoidThrowsToInt : $@convention(thin) () -> (Int64, @error Error) { +sil hidden @syncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) { bb0: %e_type = metatype $@thin E.Type %int_literal = integer_literal $Builtin.Int64, 42 @@ -137,8 +137,8 @@ bb0: sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): - %call = function_ref @call : $@convention(thin) () -> () // CHECK: 42 - %result = apply %call() : $@convention(thin) () -> () + %call = function_ref @call : $@async @convention(thin) () -> () // CHECK: 42 + %result = apply %call() : $@async @convention(thin) () -> () %2 = integer_literal $Builtin.Int32, 0 %3 = struct $Int32 (%2 : $Builtin.Int32) diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil index e2db01768eb88..7476d504e579d 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil @@ -61,7 +61,7 @@ sil_witness_table hidden E: Error module main { } -sil hidden @call : $@convention(thin) () -> () { +sil hidden @call : $@async @convention(thin) () -> () { bb0: %voidThrowsToInt = function_ref @voidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) try_apply %voidThrowsToInt() : $@async @convention(thin) () -> (Int, @error Error), normal bb1, error bb3 @@ -124,8 +124,8 @@ bb0: sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): - %call = function_ref @call : $@convention(thin) () -> () // CHECK: 42 - %result = apply %call() : $@convention(thin) () -> () + %call = function_ref @call : $@async @convention(thin) () -> () // CHECK: 42 + %result = apply %call() : $@async @convention(thin) () -> () %2 = integer_literal $Builtin.Int32, 0 %3 = struct $Int32 (%2 : $Builtin.Int32) diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil index 632ab842c90c2..886f2625ba8dd 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil @@ -61,7 +61,7 @@ sil_witness_table hidden E: Error module main { } -sil hidden @call : $@convention(thin) () -> () { +sil hidden @call : $@async @convention(thin) () -> () { bb0: %voidThrowsToInt = function_ref @voidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) try_apply %voidThrowsToInt() : $@async @convention(thin) () -> (Int64, @error Error), normal bb1, error bb3 @@ -100,8 +100,8 @@ bb5: // CHECK-LL: define hidden swiftcc void @voidThrowsToInt(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { sil hidden @voidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) { entry: - %syncVoidThrowsToInt = function_ref @syncVoidThrowsToInt : $@convention(thin) () -> (Int64, @error Error) - try_apply %syncVoidThrowsToInt() : $@convention(thin) () -> (Int64, @error Error), normal success1, error error1 + %syncVoidThrowsToInt = function_ref @syncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) + try_apply %syncVoidThrowsToInt() : $@async @convention(thin) () -> (Int64, @error Error), normal success1, error error1 success1(%out1 : $Int64): %asyncVoidThrowsToInt = function_ref @asyncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) try_apply %asyncVoidThrowsToInt() : $@async @convention(thin) () -> (Int64, @error Error), normal success2, error error2 @@ -115,7 +115,7 @@ error(%error : $Error): throw %error : $Error } -sil hidden @syncVoidThrowsToInt : $@convention(thin) () -> (Int64, @error Error) { +sil hidden @syncVoidThrowsToInt : $@async @convention(thin) () -> (Int64, @error Error) { bb0: %int_literal = integer_literal $Builtin.Int64, 1 // user: %2 %Int64 = struct $Int64 (%int_literal : $Builtin.Int64) // user: %3 @@ -138,8 +138,8 @@ bb0: sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): - %call = function_ref @call : $@convention(thin) () -> () // CHECK: 42 - %result = apply %call() : $@convention(thin) () -> () + %call = function_ref @call : $@async @convention(thin) () -> () // CHECK: 42 + %result = apply %call() : $@async @convention(thin) () -> () %2 = integer_literal $Builtin.Int32, 0 %3 = struct $Int32 (%2 : $Builtin.Int32) diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil index c0f577f5f20c7..4d84038538740 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil @@ -61,7 +61,7 @@ sil_witness_table hidden E: Error module main { } -sil hidden @call : $@convention(thin) () -> () { +sil hidden @call : $@async @convention(thin) () -> () { bb0: %voidThrowsToInt = function_ref @voidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) try_apply %voidThrowsToInt() : $@async @convention(thin) () -> (Int, @error Error), normal bb1, error bb3 @@ -100,15 +100,15 @@ bb5: // CHECK-LL: define hidden swiftcc void @voidThrowsToInt(%swift.context* {{%[0-9]*}}) {{#[0-9]*}} { sil hidden @voidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) { bb0: - %syncVoidThrowsToInt = function_ref @syncVoidThrowsToInt : $@convention(thin) () -> (Int, @error Error) - try_apply %syncVoidThrowsToInt() : $@convention(thin) () -> (Int, @error Error), normal bb1, error bb2 + %syncVoidThrowsToInt = function_ref @syncVoidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) + try_apply %syncVoidThrowsToInt() : $@async @convention(thin) () -> (Int, @error Error), normal bb1, error bb2 bb1(%out : $Int): return %out : $Int bb2(%error : $Error): throw %error : $Error } -sil hidden @syncVoidThrowsToInt : $@convention(thin) () -> (Int, @error Error) { +sil hidden @syncVoidThrowsToInt : $@async @convention(thin) () -> (Int, @error Error) { bb0: %e_type = metatype $@thin E.Type %int_literal = integer_literal $Builtin.Int64, 42 @@ -124,8 +124,8 @@ bb0: sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): - %call = function_ref @call : $@convention(thin) () -> () // CHECK: 42 - %result = apply %call() : $@convention(thin) () -> () + %call = function_ref @call : $@async @convention(thin) () -> () // CHECK: 42 + %result = apply %call() : $@async @convention(thin) () -> () %2 = integer_literal $Builtin.Int32, 0 %3 = struct $Int32 (%2 : $Builtin.Int32) diff --git a/test/IRGen/async/run-call-void-to-existential.sil b/test/IRGen/async/run-call-void-to-existential.sil index 1974e197b9dde..8cdf55705d0b7 100644 --- a/test/IRGen/async/run-call-void-to-existential.sil +++ b/test/IRGen/async/run-call-void-to-existential.sil @@ -52,7 +52,7 @@ bb0(%out : $*P): return %result : $() } -sil hidden @call : $@convention(thin) () -> () { +sil hidden @call : $@async @convention(thin) () -> () { bb0: %existential = alloc_stack $P %voidToExistential = function_ref @voidToExistential : $@async @convention(thin) () -> @out P @@ -72,8 +72,8 @@ bb0: sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): - %call = function_ref @call : $@convention(thin) () -> () - %result = apply %call() : $@convention(thin) () -> () // CHECK: S(int: 42) + %call = function_ref @call : $@async @convention(thin) () -> () + %result = apply %call() : $@async @convention(thin) () -> () // CHECK: S(int: 42) %2 = integer_literal $Builtin.Int32, 0 %3 = struct $Int32 (%2 : $Builtin.Int32) diff --git a/test/IRGen/async/run-call-void-to-struct_large.sil b/test/IRGen/async/run-call-void-to-struct_large.sil index 5e662c3c7aed5..32729127790df 100644 --- a/test/IRGen/async/run-call-void-to-struct_large.sil +++ b/test/IRGen/async/run-call-void-to-struct_large.sil @@ -109,7 +109,7 @@ bb0: return %2 : $Big } -sil hidden @printBig : $@convention(thin) () -> () { +sil hidden @printBig : $@async @convention(thin) () -> () { %getBig = function_ref @getBig : $@async @convention(thin) () -> Big %big = apply %getBig() : $@async @convention(thin) () -> Big %big_addr = alloc_stack $Big @@ -123,8 +123,8 @@ sil hidden @printBig : $@convention(thin) () -> () { sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): - %printBig = function_ref @printBig : $@convention(thin) () -> () - %result = apply %printBig() : $@convention(thin) () -> () // CHECK: Big(i1: 1, i2: 2, i3: 3, i4: 4, i5: 5, i6: 6, i7: 7, i8: 8, i9: 9, i0: 0) + %printBig = function_ref @printBig : $@async @convention(thin) () -> () + %result = apply %printBig() : $@async @convention(thin) () -> () // CHECK: Big(i1: 1, i2: 2, i3: 3, i4: 4, i5: 5, i6: 6, i7: 7, i8: 8, i9: 9, i0: 0) %2 = integer_literal $Builtin.Int32, 0 %3 = struct $Int32 (%2 : $Builtin.Int32) diff --git a/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil b/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil index 48695c565dc2f..2df2d5fbc1aca 100644 --- a/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil +++ b/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil @@ -52,7 +52,7 @@ bb0(%out_addr : $*τ_0_0, %in_addr : $*τ_0_0, %self_addr : $*I): return %result : $Int64 } -sil hidden @callPrintMe : $@convention(thin) (@in_guaranteed T, @in_guaranteed U) -> (Int64, @out U) { +sil hidden @callPrintMe : $@async @convention(thin) (@in_guaranteed T, @in_guaranteed U) -> (Int64, @out U) { bb0(%out_addr : $*U, %self_addr : $*T, %in_addr : $*U): %I_P_printMe = witness_method $T, #P.printMe : (Self) -> (T) async -> (Int64, T) : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P><τ_1_0> (@in_guaranteed τ_1_0, @in_guaranteed τ_0_0) -> (Int64, @out τ_1_0) %result = apply %I_P_printMe(%out_addr, %in_addr, %self_addr) : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P><τ_1_0> (@in_guaranteed τ_1_0, @in_guaranteed τ_0_0) -> (Int64, @out τ_1_0) @@ -70,8 +70,8 @@ bb0(%argc : $Int32, %argv : $UnsafeMutablePointer (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Int64, @out τ_0_1) - %result = apply %callPrintMe(%out_addr, %in_addr, %i_addr) : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : P> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Int64, @out τ_0_1) + %callPrintMe = function_ref @callPrintMe : $@async @convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : P> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Int64, @out τ_0_1) + %result = apply %callPrintMe(%out_addr, %in_addr, %i_addr) : $@async @convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : P> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Int64, @out τ_0_1) dealloc_stack %i_addr : $*I dealloc_stack %in_addr : $*I %out = load %out_addr : $*I diff --git a/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil b/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil index 2b312d014803d..42418b0ccdfec 100644 --- a/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil +++ b/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil @@ -48,7 +48,7 @@ bb0(%self_addr : $*I): return %result : $Int64 } -sil hidden @callPrintMe : $@convention(thin) (@in_guaranteed T) -> Int64 { +sil hidden @callPrintMe : $@async @convention(thin) (@in_guaranteed T) -> Int64 { bb0(%t : $*T): %I_P_printMe = witness_method $T, #P.printMe : (Self) -> () async -> Int64 : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 %result = apply %I_P_printMe(%t) : $@convention(witness_method: P) @async <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 @@ -63,8 +63,8 @@ bb0(%0 : $Int32, %1 : $UnsafeMutablePointer> %i = struct $I (%i_int : $Int64) %i_addr = alloc_stack $I store %i to %i_addr : $*I - %callPrintMe = function_ref @callPrintMe : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 - %result = apply %callPrintMe(%i_addr) : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 // users: %13, %11 // CHECK: I(int: 99) + %callPrintMe = function_ref @callPrintMe : $@async @convention(thin) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + %result = apply %callPrintMe(%i_addr) : $@async @convention(thin) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 // users: %13, %11 // CHECK: I(int: 99) dealloc_stack %i_addr : $*I %printInt64 = function_ref @printInt64 : $@convention(thin) (Int64) -> () %printInt64_result = apply %printInt64(%result) : $@convention(thin) (Int64) -> () // CHECK: 99 diff --git a/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil b/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil index c19183ee1b8c6..3b34ae72432a5 100644 --- a/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil +++ b/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil @@ -30,7 +30,7 @@ entry(%out : $*T, %in : $*T, %inout : $*T): return %result : $() } -sil @partial_apply_open_generic_capture : $@convention(thin) (@inout T) -> @async @callee_owned (@in T) -> @out T { +sil @partial_apply_open_generic_capture : $@async @convention(thin) (@inout T) -> @async @callee_owned (@in T) -> @out T { entry(%a : $*T): %f = function_ref @inGenericAndInoutGenericToGeneric : $@async @convention(thin) (@in U, @inout U) -> @out U %p = partial_apply %f(%a) : $@async @convention(thin) (@in U, @inout U) -> @out U @@ -50,8 +50,8 @@ bb0(%argc : $Int32, %argv : $UnsafeMutablePointer (@inout T) -> @async @callee_owned (@in T) -> @out T - %partiallyApplied = apply %partial_apply_open_generic_capture(%inout_addr) : $@convention(thin) (@inout T) -> @async @callee_owned (@in T) -> @out T + %partial_apply_open_generic_capture = function_ref @partial_apply_open_generic_capture : $@async @convention(thin) (@inout T) -> @async @callee_owned (@in T) -> @out T + %partiallyApplied = apply %partial_apply_open_generic_capture(%inout_addr) : $@async @convention(thin) (@inout T) -> @async @callee_owned (@in T) -> @out T %void = apply %partiallyApplied(%result_addr, %in_addr) : $@async @callee_owned (@in Int64) -> @out Int64 %result = load %result_addr : $*Int64 diff --git a/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil b/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil index c3748c8f889a2..708e99472394e 100644 --- a/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil +++ b/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil @@ -18,12 +18,12 @@ import PrintShims sil public_external @printGeneric : $@convention(thin) (@in_guaranteed T) -> () sil public_external @printInt64 : $@convention(thin) (Int64) -> () -sil hidden @createAndInvokeClosure : $@convention(thin) () -> () { +sil hidden @createAndInvokeClosure : $@async @convention(thin) () -> () { bb0: %captured_literal = integer_literal $Builtin.Int64, 783247897 %captured = struct $Int64 (%captured_literal : $Builtin.Int64) - %createPartialApply = function_ref @createPartialApply : $@convention(thin) (Int64) -> @owned @async @callee_guaranteed (Int64) -> Int64 - %partialApply = apply %createPartialApply(%captured) : $@convention(thin) (Int64) -> @owned @async @callee_guaranteed (Int64) -> Int64 + %createPartialApply = function_ref @createPartialApply : $@async @convention(thin) (Int64) -> @owned @async @callee_guaranteed (Int64) -> Int64 + %partialApply = apply %createPartialApply(%captured) : $@async @convention(thin) (Int64) -> @owned @async @callee_guaranteed (Int64) -> Int64 strong_retain %partialApply : $@async @callee_guaranteed (Int64) -> Int64 %applied_literal = integer_literal $Builtin.Int64, 7823478 %applied = struct $Int64 (%applied_literal : $Builtin.Int64) @@ -37,8 +37,8 @@ bb0: sil @main : $@async @convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%argc : $Int32, %argv : $UnsafeMutablePointer>>): - %createAndInvokeClosure = function_ref @createAndInvokeClosure : $@convention(thin) () -> () - %createAndInvokeClosure_result = apply %createAndInvokeClosure() : $@convention(thin) () -> () + %createAndInvokeClosure = function_ref @createAndInvokeClosure : $@async @convention(thin) () -> () + %createAndInvokeClosure_result = apply %createAndInvokeClosure() : $@async @convention(thin) () -> () %out_literal = integer_literal $Builtin.Int32, 0 %out = struct $Int32 (%out_literal : $Builtin.Int32) return %out : $Int32 @@ -46,7 +46,7 @@ bb0(%argc : $Int32, %argv : $UnsafeMutablePointer